Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

net: http_server: Fix capturing of request headers on concurrent HTTP2 streams #82444

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

mrodgers-witekio
Copy link
Collaborator

Fixes #82273.

Concurrent HTTP POST requests on different HTTP2 concurrent streams require that the client's header_capture_context is re-used to capture headers on a second stream before all of the body data has been received (and sent to the application) on the first stream.

As a result, any captured headers must be sent to the application callback before any headers can be received on a different stream. In practice this means that for HTTP2 the application callback is called for the first time on receiving a headers frame, before any data frames are received. All subsequent application callbacks will not include the request header data.

While this mechanism is not necessary for HTTP1, it is also updated to only send headers in the first application callback for consistency. However for HTTP/1 the first callback may also include body data in addition to the headers, since there isn't the same clear separation of headers and data into separate frames.

I've also made the header_capture_ctx part of the http_client_ctx regardless of whether CONFIG_HTTP_SERVER_HEADER_CAPTURE is defined, since otherwise it would have been difficult to manage all of the #if defined(...) needed, and replaced with if(IS_ENABLED(...)) where needed (has the bonus that the included code is compile checked regardless of which Kconfig options are defined). The buffers however are defined to a size of zero if CONFIG_HTTP_SERVER_HEADER_CAPTURE is not enabled, so the impact on code/ram size is fairly minimal in case the option is disabled (<200 bytes each for the sample app on ARM Cortex M).

While working on this fix I also spotted a second issue: since the current_detail was stored at a client level rather than a http2 stream level, concurrent HTTP2 POST requests would overwrite the resource detail and lead to data being either associated with the wrong resource detail, or not associated with any resource detail at all (giving a 404 response). This is fixed in the first commit.

Copy link
Contributor

@rlubos rlubos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thank you for the contribution. Note, that we now have another example utilizing HTTP server API (https://github.com/zephyrproject-rtos/zephyr/blob/main/samples/net/prometheus), need to align it with the API changes too.

@mrodgers-witekio mrodgers-witekio force-pushed the http2_request_headers_concurrent_streams branch from e3e15d9 to 682dcce Compare December 3, 2024 09:54
@mrodgers-witekio
Copy link
Collaborator Author

Rebased and updated prometheus sample

rlubos
rlubos previously approved these changes Dec 3, 2024
Copy link
Contributor

@rlubos rlubos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

To correctly handle concurrent HTTP POST requests via different http2
streams on the same client context, it is necessary to store the
resource detail at an HTTP2 stream level rather than at an HTTP client
level, otherwise only one resource detail can be stored.

Signed-off-by: Matt Rodgers <[email protected]>
@mrodgers-witekio mrodgers-witekio force-pushed the http2_request_headers_concurrent_streams branch from 682dcce to b3b6981 Compare December 3, 2024 10:30
@mrodgers-witekio
Copy link
Collaborator Author

Rebased again as I noticed some of the test changes were included in the wrong commit (so tests would have failed on first commit)

jukkar
jukkar previously approved these changes Dec 3, 2024
Comment on lines 188 to 192
uint8_t *data; /** HTTP request data */
size_t data_len; /** Length of HTTP request data */
struct http_header *headers; /** Array of HTTP request headers */
size_t header_count; /** Array length of HTTP request headers */
enum http_header_status headers_status; /** Status of HTTP request headers */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about doxygen comments, but do we need to set them /**< ..... */ here (as they are in the same line as what is being documented)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I just copied the comment formatting from the struct http_response_ctx but it seems this is also wrong, and has comments associated with the wrong fields in the online docs.

Will update to fix both of these.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's correct this way :) it's the syntax for when the comment is put after the element it applies to, i.e. useful when one wants to make everything fit on a single line
https://www.doxygen.nl/manual/docblocks.html#memberdoc

Copy link
Collaborator

@kartben kartben Dec 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops, sorry, looks like I'm blind today. @jukkar's suggestion IS the correct way :) sorry for the noise ☺️

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I just had a look at the web documentation and got this:
image

Have just updated with a fix

Concurrent HTTP POST requests on different HTTP2 concurrent streams
require that the client's header_capture_context is re-used to capture
headers on a second stream before all of the body data has been received
(and sent to the application) on the first stream.

As a result, any captured headers must be sent to the application
callback before any headers can be received on a different stream. In
practice this means that for HTTP2 the application callback is called
for the first time on receiving a headers frame, before any data frames
are received. All subsequent application callbacks will not include the
request header data.

While this mechanism is not necessary for HTTP1, it is also updated to
only send headers in the first application callback for consistency.

Fixes zephyrproject-rtos#82273

Signed-off-by: Matt Rodgers <[email protected]>
Dynamic resource callback function signature has changed to combine the
data, data length and request header details into a struct
http_request_ctx. Update documentation to describe this, and add a note
in the 4.1 migration guide since this is a public API change.

Signed-off-by: Matt Rodgers <[email protected]>
@mrodgers-witekio mrodgers-witekio dismissed stale reviews from jukkar and rlubos via d531324 December 3, 2024 15:36
@mrodgers-witekio mrodgers-witekio force-pushed the http2_request_headers_concurrent_streams branch from b3b6981 to d531324 Compare December 3, 2024 15:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: Networking area: Samples Samples area: Sockets Networking sockets Release Notes To be mentioned in the release notes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

net: http_server: capturing request headers does not correctly account for concurrent HTTP2 streams
5 participants