diff options
Diffstat (limited to 'src/cowboy_http2.erl')
-rw-r--r-- | src/cowboy_http2.erl | 49 |
1 files changed, 42 insertions, 7 deletions
diff --git a/src/cowboy_http2.erl b/src/cowboy_http2.erl index c5c7e0c..94581ac 100644 --- a/src/cowboy_http2.erl +++ b/src/cowboy_http2.erl @@ -365,18 +365,50 @@ commands(State=#state{socket=Socket, transport=Transport, encode_state=EncodeSta [{response, StatusCode, Headers0, Body}|Tail]) -> Headers = Headers0#{<<":status">> => integer_to_binary(StatusCode)}, {HeaderBlock, EncodeState} = headers_encode(Headers, EncodeState0), - Transport:send(Socket, [ - cow_http2:headers(StreamID, nofin, HeaderBlock), - cow_http2:data(StreamID, fin, Body) - ]), - commands(State#state{encode_state=EncodeState}, StreamID, Tail); + Response = cow_http2:headers(StreamID, nofin, HeaderBlock), + case Body of + {sendfile, O, B, P} -> + Transport:send(Socket, Response), + commands(State#state{encode_state=EncodeState}, StreamID, + [{sendfile, fin, O, B, P}|Tail]); + _ -> + Transport:send(Socket, [ + Response, + cow_http2:data(StreamID, fin, Body) + ]), + commands(State#state{encode_state=EncodeState}, StreamID, Tail) + end; %% Send a response body chunk. %% %% @todo WINDOW_UPDATE stuff require us to buffer some data. +%% +%% When the body is sent using sendfile, the current solution is not +%% very good. The body could be too large, blocking the connection. +%% Also sendfile technically only works over TCP, so it's not that +%% useful for HTTP/2. At the very least the sendfile call should be +%% split into multiple calls and flow control should be used to make +%% sure we only send as fast as the client can receive and don't block +%% anything. commands(State=#state{socket=Socket, transport=Transport}, StreamID, [{data, IsFin, Data}|Tail]) -> Transport:send(Socket, cow_http2:data(StreamID, IsFin, Data)), commands(State, StreamID, Tail); +%% Send a file. +%% +%% @todo This implementation is terrible. A good implementation would +%% need to check that Bytes is exact (or we need to document that we +%% trust it to be exact), and would need to send the file asynchronously +%% in many data frames. Perhaps a sendfile call should result in a +%% process being created specifically for this purpose. Or perhaps +%% the protocol should be "dumb" and the stream handler be the one +%% to ensure the file is sent in chunks (which would require a better +%% flow control at the stream handler level). One thing for sure, the +%% implementation necessarily varies between HTTP/1.1 and HTTP/2. +commands(State=#state{socket=Socket, transport=Transport}, StreamID, + [{sendfile, IsFin, Offset, Bytes, Path}|Tail]) -> + Transport:send(Socket, cow_http2:data_header(StreamID, IsFin, Bytes)), + Transport:sendfile(Socket, Path, Offset, Bytes), + commands(State, StreamID, Tail); %% Send a push promise. %% %% @todo We need to keep track of what promises we made so that we don't @@ -400,6 +432,10 @@ commands(State, StreamID, [{flow, _Size}|Tail]) -> %% Supervise a child process. commands(State=#state{children=Children}, StreamID, [{spawn, Pid, _Shutdown}|Tail]) -> %% @todo Shutdown commands(State#state{children=[{Pid, StreamID}|Children]}, StreamID, Tail); +%% Error handling. +commands(State, StreamID, [Error = {internal_error, _, _}|Tail]) -> + %% @todo Only reset when the stream still exists. + commands(stream_reset(State, StreamID, Error), StreamID, Tail); %% Upgrade to a new protocol. %% %% @todo Implementation. @@ -447,8 +483,7 @@ stream_init(State0=#state{ref=Ref, socket=Socket, transport=Transport, decode_st Host = Authority, %% @todo Port = todo, %% @todo - Path = PathWithQs, %% @todo - Qs = todo, %% @todo + {Path, Qs} = cow_http:parse_fullpath(PathWithQs), Req = #{ ref => Ref, |