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: curl websockets
- Contemporary messages sorted: [ by date ] [ by thread ] [ by subject ] [ by author ] [ by messages with attachments ]
From: Weston Schmidt via curl-library <curl-library_at_cool.haxx.se>
Date: Fri, 2 Jul 2021 10:03:13 -0700
I took the bitmask path for my implementation similar to what Daniel
has proposed. I like the flexibility and clarity it offers.
Something like this might work (they need better names probably):
/* WS extension declarations as needed */
CURL_WS_COMPRESS (1 << 10)
/* WS frame flags */
CURL_WS_FRAME_END (1 << 9) /* An extension so that curl can inform the caller
* of curl_easy_ws_recv() that this is the end of
* the frame on the wire in case that is important
* to an extension. */
CURL_WS_FIN (1 << 8)
CURL_WS_RSV1 (1 << 7)
CURL_WS_RSV2 (1 << 6)
CURL_WS_RSV3 (1 << 5)
/* Opcodes */
CURL_WS_OPCODE_0 0
CURL_WS_CONTINUATION 0
CURL_WS_OPCODE_1 1
CURL_WS_TEXT 1
CURL_WS_OPCODE_2 2
CURL_WS_BINARY 2
CURL_WS_OPCODE_3 3
CURL_WS_OPCODE_4 4
CURL_WS_OPCODE_5 5
CURL_WS_OPCODE_6 6
CURL_WS_OPCODE_7 7
CURL_WS_OPCODE_8 8
CURL_WS_CLOSE 8
CURL_WS_OPCODE_9 9
CURL_WS_PING 9
CURL_WS_OPCODE_A 10
CURL_WS_PONG 10
CURL_WS_OPCODE_B 11
CURL_WS_OPCODE_C 12
CURL_WS_OPCODE_D 13
CURL_WS_OPCODE_E 14
CURL_WS_OPCODE_F 15
CURLcode curl_easy_ws_send(CURL *curl, int flags, const void *data, size_t len);
CURLcode curl_easy_ws_recv(CURL *curl, int *flags, void *data, size_t
buflen, size_t *n);
/* Send a binary stream */
curl_easy_ws_send(easy, CURL_WS_BINARY, data, len);
curl_easy_ws_send(easy, CURL_WS_CONTINUATION, data, len);
curl_easy_ws_send(easy, CURL_WS_FIN | CURL_WS_CONTINUATION, data, len);
/* Send a close */
curl_easy_ws_send(easy, CURL_WS_FIN | CURL_WS_CLOSE, data, len);
/* Optional way libcurl could support for OPCODEs 8+ & libcurl asserts
the frame is CURL_WS_FIN)
curl_easy_ws_send(easy, CURL_WS_CLOSE, data, len);
/* Send a ping */
curl_easy_ws_send(easy, CURL_WS_FIN | CURL_WS_PING, data, len);
/* Receiving a binary frame with a buffer that is too small */
curl_easy_ws_recv(easy, &flags, data, buflen, &n);
returns: CURLE_AGAIN
flags: CURL_WS_BINARY
curl_easy_ws_recv(easy, &flags, data, buflen, &n);
returns: CURLE_AGAIN
flags: CURL_WS_FRAME_END | CURL_WS_BINARY
... the frame is complete now
libcurl should be able to do a bit of state checking in case the
caller makes a mistake, but it should only focus on the fragmentation
continuity and validating that a control message is not too long. I
tend to think that libcurl should try to stay out of the business of
validating any of the payload formats because the rules for processing
are tied to frame type & extensions.
This extension concerns me as it renders the
curl_easy_ws_send()/recv() to not have value ... but as long as it
could be implemented using curl_easy_send()/recv() then I'm happy.
https://datatracker.ietf.org/doc/html/draft-ietf-hybi-websocket-multiplexing-11#section-8
tl;dr - they add a few bytes in front of the standard defined WS packet.
On Fri, Jul 2, 2021 at 6:42 AM Felipe Gasper <felipe_at_felipegasper.com> wrote:
>
>
>
> > On Jul 2, 2021, at 7:40 AM, Daniel Stenberg <daniel_at_haxx.se> wrote:
> >
> > On Fri, 2 Jul 2021, Felipe Gasper wrote:
> >
> > Thanks!
> >
> >> The send interface could look much like curl_easy_send():
> >>
> >> curl_ws_send( easy, buffer, buflen, &sent, enum curl_ws_message_type type, enum curl_ws_flags flags )
> >
> > Or perhaps with the type and flags just made as a single bitmask.
>
> This will make it impossible to specify a continuation frame rather than a data frame. (Context: when a message is fragmented, the message’s first frame is text/binary, and the latter ones are continuation.)
>
> That may be OK if curl will commit to abstracting that distinction away.
>
> Also, if WS were to add a 3rd data type -- e.g., via some extension -- then it would be possible to specify CURL_WS_TEXT | CURL_WS_FANCY_NEW_EXT_TYPE, which means curl would need to detect that.
>
> Alternatively, have separate curl_ws_send_text and curl_ws_send_binary functions?
>
> >
> >> Most WS libraries that I’ve seen implement reception as a callback, with a separate control to pause reception of incoming messages.
> >
> > I won't say that's wrong, but I don't see how a callback helps with this API.
> >
> > curl_ws_recv() could just be made to not return until the entire packet can be returned. Or with an option to return a partial one.
>
> I assume you mean something like EAGAIN rather than actually blocking?
>
> Having the caller call curl_ws_recv() directly would mean that that same caller would need to know when the socket has data to read. I don’t know if that’s always a safe assumption.
>
> Also, WS includes ping/pong, so even if the socket has data there might not be any application-level data incoming. (Ping/pong isn’t usually exposed to the application.)
>
> > An area for consideration is how to deal with the buffer (size). I mean, if it wants to wait for a full packet to arrive I suppose it can be large and it might not fit in the user-provided buffer, so the application would need to call it multiple times do drain the data.
>
> Would this argue in favour of a callback, then? Curl could just allocate as it knows it needs. The caller, then, could either memcpy or assume ownership of the buffer (by, e.g., returning CURL_WS_NOFREE from the callback).
>
> Alternatively, there is precedent here with SOCK_SEQPACKET sockets, though curl has the ability to deliver what IMO would be a smoother interface.
>
> >
> > I took the liberty of jotting down some of these API thoughts in the wiki page. Still incomplete and not really functional, but I figured it could help to stir up our collective minds..
> >
> > https://github.com/curl/curl/wiki/WebSockets#api
>
> I feel funny asking this, but is there a comment feature in this wiki system? I don’t see such, but it would seem useful.
>
> -FG
-------------------------------------------------------------------
Unsubscribe: https://cool.haxx.se/list/listinfo/curl-library
Etiquette: https://curl.se/mail/etiquette.html
Received on 2021-07-02
Date: Fri, 2 Jul 2021 10:03:13 -0700
I took the bitmask path for my implementation similar to what Daniel
has proposed. I like the flexibility and clarity it offers.
Something like this might work (they need better names probably):
/* WS extension declarations as needed */
CURL_WS_COMPRESS (1 << 10)
/* WS frame flags */
CURL_WS_FRAME_END (1 << 9) /* An extension so that curl can inform the caller
* of curl_easy_ws_recv() that this is the end of
* the frame on the wire in case that is important
* to an extension. */
CURL_WS_FIN (1 << 8)
CURL_WS_RSV1 (1 << 7)
CURL_WS_RSV2 (1 << 6)
CURL_WS_RSV3 (1 << 5)
/* Opcodes */
CURL_WS_OPCODE_0 0
CURL_WS_CONTINUATION 0
CURL_WS_OPCODE_1 1
CURL_WS_TEXT 1
CURL_WS_OPCODE_2 2
CURL_WS_BINARY 2
CURL_WS_OPCODE_3 3
CURL_WS_OPCODE_4 4
CURL_WS_OPCODE_5 5
CURL_WS_OPCODE_6 6
CURL_WS_OPCODE_7 7
CURL_WS_OPCODE_8 8
CURL_WS_CLOSE 8
CURL_WS_OPCODE_9 9
CURL_WS_PING 9
CURL_WS_OPCODE_A 10
CURL_WS_PONG 10
CURL_WS_OPCODE_B 11
CURL_WS_OPCODE_C 12
CURL_WS_OPCODE_D 13
CURL_WS_OPCODE_E 14
CURL_WS_OPCODE_F 15
CURLcode curl_easy_ws_send(CURL *curl, int flags, const void *data, size_t len);
CURLcode curl_easy_ws_recv(CURL *curl, int *flags, void *data, size_t
buflen, size_t *n);
/* Send a binary stream */
curl_easy_ws_send(easy, CURL_WS_BINARY, data, len);
curl_easy_ws_send(easy, CURL_WS_CONTINUATION, data, len);
curl_easy_ws_send(easy, CURL_WS_FIN | CURL_WS_CONTINUATION, data, len);
/* Send a close */
curl_easy_ws_send(easy, CURL_WS_FIN | CURL_WS_CLOSE, data, len);
/* Optional way libcurl could support for OPCODEs 8+ & libcurl asserts
the frame is CURL_WS_FIN)
curl_easy_ws_send(easy, CURL_WS_CLOSE, data, len);
/* Send a ping */
curl_easy_ws_send(easy, CURL_WS_FIN | CURL_WS_PING, data, len);
/* Receiving a binary frame with a buffer that is too small */
curl_easy_ws_recv(easy, &flags, data, buflen, &n);
returns: CURLE_AGAIN
flags: CURL_WS_BINARY
curl_easy_ws_recv(easy, &flags, data, buflen, &n);
returns: CURLE_AGAIN
flags: CURL_WS_FRAME_END | CURL_WS_BINARY
... the frame is complete now
libcurl should be able to do a bit of state checking in case the
caller makes a mistake, but it should only focus on the fragmentation
continuity and validating that a control message is not too long. I
tend to think that libcurl should try to stay out of the business of
validating any of the payload formats because the rules for processing
are tied to frame type & extensions.
This extension concerns me as it renders the
curl_easy_ws_send()/recv() to not have value ... but as long as it
could be implemented using curl_easy_send()/recv() then I'm happy.
https://datatracker.ietf.org/doc/html/draft-ietf-hybi-websocket-multiplexing-11#section-8
tl;dr - they add a few bytes in front of the standard defined WS packet.
On Fri, Jul 2, 2021 at 6:42 AM Felipe Gasper <felipe_at_felipegasper.com> wrote:
>
>
>
> > On Jul 2, 2021, at 7:40 AM, Daniel Stenberg <daniel_at_haxx.se> wrote:
> >
> > On Fri, 2 Jul 2021, Felipe Gasper wrote:
> >
> > Thanks!
> >
> >> The send interface could look much like curl_easy_send():
> >>
> >> curl_ws_send( easy, buffer, buflen, &sent, enum curl_ws_message_type type, enum curl_ws_flags flags )
> >
> > Or perhaps with the type and flags just made as a single bitmask.
>
> This will make it impossible to specify a continuation frame rather than a data frame. (Context: when a message is fragmented, the message’s first frame is text/binary, and the latter ones are continuation.)
>
> That may be OK if curl will commit to abstracting that distinction away.
>
> Also, if WS were to add a 3rd data type -- e.g., via some extension -- then it would be possible to specify CURL_WS_TEXT | CURL_WS_FANCY_NEW_EXT_TYPE, which means curl would need to detect that.
>
> Alternatively, have separate curl_ws_send_text and curl_ws_send_binary functions?
>
> >
> >> Most WS libraries that I’ve seen implement reception as a callback, with a separate control to pause reception of incoming messages.
> >
> > I won't say that's wrong, but I don't see how a callback helps with this API.
> >
> > curl_ws_recv() could just be made to not return until the entire packet can be returned. Or with an option to return a partial one.
>
> I assume you mean something like EAGAIN rather than actually blocking?
>
> Having the caller call curl_ws_recv() directly would mean that that same caller would need to know when the socket has data to read. I don’t know if that’s always a safe assumption.
>
> Also, WS includes ping/pong, so even if the socket has data there might not be any application-level data incoming. (Ping/pong isn’t usually exposed to the application.)
>
> > An area for consideration is how to deal with the buffer (size). I mean, if it wants to wait for a full packet to arrive I suppose it can be large and it might not fit in the user-provided buffer, so the application would need to call it multiple times do drain the data.
>
> Would this argue in favour of a callback, then? Curl could just allocate as it knows it needs. The caller, then, could either memcpy or assume ownership of the buffer (by, e.g., returning CURL_WS_NOFREE from the callback).
>
> Alternatively, there is precedent here with SOCK_SEQPACKET sockets, though curl has the ability to deliver what IMO would be a smoother interface.
>
> >
> > I took the liberty of jotting down some of these API thoughts in the wiki page. Still incomplete and not really functional, but I figured it could help to stir up our collective minds..
> >
> > https://github.com/curl/curl/wiki/WebSockets#api
>
> I feel funny asking this, but is there a comment feature in this wiki system? I don’t see such, but it would seem useful.
>
> -FG
-------------------------------------------------------------------
Unsubscribe: https://cool.haxx.se/list/listinfo/curl-library
Etiquette: https://curl.se/mail/etiquette.html
Received on 2021-07-02