Skip to content

Cleanup (http-server): refactor and tidy HTTP server implementation#2284

Merged
stephenberry merged 9 commits intostephenberry:mainfrom
clagix:cleanup-http-server
Feb 1, 2026
Merged

Cleanup (http-server): refactor and tidy HTTP server implementation#2284
stephenberry merged 9 commits intostephenberry:mainfrom
clagix:cleanup-http-server

Conversation

@clagix
Copy link
Copy Markdown
Contributor

@clagix clagix commented Jan 30, 2026

  • Refactored the HTTP server to store the entire request struct (method, target, headers, body) on connection_state->request_
  • Remove unused method process_full_request (which seems to be replaced by the almost identically method process_full_request_with_conn)
  • Removed now unneeded parameters and lambda captures.

This removes the need to pass method, target, headers, body, and similar
request data through function arguments or lambda captures.
@clagix clagix changed the title cleanup(http-server): refactor and tidy HTTP server implementation Cleanup (http-server): refactor and tidy HTTP server implementation Jan 30, 2026
@wilkolbrzym-coder
Copy link
Copy Markdown

Nice cleanup on the request handling! I took a look at the implementation and noticed a critical issue with the request_ state persistence.
Since conn->request_ now lives for the lifetime of the connection, we have a header leak on Keep-Alive connections. The headers map isn't cleared between requests, so Request B will inherit headers from Request A. We need to call conn->request_.headers.clear() (and probably clear the body too) at the start of process_request_data to ensure a clean state.
Also, handle_websocket_upgrade still uses the oldsend_error_response path, which doesn't handle the connection state or socket closing, potentially leaving the connection in limbo on failure.
We should probably address these before merging. Thoughts?

@clagix
Copy link
Copy Markdown
Contributor Author

clagix commented Jan 31, 2026

Thanks — you're absolutely right. I've added clear() calls to reset the request structure before processing the next request when using "Connection: keep-alive".
I also refactored the handle_websocket_upgrade and handle_streaming_request methods. I'm not a WebSocket expert, so I'd appreciate someone reviewing the updated error handling, in particular the use of error_response_with_close().

@wilkolbrzym-coder
Copy link
Copy Markdown

It looks solid! Resetting the request state was crucial for keeping things alive, and the new error handling for WS/streaming is much more reliable.

One minor note: the old send_response methods (for raw sockets) seem to be unused now and can be removed.

@stephenberry
Copy link
Copy Markdown
Owner

Thanks @clagix for this contribution, and @wilkolbrzym-coder for looking over this PR. I'll try to look over this soon to get it merged.

@wilkolbrzym-coder
Copy link
Copy Markdown

Thanks @clagix for this contribution, and @wilkolbrzym-coder for looking over this PR. I'll try to look over this soon to get it merged.

You're welcome! Glad I could help.

@clagix
Copy link
Copy Markdown
Contributor Author

clagix commented Feb 1, 2026

Thanks, @stephenberry, for considering the PR, and thanks, @wilkolbrzym-coder, for the review and helpful suggestions!

This method is now replaced by `send_response_with_conn()`
@clagix
Copy link
Copy Markdown
Contributor Author

clagix commented Feb 1, 2026

I’ve also removed the unused send_response() method.

I’d prefer to move all *_with_conn() methods into the connection_state struct. From an OOP perspective, it seems cleaner to place methods on the type they primarily operate on (aligning with the single responsibility principle) rather than encoding that relationship in the method name.

What do you think about this approach, @stephenberry and @wilkolbrzym-coder ?

@wilkolbrzym-coder
Copy link
Copy Markdown

I’ve also removed the unused send_response() method.

I’d prefer to move all *_with_conn() methods into the connection_state struct. From an OOP perspective, it seems cleaner to place methods on the type they primarily operate on (aligning with the single responsibility principle) rather than encoding that relationship in the method name.

What do you think about this approach, @stephenberry and @wilkolbrzym-coder ?

While the OOP approach is cleaner, process_full_request needs access to the router owned by the server. Moving it to connection_state introduces coupling complications (server dependencies).
Given that this PR fixes a logic bug/security issue, I'd prefer to merge it as-is (with the current structure) to get the fix in. We can refactor connection_state in a dedicated, separate PR.

@clagix
Copy link
Copy Markdown
Contributor Author

clagix commented Feb 1, 2026

@wilkolbrzym-coder you’re right. We should wrap up this PR and prepare it for merge. The primary goal of this PR is to improve code readability, with some minor improvements to security and performance.

We can then prepare a follow-up PR to move the _with_conn methods and evaluate any new dependencies or couplings introduced by those changes.

@stephenberry
Copy link
Copy Markdown
Owner

I reviewed the changes and I think this cleanup is in a good place. More cleanup and optimization can happen in future requests. So, I'll merge it in. Thanks for the contributions, it's always nice to simplify code!

@stephenberry stephenberry merged commit 22aed07 into stephenberry:main Feb 1, 2026
43 checks passed
@clagix
Copy link
Copy Markdown
Contributor Author

clagix commented Feb 1, 2026

@stephenberry

We could probably pass conn->request_ directly to start() here. I left the req variable in place to be safe, since I’m not deeply familiar with the websocket internals.

// Create request object for WebSocket handler (move since conn is consumed)
         request req{std::move(conn->request_)};

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.

3 participants