aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGustaf Sjoberg <[email protected]>2017-03-22 09:18:43 +0100
committerLoïc Hoguin <[email protected]>2017-05-01 18:24:40 +0200
commit3f0b598edff1f411e31aca18ea96562b0d724257 (patch)
treefa1325e47e931805379aac116ed3065b328d1f58
parent0a181681223aead4043b2437fe493652db6e5f8a (diff)
downloadgun-3f0b598edff1f411e31aca18ea96562b0d724257.tar.gz
gun-3f0b598edff1f411e31aca18ea96562b0d724257.tar.bz2
gun-3f0b598edff1f411e31aca18ea96562b0d724257.zip
Add transform_header_name http option.
-rw-r--r--doc/src/manual/gun.asciidoc6
-rw-r--r--src/gun_http.erl17
-rw-r--r--src/gun_http2.erl2
-rw-r--r--test/gun_SUITE.erl13
4 files changed, 33 insertions, 5 deletions
diff --git a/doc/src/manual/gun.asciidoc b/doc/src/manual/gun.asciidoc
index 7b9ddd0..7c794ee 100644
--- a/doc/src/manual/gun.asciidoc
+++ b/doc/src/manual/gun.asciidoc
@@ -61,6 +61,12 @@ keepalive => pos_integer()::
empty line when the connection is idle. Gun only makes a best
effort here as servers usually have configurable limits to drop
idle connections. Defaults to 5000.
+transform_header_name => fun((LowercaseName :: binary()) -> TransformedName :: binary()) | undefined::
+ A function that will be applied to all header names before they
+ are sent to the server. Gun assumes that all header names are in
+ lower case. This function is useful if you, for example, need to
+ re-case header names in the event that the server incorrectly
+ considers header name case to be significant.
version => 'HTTP/1.1' | 'HTTP/1.0'::
HTTP version to use. Defaults to 'HTTP/1.1'.
diff --git a/src/gun_http.erl b/src/gun_http.erl
index 2388182..3766ca5 100644
--- a/src/gun_http.erl
+++ b/src/gun_http.erl
@@ -51,7 +51,8 @@
streams = [] :: [#stream{}],
in = head :: io(),
in_state = {0, 0} :: {non_neg_integer(), non_neg_integer()},
- out = head :: io()
+ out = head :: io(),
+ transform_header_name :: fun((binary()) -> binary())
}).
check_options(Opts) ->
@@ -68,6 +69,8 @@ do_check_options([Opt={content_handlers, Handlers}|Opts]) ->
ok -> do_check_options(Opts);
error -> {error, {options, {http, Opt}}}
end;
+do_check_options([{transform_header_name, F}|Opts]) when is_function(F) ->
+ do_check_options(Opts);
do_check_options([Opt|_]) ->
{error, {options, {http, Opt}}}.
@@ -76,8 +79,9 @@ name() -> http.
init(Owner, Socket, Transport, Opts) ->
Version = maps:get(version, Opts, 'HTTP/1.1'),
Handlers = maps:get(content_handlers, Opts, [gun_data]),
+ TransformHeaderName = maps:get(transform_header_name, Opts, fun (N) -> N end),
#http_state{owner=Owner, socket=Socket, transport=Transport, version=Version,
- content_handlers=Handlers}.
+ content_handlers=Handlers, transform_header_name=TransformHeaderName}.
%% Stop looping when we got no more data.
handle(<<>>, State) ->
@@ -255,7 +259,8 @@ request(State=#http_state{socket=Socket, transport=Transport, version=Version,
body_chunked -> [{<<"transfer-encoding">>, <<"chunked">>}|Headers3];
_ -> Headers3
end,
- Transport:send(Socket, cow_http:request(Method, Path, Version, Headers4)),
+ Headers5 = transform_header_names(State, Headers4),
+ Transport:send(Socket, cow_http:request(Method, Path, Version, Headers5)),
new_stream(State#http_state{connection=Conn, out=Out}, StreamRef, ReplyTo, Method).
request(State=#http_state{socket=Socket, transport=Transport, version=Version,
@@ -266,15 +271,19 @@ request(State=#http_state{socket=Socket, transport=Transport, version=Version,
false -> [{<<"host">>, [Host, $:, integer_to_binary(Port)]}|Headers2];
true -> Headers2
end,
+ Headers4 = transform_header_names(State, Headers3),
%% We use Headers2 because this is the smallest list.
Conn = conn_from_headers(Version, Headers2),
Transport:send(Socket, [
cow_http:request(Method, Path, Version, [
{<<"content-length">>, integer_to_binary(iolist_size(Body))}
- |Headers3]),
+ |Headers4]),
Body]),
new_stream(State#http_state{connection=Conn}, StreamRef, ReplyTo, Method).
+transform_header_names(#http_state{transform_header_name = Fun}, Headers) ->
+ lists:keymap(Fun, 1, Headers).
+
%% We are expecting a new stream.
data(State=#http_state{out=head}, StreamRef, ReplyTo, _, _) ->
error_stream_closed(State, StreamRef, ReplyTo);
diff --git a/src/gun_http2.erl b/src/gun_http2.erl
index 6073119..2195037 100644
--- a/src/gun_http2.erl
+++ b/src/gun_http2.erl
@@ -173,7 +173,7 @@ frame(settings_ack, State) -> %% @todo =#http2_state{next_settings=_NextSettings
%% PUSH_PROMISE frame.
%% @todo Continuation.
frame({push_promise, StreamID, head_fin, PromisedStreamID, HeaderBlock},
- State=#http2_state{owner=Owner, decode_state=DecodeState0}) ->
+ State=#http2_state{decode_state=DecodeState0}) ->
case get_stream_by_id(PromisedStreamID, State) of
false ->
case get_stream_by_id(StreamID, State) of
diff --git a/test/gun_SUITE.erl b/test/gun_SUITE.erl
index 171c874..505f365 100644
--- a/test/gun_SUITE.erl
+++ b/test/gun_SUITE.erl
@@ -139,3 +139,16 @@ retry_timeout(_) ->
after 400 ->
error(gone_too_late)
end.
+
+transform_header_name(_) ->
+ doc("The reply_to option allows using a separate process for requests."),
+ {ok, Pid} = gun:open("google.com", 443, #{
+ protocols => [http],
+ http_opts => #{
+ transform_header_name => fun(<<"host">>) -> <<"HOST">>; (N) -> N end
+ }
+ }),
+ {ok, http} = gun:await_up(Pid),
+ Ref = gun:get(Pid, "/", [{<<"host">>, <<"google.com">>}]),
+ {response, _, _, _} = gun:await(Pid, Ref),
+ ok.