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 <fel...@felipegasper.com> wrote: > > > > > On Jul 2, 2021, at 7:40 AM, Daniel Stenberg <dan...@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