mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 12:20:24 +00:00
Make the HTTP version type more practical
Now instead of {1, 1} we have 'HTTP/1.1', and instead of {1, 0} we have 'HTTP/1.0'. This is more efficient, easier to read in crash logs, and clearer in the code.
This commit is contained in:
parent
e0b5526f1e
commit
28186a68d0
5 changed files with 25 additions and 29 deletions
|
@ -31,7 +31,7 @@ request object.
|
||||||
The following access functions are defined in `cowboy_req`:
|
The following access functions are defined in `cowboy_req`:
|
||||||
|
|
||||||
* `method/1`: the request method (`<<"GET">>`, `<<"POST">>`...)
|
* `method/1`: the request method (`<<"GET">>`, `<<"POST">>`...)
|
||||||
* `version/1`: the HTTP version (`{1,0}` or `{1,1}`)
|
* `version/1`: the HTTP version (`'HTTP/1.0'` or `'HTTP/1.1'`)
|
||||||
* `peer/1`: the peer address and port number
|
* `peer/1`: the peer address and port number
|
||||||
* `host/1`: the hostname requested
|
* `host/1`: the hostname requested
|
||||||
* `host_info/1`: the result of the `[...]` match on the host
|
* `host_info/1`: the result of the `[...]` match on the host
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
timeout = 5000 :: timeout(), %% @todo Configurable.
|
timeout = 5000 :: timeout(), %% @todo Configurable.
|
||||||
buffer = <<>> :: binary(),
|
buffer = <<>> :: binary(),
|
||||||
connection = keepalive :: keepalive | close,
|
connection = keepalive :: keepalive | close,
|
||||||
version = {1, 1} :: cowboy_http:version(),
|
version = 'HTTP/1.1' :: cowboy_http:version(),
|
||||||
response_body = undefined :: undefined | non_neg_integer()
|
response_body = undefined :: undefined | non_neg_integer()
|
||||||
}).
|
}).
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ request(Method, URL, Headers, Body, Client=#client{
|
||||||
wait -> connect(Transport, Host, Port, Client);
|
wait -> connect(Transport, Host, Port, Client);
|
||||||
request -> {ok, Client}
|
request -> {ok, Client}
|
||||||
end,
|
end,
|
||||||
VersionBin = cowboy_http:version_to_binary(Version),
|
VersionBin = atom_to_binary(Version, latin1),
|
||||||
%% @todo do keepalive too, allow override...
|
%% @todo do keepalive too, allow override...
|
||||||
Headers2 = [
|
Headers2 = [
|
||||||
{<<"host">>, FullHost},
|
{<<"host">>, FullHost},
|
||||||
|
@ -173,7 +173,7 @@ stream_status(Client=#client{state=State, buffer=Buffer})
|
||||||
when State =:= request ->
|
when State =:= request ->
|
||||||
case binary:split(Buffer, <<"\r\n">>) of
|
case binary:split(Buffer, <<"\r\n">>) of
|
||||||
[Line, Rest] ->
|
[Line, Rest] ->
|
||||||
parse_status(Client#client{state=response, buffer=Rest}, Line);
|
parse_version(Client#client{state=response, buffer=Rest}, Line);
|
||||||
_ ->
|
_ ->
|
||||||
case recv(Client) of
|
case recv(Client) of
|
||||||
{ok, Data} ->
|
{ok, Data} ->
|
||||||
|
@ -184,11 +184,13 @@ stream_status(Client=#client{state=State, buffer=Buffer})
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
parse_status(Client, << "HTTP/", High, ".", Low, " ",
|
parse_version(Client, << "HTTP/1.1 ", Rest/binary >>) ->
|
||||||
S3, S2, S1, " ", StatusStr/binary >>)
|
parse_status(Client, Rest, 'HTTP/1.1');
|
||||||
when High >= $0, High =< $9, Low >= $0, Low =< $9,
|
parse_version(Client, << "HTTP/1.0 ", Rest/binary >>) ->
|
||||||
S3 >= $0, S3 =< $9, S2 >= $0, S2 =< $9, S1 >= $0, S1 =< $9 ->
|
parse_status(Client, Rest, 'HTTP/1.0').
|
||||||
Version = {High - $0, Low - $0},
|
|
||||||
|
parse_status(Client, << S3, S2, S1, " ", StatusStr/binary >>, Version)
|
||||||
|
when S3 >= $0, S3 =< $9, S2 >= $0, S2 =< $9, S1 >= $0, S1 =< $9 ->
|
||||||
Status = (S3 - $0) * 100 + (S2 - $0) * 10 + S1 - $0,
|
Status = (S3 - $0) * 100 + (S2 - $0) * 10 + S1 - $0,
|
||||||
{ok, Status, StatusStr, Client#client{version=Version}}.
|
{ok, Status, StatusStr, Client#client{version=Version}}.
|
||||||
|
|
||||||
|
|
|
@ -46,14 +46,13 @@
|
||||||
|
|
||||||
%% Interpretation.
|
%% Interpretation.
|
||||||
-export([cookie_to_iodata/3]).
|
-export([cookie_to_iodata/3]).
|
||||||
-export([version_to_binary/1]).
|
|
||||||
-export([urldecode/1]).
|
-export([urldecode/1]).
|
||||||
-export([urldecode/2]).
|
-export([urldecode/2]).
|
||||||
-export([urlencode/1]).
|
-export([urlencode/1]).
|
||||||
-export([urlencode/2]).
|
-export([urlencode/2]).
|
||||||
-export([x_www_form_urlencoded/1]).
|
-export([x_www_form_urlencoded/1]).
|
||||||
|
|
||||||
-type version() :: {Major::non_neg_integer(), Minor::non_neg_integer()}.
|
-type version() :: 'HTTP/1.1' | 'HTTP/1.0'.
|
||||||
-type headers() :: [{binary(), iodata()}].
|
-type headers() :: [{binary(), iodata()}].
|
||||||
-type status() :: non_neg_integer() | binary().
|
-type status() :: non_neg_integer() | binary().
|
||||||
|
|
||||||
|
@ -1001,11 +1000,6 @@ cookie_to_iodata(Name, Value, Opts) ->
|
||||||
[Name, <<"=">>, Value, <<"; Version=1">>,
|
[Name, <<"=">>, Value, <<"; Version=1">>,
|
||||||
MaxAgeBin, DomainBin, PathBin, SecureBin, HttpOnlyBin].
|
MaxAgeBin, DomainBin, PathBin, SecureBin, HttpOnlyBin].
|
||||||
|
|
||||||
%% @doc Convert an HTTP version tuple to its binary form.
|
|
||||||
-spec version_to_binary(version()) -> binary().
|
|
||||||
version_to_binary({1, 1}) -> <<"HTTP/1.1">>;
|
|
||||||
version_to_binary({1, 0}) -> <<"HTTP/1.0">>.
|
|
||||||
|
|
||||||
%% @doc Decode a URL encoded binary.
|
%% @doc Decode a URL encoded binary.
|
||||||
%% @equiv urldecode(Bin, crash)
|
%% @equiv urldecode(Bin, crash)
|
||||||
-spec urldecode(binary()) -> binary().
|
-spec urldecode(binary()) -> binary().
|
||||||
|
|
|
@ -242,9 +242,9 @@ skip_uri_fragment(<< C, Rest/bits >>, S, M, P, Q) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
parse_version(<< "HTTP/1.1\r\n", Rest/bits >>, S, M, P, Q) ->
|
parse_version(<< "HTTP/1.1\r\n", Rest/bits >>, S, M, P, Q) ->
|
||||||
parse_header(Rest, S, M, P, Q, {1, 1}, []);
|
parse_header(Rest, S, M, P, Q, 'HTTP/1.1', []);
|
||||||
parse_version(<< "HTTP/1.0\r\n", Rest/bits >>, S, M, P, Q) ->
|
parse_version(<< "HTTP/1.0\r\n", Rest/bits >>, S, M, P, Q) ->
|
||||||
parse_header(Rest, S, M, P, Q, {1, 0}, []);
|
parse_header(Rest, S, M, P, Q, 'HTTP/1.0', []);
|
||||||
parse_version(_, State, _, _, _) ->
|
parse_version(_, State, _, _, _) ->
|
||||||
error_terminate(505, State).
|
error_terminate(505, State).
|
||||||
|
|
||||||
|
@ -411,7 +411,7 @@ parse_hd_value(<<>>, S, M, P, Q, V, H, N, SoFar) ->
|
||||||
|
|
||||||
request(B, State=#state{transport=Transport}, M, P, Q, Version, Headers) ->
|
request(B, State=#state{transport=Transport}, M, P, Q, Version, Headers) ->
|
||||||
case lists:keyfind(<<"host">>, 1, Headers) of
|
case lists:keyfind(<<"host">>, 1, Headers) of
|
||||||
false when Version =:= {1, 1} ->
|
false when Version =:= 'HTTP/1.1' ->
|
||||||
error_terminate(400, State);
|
error_terminate(400, State);
|
||||||
false ->
|
false ->
|
||||||
request(B, State, M, P, Q, Version, Headers,
|
request(B, State, M, P, Q, Version, Headers,
|
||||||
|
@ -583,7 +583,7 @@ error_terminate(Code, State=#state{socket=Socket, transport=Transport,
|
||||||
{cowboy_req, resp_sent} -> ok
|
{cowboy_req, resp_sent} -> ok
|
||||||
after 0 ->
|
after 0 ->
|
||||||
_ = cowboy_req:reply(Code, cowboy_req:new(Socket, Transport,
|
_ = cowboy_req:reply(Code, cowboy_req:new(Socket, Transport,
|
||||||
undefined, <<"GET">>, <<>>, <<>>, {1, 1}, [], <<>>,
|
undefined, <<"GET">>, <<>>, <<>>, 'HTTP/1.1', [], <<>>,
|
||||||
undefined, <<>>, false, Compress, OnResponse)),
|
undefined, <<>>, false, Compress, OnResponse)),
|
||||||
ok
|
ok
|
||||||
end,
|
end,
|
||||||
|
|
|
@ -133,7 +133,7 @@
|
||||||
%% Request.
|
%% Request.
|
||||||
pid = undefined :: pid(),
|
pid = undefined :: pid(),
|
||||||
method = <<"GET">> :: binary(),
|
method = <<"GET">> :: binary(),
|
||||||
version = {1, 1} :: cowboy_http:version(),
|
version = 'HTTP/1.1' :: cowboy_http:version(),
|
||||||
peer = undefined :: undefined | {inet:ip_address(), inet:port_number()},
|
peer = undefined :: undefined | {inet:ip_address(), inet:port_number()},
|
||||||
host = undefined :: undefined | binary(),
|
host = undefined :: undefined | binary(),
|
||||||
host_info = undefined :: undefined | cowboy_router:tokens(),
|
host_info = undefined :: undefined | cowboy_router:tokens(),
|
||||||
|
@ -193,7 +193,7 @@ new(Socket, Transport, Peer, Method, Path, Query,
|
||||||
method=Method, path=Path, qs=Query, version=Version,
|
method=Method, path=Path, qs=Query, version=Version,
|
||||||
headers=Headers, host=Host, port=Port, buffer=Buffer,
|
headers=Headers, host=Host, port=Port, buffer=Buffer,
|
||||||
resp_compress=Compress, onresponse=OnResponse},
|
resp_compress=Compress, onresponse=OnResponse},
|
||||||
case CanKeepalive and (Version =:= {1, 1}) of
|
case CanKeepalive and (Version =:= 'HTTP/1.1') of
|
||||||
false ->
|
false ->
|
||||||
Req#http_req{connection=close};
|
Req#http_req{connection=close};
|
||||||
true ->
|
true ->
|
||||||
|
@ -605,7 +605,7 @@ stream_body(MaxLength, Req=#http_req{body_state=waiting, version=Version,
|
||||||
{ok, ExpectHeader, Req1} = parse_header(<<"expect">>, Req),
|
{ok, ExpectHeader, Req1} = parse_header(<<"expect">>, Req),
|
||||||
case ExpectHeader of
|
case ExpectHeader of
|
||||||
[<<"100-continue">>] ->
|
[<<"100-continue">>] ->
|
||||||
HTTPVer = cowboy_http:version_to_binary(Version),
|
HTTPVer = atom_to_binary(Version, latin1),
|
||||||
Transport:send(Socket,
|
Transport:send(Socket,
|
||||||
<< HTTPVer/binary, " ", (status(100))/binary, "\r\n\r\n" >>);
|
<< HTTPVer/binary, " ", (status(100))/binary, "\r\n\r\n" >>);
|
||||||
undefined ->
|
undefined ->
|
||||||
|
@ -935,7 +935,7 @@ reply(Status, Headers, Body, Req=#http_req{
|
||||||
method=Method, resp_compress=Compress,
|
method=Method, resp_compress=Compress,
|
||||||
resp_state=waiting, resp_headers=RespHeaders}) ->
|
resp_state=waiting, resp_headers=RespHeaders}) ->
|
||||||
HTTP11Headers = case Version of
|
HTTP11Headers = case Version of
|
||||||
{1, 1} -> [{<<"connection">>, atom_to_connection(Connection)}];
|
'HTTP/1.1' -> [{<<"connection">>, atom_to_connection(Connection)}];
|
||||||
_ -> []
|
_ -> []
|
||||||
end,
|
end,
|
||||||
Req3 = case Body of
|
Req3 = case Body of
|
||||||
|
@ -961,7 +961,7 @@ reply(Status, Headers, Body, Req=#http_req{
|
||||||
BodyFun(ChunkFun),
|
BodyFun(ChunkFun),
|
||||||
%% Terminate the chunked body for HTTP/1.1 only.
|
%% Terminate the chunked body for HTTP/1.1 only.
|
||||||
_ = case Version of
|
_ = case Version of
|
||||||
{1, 0} -> ok;
|
'HTTP/1.0' -> ok;
|
||||||
_ -> Transport:send(Socket, <<"0\r\n\r\n">>)
|
_ -> Transport:send(Socket, <<"0\r\n\r\n">>)
|
||||||
end;
|
end;
|
||||||
true -> ok
|
true -> ok
|
||||||
|
@ -1055,7 +1055,7 @@ chunked_reply(Status, Headers, Req) ->
|
||||||
-spec chunk(iodata(), req()) -> ok | {error, atom()}.
|
-spec chunk(iodata(), req()) -> ok | {error, atom()}.
|
||||||
chunk(_Data, #http_req{method= <<"HEAD">>}) ->
|
chunk(_Data, #http_req{method= <<"HEAD">>}) ->
|
||||||
ok;
|
ok;
|
||||||
chunk(Data, #http_req{socket=Socket, transport=Transport, version={1, 0}}) ->
|
chunk(Data, #http_req{socket=Socket, transport=Transport, version='HTTP/1.0'}) ->
|
||||||
Transport:send(Socket, Data);
|
Transport:send(Socket, Data);
|
||||||
chunk(Data, #http_req{socket=Socket, transport=Transport, resp_state=chunks}) ->
|
chunk(Data, #http_req{socket=Socket, transport=Transport, resp_state=chunks}) ->
|
||||||
Transport:send(Socket, [integer_to_list(iolist_size(Data), 16),
|
Transport:send(Socket, [integer_to_list(iolist_size(Data), 16),
|
||||||
|
@ -1086,7 +1086,7 @@ ensure_response(Req=#http_req{resp_state=waiting}, Status) ->
|
||||||
%% Terminate the chunked body for HTTP/1.1 only.
|
%% Terminate the chunked body for HTTP/1.1 only.
|
||||||
ensure_response(#http_req{method= <<"HEAD">>, resp_state=chunks}, _) ->
|
ensure_response(#http_req{method= <<"HEAD">>, resp_state=chunks}, _) ->
|
||||||
ok;
|
ok;
|
||||||
ensure_response(#http_req{version={1, 0}, resp_state=chunks}, _) ->
|
ensure_response(#http_req{version='HTTP/1.0', resp_state=chunks}, _) ->
|
||||||
ok;
|
ok;
|
||||||
ensure_response(#http_req{socket=Socket, transport=Transport,
|
ensure_response(#http_req{socket=Socket, transport=Transport,
|
||||||
resp_state=chunks}, _) ->
|
resp_state=chunks}, _) ->
|
||||||
|
@ -1207,7 +1207,7 @@ chunked_response(Status, Headers, Req=#http_req{
|
||||||
resp_state=waiting, resp_headers=RespHeaders}) ->
|
resp_state=waiting, resp_headers=RespHeaders}) ->
|
||||||
RespConn = response_connection(Headers, Connection),
|
RespConn = response_connection(Headers, Connection),
|
||||||
HTTP11Headers = case Version of
|
HTTP11Headers = case Version of
|
||||||
{1, 1} -> [
|
'HTTP/1.1' -> [
|
||||||
{<<"connection">>, atom_to_connection(Connection)},
|
{<<"connection">>, atom_to_connection(Connection)},
|
||||||
{<<"transfer-encoding">>, <<"chunked">>}];
|
{<<"transfer-encoding">>, <<"chunked">>}];
|
||||||
_ -> []
|
_ -> []
|
||||||
|
@ -1239,7 +1239,7 @@ response(Status, Headers, RespHeaders, DefaultHeaders, Body, Req=#http_req{
|
||||||
end,
|
end,
|
||||||
ReplyType = case Req2#http_req.resp_state of
|
ReplyType = case Req2#http_req.resp_state of
|
||||||
waiting ->
|
waiting ->
|
||||||
HTTPVer = cowboy_http:version_to_binary(Version),
|
HTTPVer = atom_to_binary(Version, latin1),
|
||||||
StatusLine = << HTTPVer/binary, " ",
|
StatusLine = << HTTPVer/binary, " ",
|
||||||
(status(Status))/binary, "\r\n" >>,
|
(status(Status))/binary, "\r\n" >>,
|
||||||
HeaderLines = [[Key, <<": ">>, Value, <<"\r\n">>]
|
HeaderLines = [[Key, <<": ">>, Value, <<"\r\n">>]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue