diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/docs/writing-tests/h2tests.md | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/docs/writing-tests/h2tests.md')
-rw-r--r-- | testing/web-platform/tests/docs/writing-tests/h2tests.md | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/testing/web-platform/tests/docs/writing-tests/h2tests.md b/testing/web-platform/tests/docs/writing-tests/h2tests.md new file mode 100644 index 0000000000..7745dca55d --- /dev/null +++ b/testing/web-platform/tests/docs/writing-tests/h2tests.md @@ -0,0 +1,152 @@ +# Writing H2 Tests + +These instructions assume you are already familiar with the testing +infrastructure and know how to write a standard HTTP/1.1 test. + +On top of the standard `main` handler that the H1 server offers, the +H2 server also offers support for specific frame handlers in the Python +scripts. Currently there is support for for `handle_headers` and `handle_data`. +Unlike the `main` handler, these are run whenever the server receives a +HEADERS frame (RequestReceived event) or a DATA frame (DataReceived event). +`main` can still be used, but it will be run after the server has received +the request in its entirety. + +Here is what a Python script for a test might look like: +```python +def handle_headers(frame, request, response): + if request.headers["test"] == "pass": + response.status = 200 + response.headers.update([('test', 'passed')]) + response.write_status_headers() + else: + response.status = 403 + response.headers.update([('test', 'failed')]) + response.write_status_headers() + response.writer.end_stream() + +def handle_data(frame, request, response): + response.writer.write_data(frame.data[::-1]) + +def main(request, response): + response.writer.write_data('\nEnd of File', last=True) +``` + +The above script is fairly simple: +1. Upon receiving the HEADERS frame, `handle_headers` is run. + - This checks for a header called 'test' and checks if it is set to 'pass'. + If true, it will immediately send a response header, otherwise it responds + with a 403 and ends the stream. +2. Any DATA frames received will then be handled by `handle_data`. This will +simply reverse the data and send it back. +3. Once the request has been fully received, `main` is run which will send +one last DATA frame and signal its the end of the stream. + +## Response Writer API ## + +The H2Response API is pretty much the same as the H1 variant, the main API +difference lies in the H2ResponseWriter which is accessed through `response.writer` + +--- + +#### `write_headers(self, headers, status_code, status_message=None, stream_id=None, last=False):` +Write a HEADER frame using the H2 Connection object, will only work if the +stream is in a state to send HEADER frames. This will automatically format +the headers so that pseudo headers are at the start of the list and correctly +prefixed with ':'. Since this using the H2 Connection object, it requires that +the stream is in the correct state to be sending this frame. + +> <b>Note</b>: Will raise ProtocolErrors if pseudo headers are missing. + +- <b>Parameters</b> + + - <b>headers</b>: List of (header, value) tuples + - <b>status_code</b>: The HTTP status code of the response + - <b>stream_id</b>: Id of stream to send frame on. Will use the request stream ID if None + - <b>last</b>: Flag to signal if this is the last frame in stream. + +--- + +#### `write_data(self, item, last=False, stream_id=None):` +Write a DATA frame using the H2 Connection object, will only work if the +stream is in a state to send DATA frames. Uses flow control to split data +into multiple data frames if it exceeds the size that can be in a single frame. +Since this using the H2 Connection object, it requires that the stream is in +the correct state to be sending this frame. + +- <b>Parameters</b> + + - <b>item</b>: The content of the DATA frame + - <b>last</b>: Flag to signal if this is the last frame in stream. + - <b>stream_id</b>: Id of stream to send frame on. Will use the request stream ID if None + +--- + +#### `write_push(self, promise_headers, push_stream_id=None, status=None, response_headers=None, response_data=None):` +This will write a push promise to the request stream. If you do not provide +headers and data for the response, then no response will be pushed, and you +should send them yourself using the ID returned from this function. + +- <b>Parameters</b> + - <b>promise_headers</b>: A list of header tuples that matches what the client would use to + request the pushed response + - <b>push_stream_id</b>: The ID of the stream the response should be pushed to. If none given, will + use the next available id. + - <b>status</b>: The status code of the response, REQUIRED if response_headers given + - <b>response_headers</b>: The headers of the response + - <b>response_data</b>: The response data. + +- <b>Returns</b>: The ID of the push stream + +--- + +#### `write_raw_header_frame(self, headers, stream_id=None, end_stream=False, end_headers=False, frame_cls=HeadersFrame):` +Unlike `write_headers`, this does not check to see if a stream is in the +correct state to have HEADER frames sent through to it. It also won't force +the order of the headers or make sure pseudo headers are prefixed with ':'. +It will build a HEADER frame and send it without using the H2 Connection +object other than to HPACK encode the headers. + +> <b>Note</b>: The `frame_cls` parameter is so that this class can be reused +by `write_raw_continuation_frame`, as their construction is identical. + +- <b>Parameters</b> + - <b>headers</b>: List of (header, value) tuples + - <b>stream_id</b>: Id of stream to send frame on. Will use the request stream ID if None + - <b>end_stream</b>: Set to `True` to add END_STREAM flag to frame + - <b>end_headers</b>: Set to `True` to add END_HEADERS flag to frame + +--- + +#### `write_raw_data_frame(self, data, stream_id=None, end_stream=False):` +Unlike `write_data`, this does not check to see if a stream is in the correct +state to have DATA frames sent through to it. It will build a DATA frame and +send it without using the H2 Connection object. It will not perform any flow control checks. + +- <b>Parameters</b> + - <b>data</b>: The data to be sent in the frame + - <b>stream_id</b>: Id of stream to send frame on. Will use the request stream ID if None + - <b>end_stream</b>: Set to True to add END_STREAM flag to frame + +--- + +#### `write_raw_continuation_frame(self, headers, stream_id=None, end_headers=False):` +This provides the ability to create and write a CONTINUATION frame to the +stream, which is not exposed by `write_headers` as the h2 library handles +the split between HEADER and CONTINUATION internally. Will perform HPACK +encoding on the headers. It also ignores the state of the stream. + +This calls `write_raw_data_frame` with `frame_cls=ContinuationFrame` since +the HEADER and CONTINUATION frames are constructed in the same way. + +- <b>Parameters</b>: + - <b>headers</b>: List of (header, value) tuples + - <b>stream_id</b>: Id of stream to send frame on. Will use the request stream ID if None + - <b>end_headers</b>: Set to True to add END_HEADERS flag to frame + +--- + +#### `end_stream(self, stream_id=None):` +Ends the stream with the given ID, or the one that request was made on if no ID given. + +- <b>Parameters</b> + - <b>stream_id</b>: Id of stream to send frame on. Will use the request stream ID if None |