Buy commercial curl support from WolfSSL. We help you work
out your issues, debug your libcurl applications, use the API, port to new
platforms, add new features and more. With a team lead by the curl founder
himself.
RE: more WebSockets
- Contemporary messages sorted: [ by date ] [ by thread ] [ by subject ] [ by author ] [ by messages with attachments ]
From: Dmitry Karpov via curl-library <curl-library_at_cool.haxx.se>
Date: Thu, 12 Aug 2021 19:23:37 +0000
> If you by "assembling" mean concatenating multiple frames until the FIN frame, then my thinking was yes, so that we wouldn't have to buffer up potentially a large amount of data before passing it on.
> How do other client implementations work and how do they handle the unlimited message size? Should we just impose our own maximum size and have applications raise it when needed?
My own C++ WS implementation handles it the following way:
1. It allows to set the max size for the message buffer in WS options, so the WS client library will assemble the message and accumulate the data up to this size and then pass it to the WS application layer.
If the incoming message doesn't fit the buffer, then WS error "too large data" is triggered.
The message buffer controlled by this option is an internal buffer and part of my WS library, so for clients that use relatively compact WS messages or don't have memory constraints and don't want to deal with memory allocations, they will get a pointer to this buffer in the "OnMessageReceived" event/callback.
This is the easiest and the most straightforward way of getting WS messages to clients that don't do something too fancy with their WS servers.
2. For cases when client wants to set up its own message buffer (i.e., in a special memory region), and for very large messages where allocating big message buffer doesn't make sense or not desired, I provide "OnMessageArrived" event/callback - when the first WS frame for the message is received.
By handling this event, the client has an option either to specify the message buffer or to set up a special "stream" interface or callback to get the parts of message data when they arrive.
This is part of my "WS message" layer, so the clients don't see any individual frames and deal only with WS messages.
> I want the API to be "good enough" to get sufficiently advanced websockets communication going against "most" server-side websockets implementations.
> When we think it is, we can start working on code to make it real and then see how it actually works with some early test client applications. The feature will be marked EXPERIMENTAL until we deem it ready anyway so there will be wiggle room to change things around all the way through until we decide it is fine enough to carve in stone (and ship enabled by default in a release).
Yes, this seems like a good approach to start with.
Obviously, most folks will be interested in getting fully implemented WS message layer, which libcurl may eventually provide, but I think that it is a right decision to provide "layers" of WS implementation (WS handshake, frames, and later messages), so any existing clients will be able to "hook" the layer(s) they want.
I already has my WS implementation in C++ that "hooks" into libcurl, so I may want to keep it and "hook" only into the handshake or frame level.
> Can you elaborate on this? Aren't ws messages just the payload from N frames concatenated and delivered?
> I know there can be control frames injected in the middle of stream of data frames, but the only standard such frames are close, ping and pong and I imagine libcurl would handle them and thus what is passed on to the client would be an unbroken stream of data frames.
Yes, this is true. But clients may want to handle ping/pong and other proprietary control frames (using reserved opcodes) in some special way.
So libcurl will probably have to provide a way to "immediately" send/receive control messages while data message delivery operation is in progress.
If libcurl will implement only the WS frame lever (at least initially) then it is client responsibility to make sure that the control frames go out before any data frames in the sending queue.
But if the WS message layer is implemented, then it should understand the priority of the control messages(frames) and send them over the wire (or deliver a received control message) before any pending data message(frames).
> There's no way then for libcurl to avoid having to buffer the entire ws message, right?
For relatively small messages, yes, libcurl should allocate a buffer of some size (size is controlled by some WS option) to make client life easier.
But for large message messages (or custom memory allocations) it should probably provide a way to either set a "stream" interface via callback or pass a pointer to the client message buffer in the " OnMessageArrived" event.
> This has been mentioned before but I don't understand why.
> Why does it matter to an application exactly how the information arrived? The application doesn't see the websocket protocol and it doesn't have to know much about it using this API.
I guess you are mentioning the upper application layers (like JS WS clients) which deal only with a subset of WS messages - like Text and Binary, Ping/Pong and Close.
Such high-level clients expose only these messages in a kind of "abstract" way and thus they kind of hide the opcodes.
But libcurl, in my opinion, is a transport layer below that higher layer, so it should provide WS opcodes in frame and messages because it is important "protocol" information (i.e., like HTTP method), and some clients may need to use reserved opcodes for their proprietary communication.
> Can opcode 3-7 and b-f be used "at will" by implementations to signal something without negotiating an extension?
In general, all extensions must be negotiated, but I saw cases when some WS client/server implementations used reserved control opcodes for their proprietary communication.
I guess they used it for some kind of "real-time" (as control frames are short and has delivery priority over data messages) out-of-band protocol.
Thanks,
Dmitry Karpov
-----Original Message-----
From: Daniel Stenberg <daniel_at_haxx.se>
Sent: Thursday, August 12, 2021 12:13 AM
To: Dmitry Karpov via curl-library <curl-library_at_cool.haxx.se>
Cc: Dmitry Karpov <dkarpov_at_roku.com>
Subject: RE: more WebSockets
On Wed, 11 Aug 2021, Dmitry Karpov via curl-library wrote:
Thanks for the feedback, this is very helpful!
> From a brief look at the document, it looks like Curl will provide
> only WebSocket frame level of communication, so the client will have
> to implement full message assembling itself.
If you by "assembling" mean concatenating multiple frames until the FIN frame, then my thinking was yes, so that we wouldn't have to buffer up potentially a large amount of data before passing it on.
How do other client implementations work and how do they handle the unlimited message size? Should we just impose our own maximum size and have applications raise it when needed?
> If my understanding is correct, then it seems like a good initial
> approach to me - it handles the most critical WS steps: WS handshake,
> frame sending/receiving and error reporting, even though it leaves WS
> message layer communication (sending/receiving full message) to the clients.
That's my current thinking, yes.
I want the API to be "good enough" to get sufficiently advanced websockets communication going against "most" server-side websockets implementations.
When we think it is, we can start working on code to make it real and then see how it actually works with some early test client applications. The feature will be marked EXPERIMENTAL until we deem it ready anyway so there will be wiggle room to change things around all the way through until we decide it is fine enough to carve in stone (and ship enabled by default in a release).
> As it was mentioned in some of our WS-related discussions, the WS
> message layer is more complex than the framing layer
Can you elaborate on this? Aren't ws messages just the payload from N frames concatenated and delivered?
I know there can be control frames injected in the middle of stream of data frames, but the only standard such frames are close, ping and pong and I imagine libcurl would handle them and thus what is passed on to the client would be an unbroken stream of data frames.
> libcurl should provide a way to the client to handle incoming message data.
> This means that besides Frame-based "send/receive" callbacks, as
> described in the document, there should be message-based callbacks on
> top of the Frame-layer, which would allow clients to work with WS
> messages, rather than with WS frames.
There's no way then for libcurl to avoid having to buffer the entire ws message, right?
> A small note about iflags:
>
> " iflags is a bitmask featuring the following (incoming) flags:
> CURLWS_TEXT - this is text data
> CURLWS_BINARY - this is binary data
> CURLWS_FIN - this is also the final fragment of a message
> CURLWS_CLOSE - this transfer is now closed"
>
> The "Text" and "Binary" are special WS frame/message opcodes, so it is
> probably better to distinguish frame flags and the frame opcodes
> instead of mixing them together.
This has been mentioned before but I don't understand why.
Why does it matter to an application exactly how the information arrived? The application doesn't see the websocket protocol and it doesn't have to know much about it using this API.
> If client is supposed to handle WS frames it gets from libcurl, then
> it needs to know the precise opcode along with the frame flags, so it
> can properly handle cases when "control" and "data" frames from
> different messages are intermixed (i.e. one large "data" messages
> intermixed with many "control" messages) and when some "custom"
> opcodes are used for some proprietary WS communications.
To me, that sounds like an argument for providing all the opcodes through to the application. I didn't understand that they are actually used like that, especially not within the same message.
Can opcode 3-7 and b-f be used "at will" by implementations to signal something without negotiating an extension?
Date: Thu, 12 Aug 2021 19:23:37 +0000
> If you by "assembling" mean concatenating multiple frames until the FIN frame, then my thinking was yes, so that we wouldn't have to buffer up potentially a large amount of data before passing it on.
> How do other client implementations work and how do they handle the unlimited message size? Should we just impose our own maximum size and have applications raise it when needed?
My own C++ WS implementation handles it the following way:
1. It allows to set the max size for the message buffer in WS options, so the WS client library will assemble the message and accumulate the data up to this size and then pass it to the WS application layer.
If the incoming message doesn't fit the buffer, then WS error "too large data" is triggered.
The message buffer controlled by this option is an internal buffer and part of my WS library, so for clients that use relatively compact WS messages or don't have memory constraints and don't want to deal with memory allocations, they will get a pointer to this buffer in the "OnMessageReceived" event/callback.
This is the easiest and the most straightforward way of getting WS messages to clients that don't do something too fancy with their WS servers.
2. For cases when client wants to set up its own message buffer (i.e., in a special memory region), and for very large messages where allocating big message buffer doesn't make sense or not desired, I provide "OnMessageArrived" event/callback - when the first WS frame for the message is received.
By handling this event, the client has an option either to specify the message buffer or to set up a special "stream" interface or callback to get the parts of message data when they arrive.
This is part of my "WS message" layer, so the clients don't see any individual frames and deal only with WS messages.
> I want the API to be "good enough" to get sufficiently advanced websockets communication going against "most" server-side websockets implementations.
> When we think it is, we can start working on code to make it real and then see how it actually works with some early test client applications. The feature will be marked EXPERIMENTAL until we deem it ready anyway so there will be wiggle room to change things around all the way through until we decide it is fine enough to carve in stone (and ship enabled by default in a release).
Yes, this seems like a good approach to start with.
Obviously, most folks will be interested in getting fully implemented WS message layer, which libcurl may eventually provide, but I think that it is a right decision to provide "layers" of WS implementation (WS handshake, frames, and later messages), so any existing clients will be able to "hook" the layer(s) they want.
I already has my WS implementation in C++ that "hooks" into libcurl, so I may want to keep it and "hook" only into the handshake or frame level.
> Can you elaborate on this? Aren't ws messages just the payload from N frames concatenated and delivered?
> I know there can be control frames injected in the middle of stream of data frames, but the only standard such frames are close, ping and pong and I imagine libcurl would handle them and thus what is passed on to the client would be an unbroken stream of data frames.
Yes, this is true. But clients may want to handle ping/pong and other proprietary control frames (using reserved opcodes) in some special way.
So libcurl will probably have to provide a way to "immediately" send/receive control messages while data message delivery operation is in progress.
If libcurl will implement only the WS frame lever (at least initially) then it is client responsibility to make sure that the control frames go out before any data frames in the sending queue.
But if the WS message layer is implemented, then it should understand the priority of the control messages(frames) and send them over the wire (or deliver a received control message) before any pending data message(frames).
> There's no way then for libcurl to avoid having to buffer the entire ws message, right?
For relatively small messages, yes, libcurl should allocate a buffer of some size (size is controlled by some WS option) to make client life easier.
But for large message messages (or custom memory allocations) it should probably provide a way to either set a "stream" interface via callback or pass a pointer to the client message buffer in the " OnMessageArrived" event.
> This has been mentioned before but I don't understand why.
> Why does it matter to an application exactly how the information arrived? The application doesn't see the websocket protocol and it doesn't have to know much about it using this API.
I guess you are mentioning the upper application layers (like JS WS clients) which deal only with a subset of WS messages - like Text and Binary, Ping/Pong and Close.
Such high-level clients expose only these messages in a kind of "abstract" way and thus they kind of hide the opcodes.
But libcurl, in my opinion, is a transport layer below that higher layer, so it should provide WS opcodes in frame and messages because it is important "protocol" information (i.e., like HTTP method), and some clients may need to use reserved opcodes for their proprietary communication.
> Can opcode 3-7 and b-f be used "at will" by implementations to signal something without negotiating an extension?
In general, all extensions must be negotiated, but I saw cases when some WS client/server implementations used reserved control opcodes for their proprietary communication.
I guess they used it for some kind of "real-time" (as control frames are short and has delivery priority over data messages) out-of-band protocol.
Thanks,
Dmitry Karpov
-----Original Message-----
From: Daniel Stenberg <daniel_at_haxx.se>
Sent: Thursday, August 12, 2021 12:13 AM
To: Dmitry Karpov via curl-library <curl-library_at_cool.haxx.se>
Cc: Dmitry Karpov <dkarpov_at_roku.com>
Subject: RE: more WebSockets
On Wed, 11 Aug 2021, Dmitry Karpov via curl-library wrote:
Thanks for the feedback, this is very helpful!
> From a brief look at the document, it looks like Curl will provide
> only WebSocket frame level of communication, so the client will have
> to implement full message assembling itself.
If you by "assembling" mean concatenating multiple frames until the FIN frame, then my thinking was yes, so that we wouldn't have to buffer up potentially a large amount of data before passing it on.
How do other client implementations work and how do they handle the unlimited message size? Should we just impose our own maximum size and have applications raise it when needed?
> If my understanding is correct, then it seems like a good initial
> approach to me - it handles the most critical WS steps: WS handshake,
> frame sending/receiving and error reporting, even though it leaves WS
> message layer communication (sending/receiving full message) to the clients.
That's my current thinking, yes.
I want the API to be "good enough" to get sufficiently advanced websockets communication going against "most" server-side websockets implementations.
When we think it is, we can start working on code to make it real and then see how it actually works with some early test client applications. The feature will be marked EXPERIMENTAL until we deem it ready anyway so there will be wiggle room to change things around all the way through until we decide it is fine enough to carve in stone (and ship enabled by default in a release).
> As it was mentioned in some of our WS-related discussions, the WS
> message layer is more complex than the framing layer
Can you elaborate on this? Aren't ws messages just the payload from N frames concatenated and delivered?
I know there can be control frames injected in the middle of stream of data frames, but the only standard such frames are close, ping and pong and I imagine libcurl would handle them and thus what is passed on to the client would be an unbroken stream of data frames.
> libcurl should provide a way to the client to handle incoming message data.
> This means that besides Frame-based "send/receive" callbacks, as
> described in the document, there should be message-based callbacks on
> top of the Frame-layer, which would allow clients to work with WS
> messages, rather than with WS frames.
There's no way then for libcurl to avoid having to buffer the entire ws message, right?
> A small note about iflags:
>
> " iflags is a bitmask featuring the following (incoming) flags:
> CURLWS_TEXT - this is text data
> CURLWS_BINARY - this is binary data
> CURLWS_FIN - this is also the final fragment of a message
> CURLWS_CLOSE - this transfer is now closed"
>
> The "Text" and "Binary" are special WS frame/message opcodes, so it is
> probably better to distinguish frame flags and the frame opcodes
> instead of mixing them together.
This has been mentioned before but I don't understand why.
Why does it matter to an application exactly how the information arrived? The application doesn't see the websocket protocol and it doesn't have to know much about it using this API.
> If client is supposed to handle WS frames it gets from libcurl, then
> it needs to know the precise opcode along with the frame flags, so it
> can properly handle cases when "control" and "data" frames from
> different messages are intermixed (i.e. one large "data" messages
> intermixed with many "control" messages) and when some "custom"
> opcodes are used for some proprietary WS communications.
To me, that sounds like an argument for providing all the opcodes through to the application. I didn't understand that they are actually used like that, especially not within the same message.
Can opcode 3-7 and b-f be used "at will" by implementations to signal something without negotiating an extension?
-- / daniel.haxx.se | Commercial curl support up to 24x7 is available! | Private help, bug fixes, support, ports, new features | https://curl.se/support.html ------------------------------------------------------------------- Unsubscribe: https://cool.haxx.se/list/listinfo/curl-library Etiquette: https://curl.se/mail/etiquette.htmlReceived on 2021-08-12