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

support custom plugins for HTTP 101 protocol switch #1103

Open
wants to merge 16 commits into
base: master
Choose a base branch
from

Conversation

PixlRainbow
Copy link
Contributor

Adds a way to add handlers for various protocols and support alternate protocols which use HTTP for initial negotiation.

We will not be responsible for implementing any of those handlers ourselves, but this should at least make it possible for downstream developers to insert their own middleware to partially handle websockets, IRC or other protocols within the same client or server.
I have successfully created an example websocket client plugin with bidirectional support, but have not published it as it seems to have problems with high traffic flow and compatibility with some servers.

@PixlRainbow
Copy link
Contributor Author

cleaned up and published the example websocket plugin, but for now not sure how to identify an unexpected disconnect

@yhirose
Copy link
Owner

yhirose commented Dec 31, 2021

@PixlRainbow, thank you for working on this. It looks very interesting enhancement!

but for now not sure how to identify an unexpected disconnect

Could is_socket_alive be used for this unexpected disconnect check?

cpp-httplib/httplib.h

Lines 2365 to 2374 in 9e0ff7b

inline bool is_socket_alive(socket_t sock) {
const auto val = detail::select_read(sock, 0, 0);
if (val == 0) {
return true;
} else if (val < 0 && errno == EBADF) {
return false;
}
char buf[1];
return detail::read_socket(sock, &buf[0], sizeof(buf), MSG_PEEK) > 0;
}

@PixlRainbow
Copy link
Contributor Author

Oddly enough, stability issues with high traffic flow only seem to occur in plain http and not in encrypted https. It appears raw TCP sockets have a chance to drop a byte or two every so often, which is enough to break my websocket example implementation.

I have noticed another issue that a few servers will simply jump directly to another protocol with HTTP 200 code without going through standard HTTP 101 signalling and negotiation. Web browsers seem to be able to handle these cases fine, but I suspect that is because they have the benefit of having an API dedicated to websocket. May need to add an extra method to allow the user to force a protocol type.

@PixlRainbow PixlRainbow marked this pull request as ready for review April 26, 2022 17:59
@PixlRainbow
Copy link
Contributor Author

PixlRainbow commented Apr 27, 2022

I don't intend to create an example for the server side, but I added server side support to have feature parity between client and server.

@PixlRainbow
Copy link
Contributor Author

@yhirose ?

@yhirose
Copy link
Owner

yhirose commented May 1, 2022

@PixlRainbow, sorry for the late reply. I'll take a look at it when I have time.

@yhirose
Copy link
Owner

yhirose commented May 6, 2022

@PixlRainbow, I just wonder what the comment 'NOT PRODUCTION READY!' in example/websocket-plugin.cpp means. Does it mean that making a production level WebSocket implementation is too hard with this extension?

I really appreciate your efforts though, I cannot picture how other users including myself can see real benefit from this pull request. If users want a production level WebSocket solution, I'd recomment uWebSockets which is fully tested for WebSocket support.

@PixlRainbow
Copy link
Contributor Author

PixlRainbow commented May 7, 2022

This is a general plugin API. It is not specific to websocket. The websocket code in the example folder is provided merely as an example as to how one could build a plugin to handle alternate protocols. This pull request does not make any websocket-specific changes to httplib itself.

The "not production ready" comment is a reference to how the "example code" is not meant for direct implementation in a production environment; in production, a user would have to consider other things like setting up a custom thread pool implementation and designing their own code for reusability.

It is similar to how we have an example for SSE in the examples folder that makes use of the content provider/receiver API, but SSE is not directly implemented inside httplib.

A real production use case is that a user could use this API to build an adapter between httplib and an existing websocket library to provide the websocket support on the same network port. Or perhaps instead of websocket, they could build their own adapter that allows them to handle http/2.0 requests without requiring us to directly maintain the actual protocol. Or more likely, they can use http 101 as negotiation onto a TCP/TLS protobuf stream.

@Fransferdy
Copy link

Oddly enough, stability issues with high traffic flow only seem to occur in plain http and not in encrypted https. It appears raw TCP sockets have a chance to drop a byte or two every so often, which is enough to break my websocket example implementation.

I have noticed another issue that a few servers will simply jump directly to another protocol with HTTP 200 code without going through standard HTTP 101 signalling and negotiation. Web browsers seem to be able to handle these cases fine, but I suspect that is because they have the benefit of having an API dedicated to websocket. May need to add an extra method to allow the user to force a protocol type.

Not sure if you have fixed this already, but a few years ago I fixed this problem in a huge open source networking library.
What happens is fairly simple actually, when you do a tcp READ, it may or may not have all bytes ready, it may give you 300 bytes out of 310, you have to keep calling tcp read and keep inserting at the end of your buffers until all the bytes you expect to receive have arrived. This situation only happens in high traffic situations, which is why many people implement on top of TCP wrong(they never catch this situation when testing). The TCP protocol makes it impossible to drop bytes, meaning, if you are dropping bytes with your high level read, it simply means you didn't pull all bytes, since a few bytes weren't available yet when you did your TCP read, and you didn't keep polling for more bytes.

@PixlRainbow
Copy link
Contributor Author

PixlRainbow commented Aug 17, 2022

it may give you 300 bytes out of 310

I do keep polling the Stream interface though, and I make use of the returned value of "number of bytes read"; I don't just assume that the buffer will always be filled. I wonder if the httplib Stream interface is dropping unread bytes before I can read them.

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.

None yet

3 participants