   """
   @spec decode_headers(binary) :: map
   def decode_headers(headers, map \\ %{}) do
-    cond do
-      headers == "" -> map
-      String.starts_with?(headers, "?") ->
-        decode_headers_into_map(
-            String.slice(headers, 1, String.length(headers)-1), map)
-      :otherwise ->
-        raise ArgumentError,
-            "decode_headers/1 string has to start with ';', "
-            <> "got: #{inspect headers}"
-    end
-  end
-
-  defp decode_headers_into_map(headers, map) do
-    case decode_next_pair(headers, "&") do
-      nil -> map
-      {{key, value}, rest} ->
-        decode_headers_into_map(rest, Map.put(map, key, value))
-    end
+    unwrap("decode_headers/1", "?", "&", headers, map)
+  end
+
+  @doc """
+  Returns a stream of two-element tuples representing key-value pairs in the
+  given `headers`.
+  Key and value in each tuple will be binaries and will be percent-unescaped.
+  ## Examples
+      iex> Sippet.URI.headers_decoder("?foo=1&bar=2") |> Enum.to_list()
+      [{"foo", "1"}, {"bar", "2"}]
+  """
+  @spec headers_decoder(binary) :: Enumerable.t
+  def headers_decoder(headers) when is_binary(headers) do
+    middle = remove_first_char("headers_decoder/1", headers, "?")
+    Stream.unfold(middle, &decode_next_pair(&1, "&"))
   end
 
   @doc """

  end

  @doc """
  Returns the string representation of the given `URI` struct.
      iex> Sippet.URI.to_string(Sippet.URI.parse("sip:foo@bar.com"))
      "sip:foo@bar.com"
      iex> Sippet.URI.to_string(%URI{scheme: "foo", host: "bar.baz"})

  """
  @spec encode_parameters(term) :: binary
  def encode_parameters(enumerable) do
    wrap("encode_parameters/1", ";", ";", enumerable, &encode_paramchar/1)
  end

  defp wrap(function_name, first_character, separator, enumerable, encode) do
    first_character <> Enum.map_join(enumerable, separator,
      &encode_pair(function_name, encode, &1))
  end

  defp encode_pair(function_name, _encode, {key, _}) when is_list(key) do
    raise ArgumentError, function_name <> " keys cannot be lists, "
                         <> "got: #{inspect key}"
  end

  defp encode_pair(function_name, _encode, {_, value}) when is_list(value) do
    raise ArgumentError, function_name <> " values cannot be lists, "
                         <> "got: #{inspect value}"
  end

  defp encode_pair(_function_name, encode, {key, nil}) do
    encode.(Kernel.to_string(key))
  end

  defp encode_pair(_function_name, encode, {key, value}) do
    encode.(Kernel.to_string(key)) <>
      "=" <> encode.(Kernel.to_string(value))
  end

  @doc """
