mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 12:20:24 +00:00
Compare commits
2 commits
af1692b4c6
...
b63e18d2cc
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b63e18d2cc | ||
![]() |
70629673c5 |
5 changed files with 109 additions and 3 deletions
|
@ -1047,6 +1047,10 @@ terminate_stream(State=#state{streams=Streams0, children=Children0},
|
||||||
Children = cowboy_children:shutdown(Children0, StreamID),
|
Children = cowboy_children:shutdown(Children0, StreamID),
|
||||||
stream_linger(State#state{streams=Streams, children=Children}, StreamID).
|
stream_linger(State#state{streams=Streams, children=Children}, StreamID).
|
||||||
|
|
||||||
|
%% We must dereference the stream state when WebTransport is in use.
|
||||||
|
terminate_stream_handler(State, StreamID, Reason,
|
||||||
|
{cowboy_webtransport, #{stream_state := StreamState}}) ->
|
||||||
|
terminate_stream_handler(State, StreamID, Reason, StreamState);
|
||||||
terminate_stream_handler(#state{opts=Opts}, StreamID, Reason, StreamState) ->
|
terminate_stream_handler(#state{opts=Opts}, StreamID, Reason, StreamState) ->
|
||||||
try
|
try
|
||||||
cowboy_stream:terminate(StreamID, Reason, StreamState)
|
cowboy_stream:terminate(StreamID, Reason, StreamState)
|
||||||
|
|
|
@ -1622,5 +1622,6 @@ error_terminate(Req, #state{handler=Handler, handler_state=HandlerState}, Class,
|
||||||
erlang:raise(Class, Reason, Stacktrace).
|
erlang:raise(Class, Reason, Stacktrace).
|
||||||
|
|
||||||
terminate(Req, #state{handler=Handler, handler_state=HandlerState}) ->
|
terminate(Req, #state{handler=Handler, handler_state=HandlerState}) ->
|
||||||
|
%% @todo I don't think the result is used anywhere?
|
||||||
Result = cowboy_handler:terminate(normal, Req, HandlerState, Handler),
|
Result = cowboy_handler:terminate(normal, Req, HandlerState, Handler),
|
||||||
{ok, Req, Result}.
|
{ok, Req, Result}.
|
||||||
|
|
|
@ -105,6 +105,7 @@ upgrade(Req=#{version := 'HTTP/3', pid := Pid, streamid := StreamID}, Env, Handl
|
||||||
%% Use 501 Not Implemented to mirror the recommendation in
|
%% Use 501 Not Implemented to mirror the recommendation in
|
||||||
%% by RFC9220 3 (WebSockets Upgrade over HTTP/3).
|
%% by RFC9220 3 (WebSockets Upgrade over HTTP/3).
|
||||||
false ->
|
false ->
|
||||||
|
%% @todo I don't think terminate will be called.
|
||||||
{ok, cowboy_req:reply(501, Req), Env}
|
{ok, cowboy_req:reply(501, Req), Env}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -212,7 +213,7 @@ commands([Command={close, _, _}|Tail], State, _, Acc) ->
|
||||||
%% @todo set_options (to increase number of streams? data amounts? or a flow command?)
|
%% @todo set_options (to increase number of streams? data amounts? or a flow command?)
|
||||||
%% @todo shutdown_reason if useful.
|
%% @todo shutdown_reason if useful.
|
||||||
|
|
||||||
terminate(State, HandlerState, Reason) ->
|
terminate(State=#state{req=Req}, HandlerState, Reason) ->
|
||||||
%cowboy_stream:terminate(StreamID, Reason, StreamState)
|
%cowboy_stream:terminate(StreamID, Reason, StreamState)
|
||||||
%% @todo This terminate is at the connection level.
|
%% @todo This terminate is at the connection level.
|
||||||
% handler_terminate(State, HandlerState, Reason),
|
% handler_terminate(State, HandlerState, Reason),
|
||||||
|
@ -223,7 +224,8 @@ terminate(State, HandlerState, Reason) ->
|
||||||
% exit(normal).
|
% exit(normal).
|
||||||
%handler_terminate(#state{handler=Handler, req=Req}, HandlerState, Reason) ->
|
%handler_terminate(#state{handler=Handler, req=Req}, HandlerState, Reason) ->
|
||||||
% cowboy_handler:terminate(Reason, Req, HandlerState, Handler).
|
% cowboy_handler:terminate(Reason, Req, HandlerState, Handler).
|
||||||
ok.
|
%% @todo I think we must call terminate ourselves.
|
||||||
|
{ok, Req, Reason}.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -246,6 +246,32 @@ datagrams(Config) ->
|
||||||
|
|
||||||
%% An HTTP/3 GOAWAY frame is also a signal to applications to initiate shutdown for all WebTransport sessions. (4.6)
|
%% An HTTP/3 GOAWAY frame is also a signal to applications to initiate shutdown for all WebTransport sessions. (4.6)
|
||||||
|
|
||||||
|
%% @todo Currently receipt of a GOAWAY frame immediately ends the connection.
|
||||||
|
%% We want to allow WT sessions to gracefully shut down before that.
|
||||||
|
%goaway_client(Config) ->
|
||||||
|
% doc("The HTTP/3 client can initiate the close of all WT sessions "
|
||||||
|
% "by sending a GOAWAY frame. (draft_webtrans_http3 4.6)"),
|
||||||
|
% %% Connect to the WebTransport server.
|
||||||
|
% #{
|
||||||
|
% conn := Conn,
|
||||||
|
% connect_stream_ref := ConnectStreamRef,
|
||||||
|
% session_id := SessionID
|
||||||
|
% } = do_webtransport_connect(Config),
|
||||||
|
% %% Open a control stream and send a GOAWAY frame.
|
||||||
|
% {ok, ControlRef} = quicer:start_stream(Conn,
|
||||||
|
% #{open_flag => ?QUIC_STREAM_OPEN_FLAG_UNIDIRECTIONAL}),
|
||||||
|
% {ok, SettingsBin, _HTTP3Machine0} = cow_http3_machine:init(client, #{}),
|
||||||
|
% {ok, _} = quicer:send(ControlRef, [
|
||||||
|
% <<0>>, %% CONTROL stream.
|
||||||
|
% SettingsBin,
|
||||||
|
% <<7>>, %% GOAWAY frame.
|
||||||
|
% cow_http3:encode_int(1),
|
||||||
|
% cow_http3:encode_int(0)
|
||||||
|
% ]),
|
||||||
|
% %% Receive a datagram indicating processing by the WT handler.
|
||||||
|
% {datagram, SessionID, <<"TEST:close_initiated">>} = do_receive_datagram(Conn),
|
||||||
|
% ok.
|
||||||
|
|
||||||
drain_wt_session_client(Config) ->
|
drain_wt_session_client(Config) ->
|
||||||
doc("The WT client can initiate the close of a single session. "
|
doc("The WT client can initiate the close of a single session. "
|
||||||
"(draft_webtrans_http3 4.6)"),
|
"(draft_webtrans_http3 4.6)"),
|
||||||
|
@ -403,18 +429,85 @@ drain_wt_session_continue_server(Config) ->
|
||||||
%% * a CLOSE_WEBTRANSPORT_SESSION capsule is either sent or received.
|
%% * a CLOSE_WEBTRANSPORT_SESSION capsule is either sent or received.
|
||||||
%% (6)
|
%% (6)
|
||||||
|
|
||||||
|
%% @todo connect_stream_closed_cleanly_client
|
||||||
|
%% @todo connect_stream_closed_abruptly_client
|
||||||
|
%% @todo close_wt_session_client
|
||||||
|
|
||||||
|
close_wt_session_server(Config) ->
|
||||||
|
doc("The WT server can close a single session. (draft_webtrans_http3 4.6)"),
|
||||||
|
%% Connect to the WebTransport server.
|
||||||
|
#{
|
||||||
|
conn := Conn,
|
||||||
|
connect_stream_ref := ConnectStreamRef,
|
||||||
|
session_id := SessionID
|
||||||
|
} = do_webtransport_connect(Config),
|
||||||
|
%% Create a bidi stream, send a special instruction to make it initiate the close.
|
||||||
|
{ok, LocalStreamRef} = quicer:start_stream(Conn, #{}),
|
||||||
|
{ok, _} = quicer:send(LocalStreamRef, <<1:2, 16#41:14, 0:2, SessionID:6, "TEST:close">>),
|
||||||
|
%% Receive the CLOSE_WEBTRANSPORT_SESSION capsule on the CONNECT stream.
|
||||||
|
CloseWTSessionCapsule = cow_capsule:close_wt_session(0, <<>>),
|
||||||
|
{fin, CloseWTSessionCapsule} = do_receive_data(ConnectStreamRef),
|
||||||
|
ok.
|
||||||
|
|
||||||
%% Upon learning that the session has been terminated, the endpoint MUST reset the send side and abort reading on the receive side of all of the streams associated with the session (see Section 2.4 of [RFC9000]) using the WEBTRANSPORT_SESSION_GONE error code; it MUST NOT send any new datagrams or open any new streams. (6)
|
%% Upon learning that the session has been terminated, the endpoint MUST reset the send side and abort reading on the receive side of all of the streams associated with the session (see Section 2.4 of [RFC9000]) using the WEBTRANSPORT_SESSION_GONE error code; it MUST NOT send any new datagrams or open any new streams. (6)
|
||||||
|
|
||||||
|
%% @todo wt_session_gone_client/server
|
||||||
|
|
||||||
%% To terminate a session with a detailed error message, an application MAY send an HTTP capsule [HTTP-DATAGRAM] of type CLOSE_WEBTRANSPORT_SESSION (0x2843). (6)
|
%% To terminate a session with a detailed error message, an application MAY send an HTTP capsule [HTTP-DATAGRAM] of type CLOSE_WEBTRANSPORT_SESSION (0x2843). (6)
|
||||||
|
%% @todo close_wt_session_client/server
|
||||||
|
|
||||||
%% Application Error Message: A UTF-8 encoded error message string provided by the application closing the session. The message takes up the remainder of the capsule, and its length MUST NOT exceed 1024 bytes. (6)
|
%% Application Error Message: A UTF-8 encoded error message string provided by the application closing the session. The message takes up the remainder of the capsule, and its length MUST NOT exceed 1024 bytes. (6)
|
||||||
|
|
||||||
|
%% @todo close_wt_session_app_code_msg_client
|
||||||
|
|
||||||
|
close_wt_session_app_code_server(Config) ->
|
||||||
|
doc("The WT server can close a single session with an application error code. "
|
||||||
|
"(draft_webtrans_http3 4.6)"),
|
||||||
|
%% Connect to the WebTransport server.
|
||||||
|
#{
|
||||||
|
conn := Conn,
|
||||||
|
connect_stream_ref := ConnectStreamRef,
|
||||||
|
session_id := SessionID
|
||||||
|
} = do_webtransport_connect(Config),
|
||||||
|
%% Create a bidi stream, send a special instruction to make it initiate the close.
|
||||||
|
{ok, LocalStreamRef} = quicer:start_stream(Conn, #{}),
|
||||||
|
{ok, _} = quicer:send(LocalStreamRef, <<1:2, 16#41:14, 0:2, SessionID:6,
|
||||||
|
"TEST:close_app_code">>),
|
||||||
|
%% Receive the CLOSE_WEBTRANSPORT_SESSION capsule on the CONNECT stream.
|
||||||
|
CloseWTSessionCapsule = cow_capsule:close_wt_session(1234567890, <<>>),
|
||||||
|
{fin, CloseWTSessionCapsule} = do_receive_data(ConnectStreamRef),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
close_wt_session_app_code_msg_server(Config) ->
|
||||||
|
doc("The WT server can close a single session with an application error code "
|
||||||
|
"and an application error message. (draft_webtrans_http3 4.6)"),
|
||||||
|
%% Connect to the WebTransport server.
|
||||||
|
#{
|
||||||
|
conn := Conn,
|
||||||
|
connect_stream_ref := ConnectStreamRef,
|
||||||
|
session_id := SessionID
|
||||||
|
} = do_webtransport_connect(Config),
|
||||||
|
%% Create a bidi stream, send a special instruction to make it initiate the close.
|
||||||
|
{ok, LocalStreamRef} = quicer:start_stream(Conn, #{}),
|
||||||
|
{ok, _} = quicer:send(LocalStreamRef, <<1:2, 16#41:14, 0:2, SessionID:6,
|
||||||
|
"TEST:close_app_code_msg">>),
|
||||||
|
%% Receive the CLOSE_WEBTRANSPORT_SESSION capsule on the CONNECT stream.
|
||||||
|
CloseWTSessionCapsule = iolist_to_binary(cow_capsule:close_wt_session(1234567890,
|
||||||
|
<<"onetwothreefourfivesixseveneightnineten">>)),
|
||||||
|
{fin, CloseWTSessionCapsule} = do_receive_data(ConnectStreamRef),
|
||||||
|
ok.
|
||||||
|
|
||||||
%% An endpoint that sends a CLOSE_WEBTRANSPORT_SESSION capsule MUST immediately send a FIN. The endpoint MAY send a STOP_SENDING to indicate it is no longer reading from the CONNECT stream. The recipient MUST either close or reset the stream in response. (6)
|
%% An endpoint that sends a CLOSE_WEBTRANSPORT_SESSION capsule MUST immediately send a FIN. The endpoint MAY send a STOP_SENDING to indicate it is no longer reading from the CONNECT stream. The recipient MUST either close or reset the stream in response. (6)
|
||||||
|
%% @todo close_wt_session_server_fin
|
||||||
|
%% @todo The part about close/reset should be tested in close_wt_session_client.
|
||||||
|
|
||||||
%% If any additional stream data is received on the CONNECT stream after receiving a CLOSE_WEBTRANSPORT_SESSION capsule, the stream MUST be reset with code H3_MESSAGE_ERROR. (6)
|
%% If any additional stream data is received on the CONNECT stream after receiving a CLOSE_WEBTRANSPORT_SESSION capsule, the stream MUST be reset with code H3_MESSAGE_ERROR. (6)
|
||||||
|
%% @todo close_wt_session_followed_by_data
|
||||||
|
|
||||||
%% Cleanly terminating a CONNECT stream without a CLOSE_WEBTRANSPORT_SESSION capsule SHALL be semantically equivalent to terminating it with a CLOSE_WEBTRANSPORT_SESSION capsule that has an error code of 0 and an empty error string. (6)
|
%% Cleanly terminating a CONNECT stream without a CLOSE_WEBTRANSPORT_SESSION capsule SHALL be semantically equivalent to terminating it with a CLOSE_WEBTRANSPORT_SESSION capsule that has an error code of 0 and an empty error string. (6)
|
||||||
|
%% @todo connect_stream_closed_app_code_0_empty_msg
|
||||||
|
|
||||||
|
%% @todo This one is about gracefully closing HTTP/3 connection with WT sessions.
|
||||||
%% the endpoint SHOULD wait until all CONNECT streams have been closed by the peer before sending the CONNECTION_CLOSE (6)
|
%% the endpoint SHOULD wait until all CONNECT streams have been closed by the peer before sending the CONNECTION_CLOSE (6)
|
||||||
|
|
||||||
%% Helpers.
|
%% Helpers.
|
||||||
|
|
|
@ -50,7 +50,13 @@ webtransport_handle(Event = {stream_data, _StreamID, _IsFin, <<"TEST:", Test/bit
|
||||||
{[{open_stream, OpenStreamRef, bidi, <<>>}],
|
{[{open_stream, OpenStreamRef, bidi, <<>>}],
|
||||||
Streams#{OpenStreamRef => bidi}};
|
Streams#{OpenStreamRef => bidi}};
|
||||||
<<"initiate_close">> ->
|
<<"initiate_close">> ->
|
||||||
{[initiate_close], Streams}
|
{[initiate_close], Streams};
|
||||||
|
<<"close">> ->
|
||||||
|
{[close], Streams};
|
||||||
|
<<"close_app_code">> ->
|
||||||
|
{[{close, 1234567890}], Streams};
|
||||||
|
<<"close_app_code_msg">> ->
|
||||||
|
{[{close, 1234567890, <<"onetwothreefourfivesixseveneightnineten">>}], Streams}
|
||||||
end;
|
end;
|
||||||
webtransport_handle(Event = {stream_data, StreamID, IsFin, Data}, Streams) ->
|
webtransport_handle(Event = {stream_data, StreamID, IsFin, Data}, Streams) ->
|
||||||
ct:pal("WT handle ~p~n", [Event]),
|
ct:pal("WT handle ~p~n", [Event]),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue