zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

cookies.ex (4069B)


      1 defmodule Plug.Conn.Cookies do
      2   @moduledoc """
      3   Conveniences for encoding and decoding cookies.
      4   """
      5 
      6   @doc """
      7   Decodes the given cookies as given in either a request or response header.
      8 
      9   If a cookie is invalid, it is automatically discarded from the result.
     10 
     11   ## Examples
     12 
     13       iex> decode("key1=value1;key2=value2")
     14       %{"key1" => "value1", "key2" => "value2"}
     15 
     16   """
     17   def decode(cookie) do
     18     do_decode(:binary.split(cookie, ";", [:global]), %{})
     19   end
     20 
     21   defp do_decode([], acc), do: acc
     22 
     23   defp do_decode([h | t], acc) do
     24     case decode_kv(h) do
     25       {k, v} -> do_decode(t, Map.put(acc, k, v))
     26       false -> do_decode(t, acc)
     27     end
     28   end
     29 
     30   defp decode_kv(""), do: false
     31   defp decode_kv(<<h, t::binary>>) when h in [?\s, ?\t], do: decode_kv(t)
     32   defp decode_kv(kv), do: decode_key(kv, "")
     33 
     34   defp decode_key("", _key), do: false
     35   defp decode_key(<<?=, _::binary>>, ""), do: false
     36   defp decode_key(<<?=, t::binary>>, key), do: decode_value(t, "", key, "")
     37   defp decode_key(<<h, _::binary>>, _key) when h in [?\s, ?\t, ?\r, ?\n, ?\v, ?\f], do: false
     38   defp decode_key(<<h, t::binary>>, key), do: decode_key(t, <<key::binary, h>>)
     39 
     40   defp decode_value("", _spaces, key, value), do: {key, value}
     41 
     42   defp decode_value(<<?\s, t::binary>>, spaces, key, value),
     43     do: decode_value(t, <<spaces::binary, ?\s>>, key, value)
     44 
     45   defp decode_value(<<h, _::binary>>, _spaces, _key, _value) when h in [?\t, ?\r, ?\n, ?\v, ?\f],
     46     do: false
     47 
     48   defp decode_value(<<h, t::binary>>, spaces, key, value),
     49     do: decode_value(t, "", key, <<value::binary, spaces::binary, h>>)
     50 
     51   @doc """
     52   Encodes the given cookies as expected in a response header.
     53   """
     54   def encode(key, opts \\ %{}) when is_map(opts) do
     55     value = Map.get(opts, :value)
     56     path = Map.get(opts, :path, "/")
     57 
     58     IO.iodata_to_binary([
     59       "#{key}=#{value}; path=#{path}",
     60       emit_if(opts[:domain], &["; domain=", &1]),
     61       emit_if(opts[:max_age], &encode_max_age(&1, opts)),
     62       emit_if(Map.get(opts, :secure, false), "; secure"),
     63       emit_if(Map.get(opts, :http_only, true), "; HttpOnly"),
     64       emit_if(Map.get(opts, :same_site, nil), &encode_same_site/1),
     65       emit_if(opts[:extra], &["; ", &1])
     66     ])
     67   end
     68 
     69   defp encode_max_age(max_age, opts) do
     70     time = Map.get(opts, :universal_time) || :calendar.universal_time()
     71     time = add_seconds(time, max_age)
     72     ["; expires=", rfc2822(time), "; max-age=", Integer.to_string(max_age)]
     73   end
     74 
     75   defp encode_same_site(value) when is_binary(value), do: "; SameSite=#{value}"
     76 
     77   defp emit_if(value, fun_or_string) do
     78     cond do
     79       !value ->
     80         []
     81 
     82       is_function(fun_or_string) ->
     83         fun_or_string.(value)
     84 
     85       is_binary(fun_or_string) ->
     86         fun_or_string
     87     end
     88   end
     89 
     90   defp pad(number) when number in 0..9, do: <<?0, ?0 + number>>
     91   defp pad(number), do: Integer.to_string(number)
     92 
     93   defp rfc2822({{year, month, day} = date, {hour, minute, second}}) do
     94     # Sat, 17 Apr 2010 14:00:00 GMT
     95     [
     96       weekday_name(:calendar.day_of_the_week(date)),
     97       ?,,
     98       ?\s,
     99       pad(day),
    100       ?\s,
    101       month_name(month),
    102       ?\s,
    103       Integer.to_string(year),
    104       ?\s,
    105       pad(hour),
    106       ?:,
    107       pad(minute),
    108       ?:,
    109       pad(second),
    110       " GMT"
    111     ]
    112   end
    113 
    114   defp weekday_name(1), do: "Mon"
    115   defp weekday_name(2), do: "Tue"
    116   defp weekday_name(3), do: "Wed"
    117   defp weekday_name(4), do: "Thu"
    118   defp weekday_name(5), do: "Fri"
    119   defp weekday_name(6), do: "Sat"
    120   defp weekday_name(7), do: "Sun"
    121 
    122   defp month_name(1), do: "Jan"
    123   defp month_name(2), do: "Feb"
    124   defp month_name(3), do: "Mar"
    125   defp month_name(4), do: "Apr"
    126   defp month_name(5), do: "May"
    127   defp month_name(6), do: "Jun"
    128   defp month_name(7), do: "Jul"
    129   defp month_name(8), do: "Aug"
    130   defp month_name(9), do: "Sep"
    131   defp month_name(10), do: "Oct"
    132   defp month_name(11), do: "Nov"
    133   defp month_name(12), do: "Dec"
    134 
    135   defp add_seconds(time, seconds_to_add) do
    136     time_seconds = :calendar.datetime_to_gregorian_seconds(time)
    137     :calendar.gregorian_seconds_to_datetime(time_seconds + seconds_to_add)
    138   end
    139 end