Skip to content

Conversation

@falbrechtskirchinger
Copy link
Contributor

@falbrechtskirchinger falbrechtskirchinger commented Feb 4, 2025

This PR lays the groundwork for an upcoming implementation of WebSockets. It makes the following changes:

  1. Stream handler: Introduce a new callback StreamHandler. If set, it replaces all other content-serving mechanisms and – in conjunction with a response handler – provides a minimal interface to implement a WebSocket client. The Response struct gained a set_stream_handler() function to accomplish the same server-side.
  2. Stream changes: Add nonblocking_read_size() which returns a size that is guaranteed to not block read() calls. read() may return fewer bytes including none at all. Add timeout setters, to allow other protocol implementations to set more appropriate timeouts in stream handlers.
  3. random_bytes(): Refactor random_string() into random_bytes(). This will be used to generate the masking key for WebSockets.
  4. select_read(): Add a two FD version of select_read() with support for infinite timeouts. This change is critical to implement bidirectional communication. The WebSockets implementation will use this to interrupt reads when a message is added to the send queue. Branching on the non-type template parameter ensures the common, single FD case isn't pessimized.
  5. Forward declaration: Forward declare a few functions used by the WebSockets header to allow it to compile with split httplib.{h,cc}.

I'm aware of #1103 and while I liked it initially, IMHO, it falls short of its goal of supporting protocol switching generically. The stream handler mechanism doesn't bill itself as anything but another way to handle the body of an HTTP response. It's meant as a low-level, advanced feature that doesn't have to be exposed in the high-level API. (Protocol switching isn't limited to GET requests.)
I've happily taken some inspiration from it, though. (Thanks, @PixlRainbow!)

Checklist:

  • Explore requirements for WebSocket servers.
  • Replace all occurrences of std::function<bool(Stream&)> with StreamHandler?
  • Test on platforms other than Linux. Also, test with CPPHTTPLIB_USE_POLL where applicable.
  • Test split header/implementation.
  • Documentation.

Introduce a new callback StreamHandler that replaces all other
content-serving mechanisms, if set. The handler is not called in
response to HEAD or CONNECT requests, or when following a redirect.

In conjunction with a response handler, it provides a minimal interface
to implement a WebSocket client.
Add a function to streams for obtaining the maximum size that may be
passed to read() without blocking. read() may return fewer bytes
including none.
Refactor detail::random_string() to output random bytes in addition to
random alphanumeric strings, and change the interface to fill a buffer
given a char pointer and length, instead of returning a string.

Rename the new function to random_bytes().

Add an overload random_string() that implements the old behavior on top
of random_bytes().
Add an overload for detail::select_read() that accepts two FDs and
reports readability via two boolean out parameters. This is required to
implement interruptible reads.

Both overloads build on top of a common implementation that optimizes to
the same assembly for the common, single FD case.
- random_bytes()
- random_string()
- select_read()
- set_nonblocking()
Similarly to client stream handlers, setting a handler on the response
replaces all other content-serving mechanisms.  No connection-,
content-, or ranges-related headers are added.

This provides a minimal interface to implement a WebSocket server.
By passing -1 in sec, the timeout becomes infinite. This is only valid
for the 2 FD version.
@falbrechtskirchinger falbrechtskirchinger marked this pull request as ready for review February 6, 2025 22:03
@falbrechtskirchinger
Copy link
Contributor Author

Re-submitting in smaller pieces.

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