123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757 |
- /***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
- #include "curl_setup.h"
- #include <curl/curl.h>
- #ifdef USE_WEBSOCKETS
- #include "urldata.h"
- #include "dynbuf.h"
- #include "rand.h"
- #include "curl_base64.h"
- #include "sendf.h"
- #include "multiif.h"
- #include "ws.h"
- #include "easyif.h"
- #include "transfer.h"
- #include "nonblock.h"
- /* The last 3 #include files should be in this order */
- #include "curl_printf.h"
- #include "curl_memory.h"
- #include "memdebug.h"
- struct wsfield {
- const char *name;
- const char *val;
- };
- CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req)
- {
- unsigned int i;
- CURLcode result = CURLE_OK;
- unsigned char rand[16];
- char *randstr;
- size_t randlen;
- char keyval[40];
- struct SingleRequest *k = &data->req;
- struct wsfield heads[]= {
- {
- /* The request MUST contain an |Upgrade| header field whose value
- MUST include the "websocket" keyword. */
- "Upgrade:", "websocket"
- },
- {
- /* The request MUST contain a |Connection| header field whose value
- MUST include the "Upgrade" token. */
- "Connection:", "Upgrade",
- },
- {
- /* The request MUST include a header field with the name
- |Sec-WebSocket-Version|. The value of this header field MUST be
- 13. */
- "Sec-WebSocket-Version:", "13",
- },
- {
- /* The request MUST include a header field with the name
- |Sec-WebSocket-Key|. The value of this header field MUST be a nonce
- consisting of a randomly selected 16-byte value that has been
- base64-encoded (see Section 4 of [RFC4648]). The nonce MUST be
- selected randomly for each connection. */
- "Sec-WebSocket-Key:", NULL,
- }
- };
- heads[3].val = &keyval[0];
- /* 16 bytes random */
- result = Curl_rand(data, (unsigned char *)rand, sizeof(rand));
- if(result)
- return result;
- result = Curl_base64_encode((char *)rand, sizeof(rand), &randstr, &randlen);
- if(result)
- return result;
- DEBUGASSERT(randlen < sizeof(keyval));
- if(randlen >= sizeof(keyval))
- return CURLE_FAILED_INIT;
- strcpy(keyval, randstr);
- free(randstr);
- for(i = 0; !result && (i < sizeof(heads)/sizeof(heads[0])); i++) {
- if(!Curl_checkheaders(data, STRCONST(heads[i].name))) {
- #ifdef USE_HYPER
- char field[128];
- msnprintf(field, sizeof(field), "%s %s", heads[i].name,
- heads[i].val);
- result = Curl_hyper_header(data, req, field);
- #else
- (void)data;
- result = Curl_dyn_addf(req, "%s %s\r\n", heads[i].name,
- heads[i].val);
- #endif
- }
- }
- k->upgr101 = UPGR101_WS;
- Curl_dyn_init(&data->req.p.http->ws.buf, MAX_WS_SIZE * 2);
- return result;
- }
- CURLcode Curl_ws_accept(struct Curl_easy *data)
- {
- struct SingleRequest *k = &data->req;
- struct HTTP *ws = data->req.p.http;
- struct connectdata *conn = data->conn;
- struct websocket *wsp = &data->req.p.http->ws;
- CURLcode result;
- /* Verify the Sec-WebSocket-Accept response.
- The sent value is the base64 encoded version of a SHA-1 hash done on the
- |Sec-WebSocket-Key| header field concatenated with
- the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".
- */
- /* If the response includes a |Sec-WebSocket-Extensions| header field and
- this header field indicates the use of an extension that was not present
- in the client's handshake (the server has indicated an extension not
- requested by the client), the client MUST Fail the WebSocket Connection.
- */
- /* If the response includes a |Sec-WebSocket-Protocol| header field
- and this header field indicates the use of a subprotocol that was
- not present in the client's handshake (the server has indicated a
- subprotocol not requested by the client), the client MUST Fail
- the WebSocket Connection. */
- /* 4 bytes random */
- result = Curl_rand(data, (unsigned char *)&ws->ws.mask, sizeof(ws->ws.mask));
- if(result)
- return result;
- infof(data, "Received 101, switch to WebSocket; mask %02x%02x%02x%02x",
- ws->ws.mask[0], ws->ws.mask[1], ws->ws.mask[2], ws->ws.mask[3]);
- k->upgr101 = UPGR101_RECEIVED;
- if(data->set.connect_only)
- /* switch off non-blocking sockets */
- (void)curlx_nonblock(conn->sock[FIRSTSOCKET], FALSE);
- wsp->oleft = 0;
- return result;
- }
- #define WSBIT_FIN 0x80
- #define WSBIT_OPCODE_CONT 0
- #define WSBIT_OPCODE_TEXT (1)
- #define WSBIT_OPCODE_BIN (2)
- #define WSBIT_OPCODE_CLOSE (8)
- #define WSBIT_OPCODE_PING (9)
- #define WSBIT_OPCODE_PONG (0xa)
- #define WSBIT_OPCODE_MASK (0xf)
- #define WSBIT_MASK 0x80
- /* remove the spent bytes from the beginning of the buffer as that part has
- now been delivered to the application */
- static void ws_decode_clear(struct Curl_easy *data)
- {
- struct websocket *wsp = &data->req.p.http->ws;
- size_t spent = wsp->usedbuf;
- size_t len = Curl_dyn_len(&wsp->buf);
- size_t keep = len - spent;
- DEBUGASSERT(len >= spent);
- Curl_dyn_tail(&wsp->buf, keep);
- }
- /* ws_decode() decodes a binary frame into structured WebSocket data,
- wpkt - the incoming raw data. If NULL, work on the already buffered data.
- ilen - the size of the provided data, perhaps too little, perhaps too much
- out - stored pointed to extracted data
- olen - stored length of the extracted data
- oleft - number of unread bytes pending to that belongs to this frame
- more - if there is more data in there
- flags - stored bitmask about the frame
- Returns CURLE_AGAIN if there is only a partial frame in the buffer. Then it
- stores the first part in the ->extra buffer to be used in the next call
- when more data is provided.
- */
- static CURLcode ws_decode(struct Curl_easy *data,
- unsigned char *wpkt, size_t ilen,
- unsigned char **out, size_t *olen,
- curl_off_t *oleft,
- bool *more,
- unsigned int *flags)
- {
- bool fin;
- unsigned char opcode;
- curl_off_t total;
- size_t dataindex = 2;
- curl_off_t plen; /* size of data in the buffer */
- curl_off_t payloadsize;
- struct websocket *wsp = &data->req.p.http->ws;
- unsigned char *p;
- CURLcode result;
- *olen = 0;
- /* add the incoming bytes, if any */
- if(wpkt) {
- result = Curl_dyn_addn(&wsp->buf, wpkt, ilen);
- if(result)
- return result;
- }
- plen = Curl_dyn_len(&wsp->buf);
- if(plen < 2) {
- /* the smallest possible frame is two bytes */
- infof(data, "WS: plen == %u, EAGAIN", (int)plen);
- return CURLE_AGAIN;
- }
- p = Curl_dyn_uptr(&wsp->buf);
- fin = p[0] & WSBIT_FIN;
- opcode = p[0] & WSBIT_OPCODE_MASK;
- infof(data, "WS:%d received FIN bit %u", __LINE__, (int)fin);
- *flags = 0;
- switch(opcode) {
- case WSBIT_OPCODE_CONT:
- if(!fin)
- *flags |= CURLWS_CONT;
- infof(data, "WS: received OPCODE CONT");
- break;
- case WSBIT_OPCODE_TEXT:
- infof(data, "WS: received OPCODE TEXT");
- *flags |= CURLWS_TEXT;
- break;
- case WSBIT_OPCODE_BIN:
- infof(data, "WS: received OPCODE BINARY");
- *flags |= CURLWS_BINARY;
- break;
- case WSBIT_OPCODE_CLOSE:
- infof(data, "WS: received OPCODE CLOSE");
- *flags |= CURLWS_CLOSE;
- break;
- case WSBIT_OPCODE_PING:
- infof(data, "WS: received OPCODE PING");
- *flags |= CURLWS_PING;
- break;
- case WSBIT_OPCODE_PONG:
- infof(data, "WS: received OPCODE PONG");
- *flags |= CURLWS_PONG;
- break;
- }
- if(p[1] & WSBIT_MASK) {
- /* A client MUST close a connection if it detects a masked frame. */
- failf(data, "WS: masked input frame");
- return CURLE_RECV_ERROR;
- }
- payloadsize = p[1];
- if(payloadsize == 126) {
- if(plen < 4) {
- infof(data, "WS:%d plen == %u, EAGAIN", __LINE__, (int)plen);
- return CURLE_AGAIN; /* not enough data available */
- }
- payloadsize = (p[2] << 8) | p[3];
- dataindex += 2;
- }
- else if(payloadsize == 127) {
- /* 64 bit payload size */
- if(plen < 10)
- return CURLE_AGAIN;
- if(p[2] & 80) {
- failf(data, "WS: too large frame");
- return CURLE_RECV_ERROR;
- }
- dataindex += 8;
- payloadsize = ((curl_off_t)p[2] << 56) |
- (curl_off_t)p[3] << 48 |
- (curl_off_t)p[4] << 40 |
- (curl_off_t)p[5] << 32 |
- (curl_off_t)p[6] << 24 |
- (curl_off_t)p[7] << 16 |
- (curl_off_t)p[8] << 8 |
- p[9];
- }
- total = dataindex + payloadsize;
- if(total > plen) {
- /* deliver a partial frame */
- *oleft = total - dataindex;
- payloadsize = total - dataindex;
- }
- else {
- *oleft = 0;
- if(plen > total)
- /* there is another fragment after */
- *more = TRUE;
- }
- /* point to the payload */
- *out = &p[dataindex];
- /* return the payload length */
- *olen = payloadsize;
- /* number of bytes "used" from the buffer */
- wsp->usedbuf = dataindex + payloadsize;
- infof(data, "WS: received %zu bytes payload (%zu left)",
- payloadsize, *oleft);
- return CURLE_OK;
- }
- /* Curl_ws_writecb() is the write callback for websocket traffic. The
- websocket data is provided to this raw, in chunks. This function should
- handle/decode the data and call the "real" underlying callback accordingly.
- */
- size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */,
- size_t nitems, void *userp)
- {
- struct HTTP *ws = (struct HTTP *)userp;
- struct Curl_easy *data = ws->ws.data;
- void *writebody_ptr = data->set.out;
- if(data->set.ws_raw_mode)
- return data->set.fwrite_func(buffer, size, nitems, writebody_ptr);
- else if(nitems) {
- unsigned char *frame = NULL;
- size_t flen = 0;
- size_t wrote = 0;
- CURLcode result;
- bool more; /* there's is more to parse in the buffer */
- curl_off_t oleft;
- decode:
- more = FALSE;
- oleft = ws->ws.frame.bytesleft;
- if(!oleft) {
- unsigned int recvflags;
- result = ws_decode(data, (unsigned char *)buffer, nitems,
- &frame, &flen, &oleft, &more, &recvflags);
- if(result == CURLE_AGAIN)
- /* insufficient amount of data, keep it for later */
- return nitems;
- else if(result) {
- infof(data, "WS: decode error %d", (int)result);
- return nitems - 1;
- }
- /* Store details about the frame to be reachable with curl_ws_meta()
- from within the write callback */
- ws->ws.frame.age = 0;
- ws->ws.frame.offset = 0;
- ws->ws.frame.flags = recvflags;
- ws->ws.frame.bytesleft = oleft;
- }
- else {
- if(nitems > (size_t)ws->ws.frame.bytesleft) {
- nitems = ws->ws.frame.bytesleft;
- more = TRUE;
- }
- else
- more = FALSE;
- ws->ws.frame.offset += nitems;
- ws->ws.frame.bytesleft -= nitems;
- frame = (unsigned char *)buffer;
- flen = nitems;
- }
- if((ws->ws.frame.flags & CURLWS_PING) && !oleft) {
- /* auto-respond to PINGs, only works for single-frame payloads atm */
- size_t bytes;
- infof(data, "WS: auto-respond to PING with a PONG");
- DEBUGASSERT(frame);
- /* send back the exact same content as a PONG */
- result = curl_ws_send(data, frame, flen, &bytes, 0, CURLWS_PONG);
- if(result)
- return result;
- }
- else {
- /* deliver the decoded frame to the user callback */
- Curl_set_in_callback(data, true);
- wrote = data->set.fwrite_func((char *)frame, 1, flen, writebody_ptr);
- Curl_set_in_callback(data, false);
- if(wrote != flen)
- return 0;
- }
- if(oleft)
- ws->ws.frame.offset += flen;
- /* the websocket frame has been delivered */
- ws_decode_clear(data);
- if(more) {
- /* there's more websocket data to deal with in the buffer */
- buffer = NULL; /* the buffer as been drained already */
- goto decode;
- }
- }
- return nitems;
- }
- CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
- size_t buflen, size_t *nread,
- struct curl_ws_frame **metap)
- {
- size_t bytes;
- CURLcode result;
- struct websocket *wsp = &data->req.p.http->ws;
- *nread = 0;
- *metap = NULL;
- /* get a download buffer */
- result = Curl_preconnect(data);
- if(result)
- return result;
- do {
- bool drain = FALSE; /* if there is pending buffered data to drain */
- char *inbuf = data->state.buffer;
- bytes = wsp->stillbuffer;
- if(!bytes) {
- result = curl_easy_recv(data, data->state.buffer,
- data->set.buffer_size, &bytes);
- if(result)
- return result;
- }
- else {
- /* the pending bytes can be found here */
- inbuf = wsp->stillb;
- drain = TRUE;
- }
- if(bytes) {
- unsigned char *out;
- size_t olen;
- bool more;
- unsigned int recvflags;
- curl_off_t oleft = wsp->frame.bytesleft;
- infof(data, "WS: got %u websocket bytes to decode", (int)bytes);
- if(!oleft && !drain) {
- result = ws_decode(data, (unsigned char *)inbuf, bytes,
- &out, &olen, &oleft, &more, &recvflags);
- if(result == CURLE_AGAIN)
- /* a packet fragment only */
- break;
- else if(result)
- return result;
- wsp->frame.offset = 0;
- wsp->frame.bytesleft = oleft;
- wsp->frame.flags = recvflags;
- }
- else {
- olen = oleft;
- out = (unsigned char *)wsp->stillb;
- recvflags = wsp->frame.flags;
- if((curl_off_t)buflen < oleft)
- /* there is still data left after this */
- wsp->frame.bytesleft -= buflen;
- else
- wsp->frame.bytesleft = 0;
- }
- /* auto-respond to PINGs */
- if((recvflags & CURLWS_PING) && !oleft) {
- infof(data, "WS: auto-respond to PING with a PONG");
- /* send back the exact same content as a PONG */
- result = curl_ws_send(data, out, olen, &bytes, 0, CURLWS_PONG);
- if(result)
- return result;
- }
- else {
- if(olen < buflen) {
- /* copy the payload to the user buffer */
- memcpy(buffer, out, olen);
- *nread = olen;
- if(!oleft)
- /* websocket frame has been delivered */
- ws_decode_clear(data);
- }
- else {
- /* copy a partial payload */
- memcpy(buffer, out, buflen);
- *nread = buflen;
- /* remember what is left and where */
- wsp->stillbuffer = olen - buflen;
- wsp->stillb = (char *)buffer + buflen;
- }
- wsp->frame.offset += *nread;
- }
- }
- else
- *nread = bytes;
- break;
- } while(1);
- *metap = &wsp->frame;
- return CURLE_OK;
- }
- static void ws_xor(struct Curl_easy *data,
- const unsigned char *source,
- unsigned char *dest,
- size_t len)
- {
- struct websocket *wsp = &data->req.p.http->ws;
- size_t i;
- /* append payload after the mask, XOR appropriately */
- for(i = 0; i < len; i++) {
- dest[i] = source[i] ^ wsp->mask[wsp->xori];
- wsp->xori++;
- wsp->xori &= 3;
- }
- }
- /***
- RFC 6455 Section 5.2
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-------+-+-------------+-------------------------------+
- |F|R|R|R| opcode|M| Payload len | Extended payload length |
- |I|S|S|S| (4) |A| (7) | (16/64) |
- |N|V|V|V| |S| | (if payload len==126/127) |
- | |1|2|3| |K| | |
- +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
- | Extended payload length continued, if payload len == 127 |
- + - - - - - - - - - - - - - - - +-------------------------------+
- | |Masking-key, if MASK set to 1 |
- +-------------------------------+-------------------------------+
- | Masking-key (continued) | Payload Data |
- +-------------------------------- - - - - - - - - - - - - - - - +
- : Payload Data continued ... :
- + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
- | Payload Data continued ... |
- +---------------------------------------------------------------+
- */
- static size_t ws_packethead(struct Curl_easy *data,
- size_t len, unsigned int flags)
- {
- struct HTTP *ws = data->req.p.http;
- unsigned char *out = (unsigned char *)data->state.ulbuf;
- unsigned char firstbyte = 0;
- int outi;
- unsigned char opcode;
- if(flags & CURLWS_TEXT) {
- opcode = WSBIT_OPCODE_TEXT;
- infof(data, "WS: send OPCODE TEXT");
- }
- else if(flags & CURLWS_CLOSE) {
- opcode = WSBIT_OPCODE_CLOSE;
- infof(data, "WS: send OPCODE CLOSE");
- }
- else if(flags & CURLWS_PING) {
- opcode = WSBIT_OPCODE_PING;
- infof(data, "WS: send OPCODE PING");
- }
- else if(flags & CURLWS_PONG) {
- opcode = WSBIT_OPCODE_PONG;
- infof(data, "WS: send OPCODE PONG");
- }
- else {
- opcode = WSBIT_OPCODE_BIN;
- infof(data, "WS: send OPCODE BINARY");
- }
- if(!(flags & CURLWS_CONT)) {
- /* if not marked as continuing, assume this is the final fragment */
- firstbyte |= WSBIT_FIN | opcode;
- ws->ws.contfragment = FALSE;
- }
- else if(ws->ws.contfragment) {
- /* the previous fragment was not a final one and this isn't either, keep a
- CONT opcode and no FIN bit */
- firstbyte |= WSBIT_OPCODE_CONT;
- }
- else {
- ws->ws.contfragment = TRUE;
- }
- out[0] = firstbyte;
- if(len > 65535) {
- out[1] = 127 | WSBIT_MASK;
- out[2] = (len >> 8) & 0xff;
- out[3] = len & 0xff;
- outi = 10;
- }
- else if(len > 126) {
- out[1] = 126 | WSBIT_MASK;
- out[2] = (len >> 8) & 0xff;
- out[3] = len & 0xff;
- outi = 4;
- }
- else {
- out[1] = (unsigned char)len | WSBIT_MASK;
- outi = 2;
- }
- infof(data, "WS: send FIN bit %u (byte %02x)",
- firstbyte & WSBIT_FIN ? 1 : 0,
- firstbyte);
- infof(data, "WS: send payload len %u", (int)len);
- /* 4 bytes mask */
- memcpy(&out[outi], &ws->ws.mask, 4);
- if(data->set.upload_buffer_size < (len + 10))
- return 0;
- /* pass over the mask */
- outi += 4;
- ws->ws.xori = 0;
- /* return packet size */
- return outi;
- }
- CURL_EXTERN CURLcode curl_ws_send(struct Curl_easy *data, const void *buffer,
- size_t buflen, size_t *sent,
- curl_off_t totalsize,
- unsigned int sendflags)
- {
- CURLcode result;
- size_t headlen;
- char *out;
- ssize_t written;
- struct websocket *wsp = &data->req.p.http->ws;
- if(!data->set.ws_raw_mode) {
- result = Curl_get_upload_buffer(data);
- if(result)
- return result;
- }
- else {
- if(totalsize || sendflags)
- return CURLE_BAD_FUNCTION_ARGUMENT;
- }
- if(data->set.ws_raw_mode) {
- if(!buflen)
- /* nothing to do */
- return CURLE_OK;
- /* raw mode sends exactly what was requested, and this is from within
- the write callback */
- if(Curl_is_in_callback(data))
- result = Curl_write(data, data->conn->writesockfd, buffer, buflen,
- &written);
- else
- result = Curl_senddata(data, buffer, buflen, &written);
- infof(data, "WS: wanted to send %zu bytes, sent %zu bytes",
- buflen, written);
- *sent = written;
- return result;
- }
- if(buflen > (data->set.upload_buffer_size - 10))
- /* don't do more than this in one go */
- buflen = data->set.upload_buffer_size - 10;
- if(sendflags & CURLWS_OFFSET) {
- if(totalsize) {
- /* a frame series 'totalsize' bytes big, this is the first */
- headlen = ws_packethead(data, totalsize, sendflags);
- wsp->sleft = totalsize - buflen;
- }
- else {
- headlen = 0;
- if((curl_off_t)buflen > wsp->sleft) {
- infof(data, "WS: unaligned frame size (sending %zu instead of %zu)",
- buflen, wsp->sleft);
- wsp->sleft = 0;
- }
- else
- wsp->sleft -= buflen;
- }
- }
- else
- headlen = ws_packethead(data, buflen, sendflags);
- /* headlen is the size of the frame header */
- out = data->state.ulbuf;
- if(buflen)
- /* for PING and PONG etc there might not be a payload */
- ws_xor(data, buffer, (unsigned char *)out + headlen, buflen);
- if(data->set.connect_only)
- result = Curl_senddata(data, out, buflen + headlen, &written);
- else
- result = Curl_write(data, data->conn->writesockfd, out,
- buflen + headlen, &written);
- infof(data, "WS: wanted to send %zu bytes, sent %zu bytes",
- headlen + buflen, written);
- *sent = written;
- return result;
- }
- void Curl_ws_done(struct Curl_easy *data)
- {
- struct websocket *wsp = &data->req.p.http->ws;
- DEBUGASSERT(wsp);
- Curl_dyn_free(&wsp->buf);
- }
- CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
- {
- /* we only return something for websocket, called from within the callback
- when not using raw mode */
- if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) && data->req.p.http &&
- !data->set.ws_raw_mode)
- return &data->req.p.http->ws.frame;
- return NULL;
- }
- #else
- CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen,
- size_t *nread,
- struct curl_ws_frame **metap)
- {
- (void)curl;
- (void)buffer;
- (void)buflen;
- (void)nread;
- (void)metap;
- return CURLE_OK;
- }
- CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer,
- size_t buflen, size_t *sent,
- curl_off_t framesize,
- unsigned int sendflags)
- {
- (void)curl;
- (void)buffer;
- (void)buflen;
- (void)sent;
- (void)framesize;
- (void)sendflags;
- return CURLE_OK;
- }
- CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
- {
- (void)data;
- return NULL;
- }
- #endif /* USE_WEBSOCKETS */
|