0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-14 12:20:24 +00:00

Use a single Transport:send/2 call to send the reply if possible

This gives a huge boost in performance when replies are small.
This commit is contained in:
Loïc Hoguin 2012-09-26 10:08:43 +02:00
parent 7481cf9963
commit bfab8d4b22

View file

@ -122,6 +122,8 @@
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
-endif. -endif.
-type resp_body_fun() :: fun(() -> {sent, non_neg_integer()}).
-record(http_req, { -record(http_req, {
%% Transport. %% Transport.
socket = undefined :: undefined | inet:socket(), socket = undefined :: undefined | inet:socket(),
@ -154,8 +156,7 @@
%% Response. %% Response.
resp_state = waiting :: locked | waiting | chunks | done, resp_state = waiting :: locked | waiting | chunks | done,
resp_headers = [] :: cowboy_http:headers(), resp_headers = [] :: cowboy_http:headers(),
resp_body = <<>> :: iodata() resp_body = <<>> :: iodata() | {non_neg_integer(), resp_body_fun()},
| {non_neg_integer(), fun(() -> {sent, non_neg_integer()})},
%% Functions. %% Functions.
onresponse = undefined :: undefined | cowboy_protocol:onresponse_fun(), onresponse = undefined :: undefined | cowboy_protocol:onresponse_fun(),
@ -825,8 +826,8 @@ set_resp_body(Body, Req) ->
%% `reply/3'. %% `reply/3'.
%% %%
%% @see cowboy_req:transport/1. %% @see cowboy_req:transport/1.
-spec set_resp_body_fun(non_neg_integer(), -spec set_resp_body_fun(non_neg_integer(), resp_body_fun(), Req)
fun(() -> {sent, non_neg_integer()}), Req) -> Req when Req::req(). -> Req when Req::req().
set_resp_body_fun(StreamLen, StreamFun, Req) -> set_resp_body_fun(StreamLen, StreamFun, Req) ->
Req#http_req{resp_body={StreamLen, StreamFun}}. Req#http_req{resp_body={StreamLen, StreamFun}}.
@ -861,29 +862,35 @@ reply(Status, Headers, Req=#http_req{resp_body=Body}) ->
reply(Status, Headers, Body, Req). reply(Status, Headers, Body, Req).
%% @doc Send a reply to the client. %% @doc Send a reply to the client.
-spec reply(cowboy_http:status(), cowboy_http:headers(), iodata(), Req) -spec reply(cowboy_http:status(), cowboy_http:headers(),
iodata() | {non_neg_integer() | resp_body_fun()}, Req)
-> {ok, Req} when Req::req(). -> {ok, Req} when Req::req().
reply(Status, Headers, Body, Req=#http_req{socket=Socket, transport=Transport, reply(Status, Headers, Body, Req=#http_req{
version=Version, connection=Connection, version=Version, connection=Connection,
method=Method, resp_state=waiting, resp_headers=RespHeaders}) -> method=Method, resp_state=waiting, resp_headers=RespHeaders}) ->
RespConn = response_connection(Headers, Connection), RespConn = response_connection(Headers, Connection),
ContentLen = case Body of {CL, _} -> CL; _ -> iolist_size(Body) end,
HTTP11Headers = case Version of HTTP11Headers = case Version of
{1, 1} -> [{<<"connection">>, atom_to_connection(Connection)}]; {1, 1} -> [{<<"connection">>, atom_to_connection(Connection)}];
_ -> [] _ -> []
end, end,
{ReplyType, Req2} = response(Status, Headers, RespHeaders, [ case Body of
{<<"content-length">>, integer_to_list(ContentLen)}, {ContentLength, BodyFun} ->
{RespType, Req2} = response(Status, Headers, RespHeaders, [
{<<"content-length">>, integer_to_list(ContentLength)},
{<<"date">>, cowboy_clock:rfc1123()}, {<<"date">>, cowboy_clock:rfc1123()},
{<<"server">>, <<"Cowboy">>} {<<"server">>, <<"Cowboy">>}
|HTTP11Headers], Req), |HTTP11Headers], <<>>, Req),
if Method =:= <<"HEAD">> -> ok; if RespType =/= hook, Method =/= <<"HEAD">> -> BodyFun();
ReplyType =:= hook -> ok; %% Hook replied for us, stop there. true -> ok
true -> end;
case Body of _ ->
{_, StreamFun} -> StreamFun(); {_, Req2} = response(Status, Headers, RespHeaders, [
_ -> Transport:send(Socket, Body) {<<"content-length">>, integer_to_list(iolist_size(Body))},
end {<<"date">>, cowboy_clock:rfc1123()},
{<<"server">>, <<"Cowboy">>}
|HTTP11Headers],
case Method of <<"HEAD">> -> <<>>; _ -> Body end,
Req)
end, end,
{ok, Req2#http_req{connection=RespConn, resp_state=done, {ok, Req2#http_req{connection=RespConn, resp_state=done,
resp_headers=[], resp_body= <<>>}}. resp_headers=[], resp_body= <<>>}}.
@ -910,7 +917,7 @@ chunked_reply(Status, Headers, Req=#http_req{
{_, Req2} = response(Status, Headers, RespHeaders, [ {_, Req2} = response(Status, Headers, RespHeaders, [
{<<"date">>, cowboy_clock:rfc1123()}, {<<"date">>, cowboy_clock:rfc1123()},
{<<"server">>, <<"Cowboy">>} {<<"server">>, <<"Cowboy">>}
|HTTP11Headers], Req), |HTTP11Headers], <<>>, Req),
{ok, Req2#http_req{connection=RespConn, resp_state=chunks, {ok, Req2#http_req{connection=RespConn, resp_state=chunks,
resp_headers=[], resp_body= <<>>}}. resp_headers=[], resp_body= <<>>}}.
@ -934,7 +941,7 @@ upgrade_reply(Status, Headers, Req=#http_req{
resp_state=waiting, resp_headers=RespHeaders}) -> resp_state=waiting, resp_headers=RespHeaders}) ->
{_, Req2} = response(Status, Headers, RespHeaders, [ {_, Req2} = response(Status, Headers, RespHeaders, [
{<<"connection">>, <<"Upgrade">>} {<<"connection">>, <<"Upgrade">>}
], Req), ], <<>>, Req),
{ok, Req2#http_req{resp_state=done, resp_headers=[], resp_body= <<>>}}. {ok, Req2#http_req{resp_state=done, resp_headers=[], resp_body= <<>>}}.
%% @doc Ensure the response has been sent fully. %% @doc Ensure the response has been sent fully.
@ -1049,9 +1056,9 @@ transport(#http_req{transport=Transport, socket=Socket}) ->
%% Internal. %% Internal.
-spec response(cowboy_http:status(), cowboy_http:headers(), -spec response(cowboy_http:status(), cowboy_http:headers(),
cowboy_http:headers(), cowboy_http:headers(), Req) cowboy_http:headers(), cowboy_http:headers(), iodata(), Req)
-> {normal | hook, Req} when Req::req(). -> {normal | hook, Req} when Req::req().
response(Status, Headers, RespHeaders, DefaultHeaders, Req=#http_req{ response(Status, Headers, RespHeaders, DefaultHeaders, Body, Req=#http_req{
socket=Socket, transport=Transport, version=Version, socket=Socket, transport=Transport, version=Version,
pid=ReqPid, onresponse=OnResponse}) -> pid=ReqPid, onresponse=OnResponse}) ->
FullHeaders = response_merge_headers(Headers, RespHeaders, DefaultHeaders), FullHeaders = response_merge_headers(Headers, RespHeaders, DefaultHeaders),
@ -1069,7 +1076,7 @@ response(Status, Headers, RespHeaders, DefaultHeaders, Req=#http_req{
(status(Status))/binary, "\r\n" >>, (status(Status))/binary, "\r\n" >>,
HeaderLines = [[Key, <<": ">>, Value, <<"\r\n">>] HeaderLines = [[Key, <<": ">>, Value, <<"\r\n">>]
|| {Key, Value} <- FullHeaders], || {Key, Value} <- FullHeaders],
Transport:send(Socket, [StatusLine, HeaderLines, <<"\r\n">>]), Transport:send(Socket, [StatusLine, HeaderLines, <<"\r\n">>, Body]),
ReqPid ! {?MODULE, resp_sent}, ReqPid ! {?MODULE, resp_sent},
normal; normal;
_ -> _ ->