mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 20:30:23 +00:00
Close the connection when the application sends Connection: close
Now Cowboy checks headers sent to the client for the 'Connection' header value, parses it, and checks whether it contains a 'close' or 'keep-alive' value. It makes sure to close or keep the connection alive depending on the value found there, if any. Also change chunked replies to not close the connection by default unless the application requests it.
This commit is contained in:
parent
237b468f42
commit
8e835bce9f
2 changed files with 27 additions and 5 deletions
|
@ -318,6 +318,7 @@ body_qs(Req) ->
|
||||||
reply(Code, Headers, Body, Req=#http_req{socket=Socket,
|
reply(Code, Headers, Body, Req=#http_req{socket=Socket,
|
||||||
transport=Transport, connection=Connection,
|
transport=Transport, connection=Connection,
|
||||||
method=Method, resp_state=waiting}) ->
|
method=Method, resp_state=waiting}) ->
|
||||||
|
RespConn = response_connection(Headers, Connection),
|
||||||
Head = response_head(Code, Headers, [
|
Head = response_head(Code, Headers, [
|
||||||
{<<"Connection">>, atom_to_connection(Connection)},
|
{<<"Connection">>, atom_to_connection(Connection)},
|
||||||
{<<"Content-Length">>,
|
{<<"Content-Length">>,
|
||||||
|
@ -329,22 +330,23 @@ reply(Code, Headers, Body, Req=#http_req{socket=Socket,
|
||||||
'HEAD' -> Transport:send(Socket, Head);
|
'HEAD' -> Transport:send(Socket, Head);
|
||||||
_ -> Transport:send(Socket, [Head, Body])
|
_ -> Transport:send(Socket, [Head, Body])
|
||||||
end,
|
end,
|
||||||
{ok, Req#http_req{resp_state=done}}.
|
{ok, Req#http_req{connection=RespConn, resp_state=done}}.
|
||||||
|
|
||||||
%% @doc Initiate the sending of a chunked reply to the client.
|
%% @doc Initiate the sending of a chunked reply to the client.
|
||||||
%% @see cowboy_http_req:chunk/2
|
%% @see cowboy_http_req:chunk/2
|
||||||
-spec chunked_reply(http_status(), http_headers(), #http_req{})
|
-spec chunked_reply(http_status(), http_headers(), #http_req{})
|
||||||
-> {ok, #http_req{}}.
|
-> {ok, #http_req{}}.
|
||||||
chunked_reply(Code, Headers, Req=#http_req{socket=Socket, transport=Transport,
|
chunked_reply(Code, Headers, Req=#http_req{socket=Socket, transport=Transport,
|
||||||
resp_state=waiting}) ->
|
connection=Connection, resp_state=waiting}) ->
|
||||||
|
RespConn = response_connection(Headers, Connection),
|
||||||
Head = response_head(Code, Headers, [
|
Head = response_head(Code, Headers, [
|
||||||
{<<"Connection">>, <<"close">>},
|
{<<"Connection">>, atom_to_connection(Connection)},
|
||||||
{<<"Transfer-Encoding">>, <<"chunked">>},
|
{<<"Transfer-Encoding">>, <<"chunked">>},
|
||||||
{<<"Date">>, cowboy_clock:rfc1123()},
|
{<<"Date">>, cowboy_clock:rfc1123()},
|
||||||
{<<"Server">>, <<"Cowboy">>}
|
{<<"Server">>, <<"Cowboy">>}
|
||||||
]),
|
]),
|
||||||
Transport:send(Socket, Head),
|
Transport:send(Socket, Head),
|
||||||
{ok, Req#http_req{resp_state=chunks}}.
|
{ok, Req#http_req{connection=RespConn, resp_state=chunks}}.
|
||||||
|
|
||||||
%% @doc Send a chunk of data.
|
%% @doc Send a chunk of data.
|
||||||
%%
|
%%
|
||||||
|
@ -381,6 +383,26 @@ parse_qs(Qs) ->
|
||||||
[Name, Value] -> {quoted:from_url(Name), quoted:from_url(Value)}
|
[Name, Value] -> {quoted:from_url(Name), quoted:from_url(Value)}
|
||||||
end || Token <- Tokens].
|
end || Token <- Tokens].
|
||||||
|
|
||||||
|
-spec response_connection(http_headers(), keepalive | close)
|
||||||
|
-> keepalive | close.
|
||||||
|
response_connection([], Connection) ->
|
||||||
|
Connection;
|
||||||
|
response_connection([{Name, Value}|Tail], Connection) ->
|
||||||
|
case Name of
|
||||||
|
'Connection' -> response_connection_parse(Value);
|
||||||
|
Name ->
|
||||||
|
Name2 = cowboy_bstr:to_lower(Name),
|
||||||
|
case Name2 of
|
||||||
|
<<"connection">> -> response_connection_parse(Value);
|
||||||
|
_Any -> response_connection(Tail, Connection)
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec response_connection_parse(binary()) -> keepalive | close.
|
||||||
|
response_connection_parse(ReplyConn) ->
|
||||||
|
Tokens = cowboy_http:parse_tokens_list(ReplyConn),
|
||||||
|
cowboy_http:connection_to_atom(Tokens).
|
||||||
|
|
||||||
-spec response_head(http_status(), http_headers(), http_headers()) -> iolist().
|
-spec response_head(http_status(), http_headers(), http_headers()) -> iolist().
|
||||||
response_head(Code, Headers, DefaultHeaders) ->
|
response_head(Code, Headers, DefaultHeaders) ->
|
||||||
StatusLine = <<"HTTP/1.1 ", (status(Code))/binary, "\r\n">>,
|
StatusLine = <<"HTTP/1.1 ", (status(Code))/binary, "\r\n">>,
|
||||||
|
|
|
@ -117,7 +117,7 @@ headers_dupe(Config) ->
|
||||||
{ok, Data} = gen_tcp:recv(Socket, 0, 6000),
|
{ok, Data} = gen_tcp:recv(Socket, 0, 6000),
|
||||||
{_Start, _Length} = binary:match(Data, <<"Connection: close">>),
|
{_Start, _Length} = binary:match(Data, <<"Connection: close">>),
|
||||||
nomatch = binary:match(Data, <<"Connection: keep-alive">>),
|
nomatch = binary:match(Data, <<"Connection: keep-alive">>),
|
||||||
ok = gen_tcp:close(Socket).
|
{error, closed} = gen_tcp:recv(Socket, 0, 1000).
|
||||||
|
|
||||||
headers_huge(Config) ->
|
headers_huge(Config) ->
|
||||||
Cookie = lists:flatten(["whatever_man_biiiiiiiiiiiig_cookie_me_want_77="
|
Cookie = lists:flatten(["whatever_man_biiiiiiiiiiiig_cookie_me_want_77="
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue