Skip to content

Replace the HTTP header maps with glz::http_headers - #2718

Open
annihilatorq wants to merge 9 commits into
stephenberry:mainfrom
annihilatorq:feature/http-headers-integration
Open

Replace the HTTP header maps with glz::http_headers#2718
annihilatorq wants to merge 9 commits into
stephenberry:mainfrom
annihilatorq:feature/http-headers-integration

Conversation

@annihilatorq

Copy link
Copy Markdown
Collaborator

Ports the net stack to glz::http_headers (#2709).

Rewriting the header lookups for the new container resulted in fixing two WebSocket bugs. glz::http_headers keeps repeated field names instead of overwriting them, and its .contains_token() replaced the hand-rolled substring and prefix comparisons, so every lookup had to be revisited, and both were found there.

WebSocket implementation bugs

1 - the handshake response check never compared a full field name. The code used a comparison like this:
strncasecmp(name.c_str(), "Upgrade", 7) for the headers "Upgrade", "Connection" and "Sec-WebSocket-Accept" comparing the first N bytes using strncasecmp, where N is the length of the expected header name. The problem is that this approach would accept headers such as "Connection-Something" as valid because the name starts with "Connection", and so on with other headers listed previously.

Example - the old client accepted this response even though the server never sent an Upgrade field:

HTTP/1.1 101 Switching Protocols\r\n
Upgrade-Extra: websocket\r\n
Connection: Upgrade\r\n
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\n

2 - the client is allowed to send a list-based field not as a comma-separated list, but as separate header fields separated by CRLF. The previous issue was that std::unordered_map used a "last-wins" approach, thereby discarding one of the values for that header. In such cases, user could only hope that the last header received contained the required token ("Connection: Upgrade") as the header-field value.

Example - the old server refused this valid request because keep-alive arrived last:

GET /ws HTTP/1.1\r\n
Host: 127.0.0.1:8080\r\n
Upgrade: websocket\r\n
Connection: Upgrade\r\n
Connection: keep-alive\r\n
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n
Sec-WebSocket-Version: 13\r\n\r\n

BREAKING CHANGE: request::headers is now glz::http_headers instead of
std::unordered_map<std::string, std::string>. Names keep the casing they
arrived with instead of being lowercased, and a repeated field no longer
overwrites the one before it. Lookups stay case-insensitive.
BREAKING CHANGE: response::response_headers is now glz::http_headers, along
with every header parameter on http_client and the streaming interfaces.
Response names keep the casing they were given, so content_type() writes
"Content-Type" rather than "content-type".
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant