VirtualBox

source: vbox/trunk/src/libs/curl-8.0.1/lib/ws.c@ 99885

Last change on this file since 99885 was 99344, checked in by vboxsync, 2 years ago

curl-8.0.1: Applied and adjusted our curl changes to 7.87.0 bugref:10417

  • Property svn:eol-style set to native
File size: 24.0 KB
Line 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24#include "curl_setup.h"
25#include <curl/curl.h>
26
27#ifdef USE_WEBSOCKETS
28
29#include "urldata.h"
30#include "dynbuf.h"
31#include "rand.h"
32#include "curl_base64.h"
33#include "sendf.h"
34#include "multiif.h"
35#include "ws.h"
36#include "easyif.h"
37#include "transfer.h"
38#include "nonblock.h"
39
40/* The last 3 #include files should be in this order */
41#include "curl_printf.h"
42#include "curl_memory.h"
43#include "memdebug.h"
44
45struct wsfield {
46 const char *name;
47 const char *val;
48};
49
50CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req)
51{
52 unsigned int i;
53 CURLcode result = CURLE_OK;
54 unsigned char rand[16];
55 char *randstr;
56 size_t randlen;
57 char keyval[40];
58 struct SingleRequest *k = &data->req;
59 struct wsfield heads[]= {
60 {
61 /* The request MUST contain an |Upgrade| header field whose value
62 MUST include the "websocket" keyword. */
63 "Upgrade:", "websocket"
64 },
65 {
66 /* The request MUST contain a |Connection| header field whose value
67 MUST include the "Upgrade" token. */
68 "Connection:", "Upgrade",
69 },
70 {
71 /* The request MUST include a header field with the name
72 |Sec-WebSocket-Version|. The value of this header field MUST be
73 13. */
74 "Sec-WebSocket-Version:", "13",
75 },
76 {
77 /* The request MUST include a header field with the name
78 |Sec-WebSocket-Key|. The value of this header field MUST be a nonce
79 consisting of a randomly selected 16-byte value that has been
80 base64-encoded (see Section 4 of [RFC4648]). The nonce MUST be
81 selected randomly for each connection. */
82 "Sec-WebSocket-Key:", NULL,
83 }
84 };
85 heads[3].val = &keyval[0];
86
87 /* 16 bytes random */
88 result = Curl_rand(data, (unsigned char *)rand, sizeof(rand));
89 if(result)
90 return result;
91 result = Curl_base64_encode((char *)rand, sizeof(rand), &randstr, &randlen);
92 if(result)
93 return result;
94 DEBUGASSERT(randlen < sizeof(keyval));
95 if(randlen >= sizeof(keyval))
96 return CURLE_FAILED_INIT;
97 strcpy(keyval, randstr);
98 free(randstr);
99 for(i = 0; !result && (i < sizeof(heads)/sizeof(heads[0])); i++) {
100 if(!Curl_checkheaders(data, STRCONST(heads[i].name))) {
101#ifdef USE_HYPER
102 char field[128];
103 msnprintf(field, sizeof(field), "%s %s", heads[i].name,
104 heads[i].val);
105 result = Curl_hyper_header(data, req, field);
106#else
107 (void)data;
108 result = Curl_dyn_addf(req, "%s %s\r\n", heads[i].name,
109 heads[i].val);
110#endif
111 }
112 }
113 k->upgr101 = UPGR101_WS;
114 Curl_dyn_init(&data->req.p.http->ws.buf, MAX_WS_SIZE * 2);
115 return result;
116}
117
118/*
119 * 'nread' is number of bytes of websocket data already in the buffer at
120 * 'mem'.
121 */
122CURLcode Curl_ws_accept(struct Curl_easy *data,
123 const char *mem, size_t nread)
124{
125 struct SingleRequest *k = &data->req;
126 struct HTTP *ws = data->req.p.http;
127 struct connectdata *conn = data->conn;
128 struct websocket *wsp = &data->req.p.http->ws;
129 struct ws_conn *wsc = &conn->proto.ws;
130 CURLcode result;
131
132 /* Verify the Sec-WebSocket-Accept response.
133
134 The sent value is the base64 encoded version of a SHA-1 hash done on the
135 |Sec-WebSocket-Key| header field concatenated with
136 the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11".
137 */
138
139 /* If the response includes a |Sec-WebSocket-Extensions| header field and
140 this header field indicates the use of an extension that was not present
141 in the client's handshake (the server has indicated an extension not
142 requested by the client), the client MUST Fail the WebSocket Connection.
143 */
144
145 /* If the response includes a |Sec-WebSocket-Protocol| header field
146 and this header field indicates the use of a subprotocol that was
147 not present in the client's handshake (the server has indicated a
148 subprotocol not requested by the client), the client MUST Fail
149 the WebSocket Connection. */
150
151 /* 4 bytes random */
152 result = Curl_rand(data, (unsigned char *)&ws->ws.mask, sizeof(ws->ws.mask));
153 if(result)
154 return result;
155
156 infof(data, "Received 101, switch to WebSocket; mask %02x%02x%02x%02x",
157 ws->ws.mask[0], ws->ws.mask[1], ws->ws.mask[2], ws->ws.mask[3]);
158 Curl_dyn_init(&wsc->early, data->set.buffer_size);
159 if(nread) {
160 result = Curl_dyn_addn(&wsc->early, mem, nread);
161 if(result)
162 return result;
163 infof(data, "%zu bytes websocket payload", nread);
164 wsp->stillb = Curl_dyn_ptr(&wsc->early);
165 wsp->stillblen = Curl_dyn_len(&wsc->early);
166 }
167 k->upgr101 = UPGR101_RECEIVED;
168
169 return result;
170}
171
172#define WSBIT_FIN 0x80
173#define WSBIT_OPCODE_CONT 0
174#define WSBIT_OPCODE_TEXT (1)
175#define WSBIT_OPCODE_BIN (2)
176#define WSBIT_OPCODE_CLOSE (8)
177#define WSBIT_OPCODE_PING (9)
178#define WSBIT_OPCODE_PONG (0xa)
179#define WSBIT_OPCODE_MASK (0xf)
180
181#define WSBIT_MASK 0x80
182
183/* remove the spent bytes from the beginning of the buffer as that part has
184 now been delivered to the application */
185static void ws_decode_shift(struct Curl_easy *data, size_t spent)
186{
187 struct websocket *wsp = &data->req.p.http->ws;
188 size_t len = Curl_dyn_len(&wsp->buf);
189 size_t keep = len - spent;
190 DEBUGASSERT(len >= spent);
191 Curl_dyn_tail(&wsp->buf, keep);
192}
193
194/* ws_decode() decodes a binary frame into structured WebSocket data,
195
196 data - the transfer
197 inbuf - incoming raw data. If NULL, work on the already buffered data.
198 inlen - size of the provided data, perhaps too little, perhaps too much
199 headlen - stored length of the frame header
200 olen - stored length of the extracted data
201 oleft - number of unread bytes pending to that belongs to this frame
202 flags - stored bitmask about the frame
203
204 Returns CURLE_AGAIN if there is only a partial frame in the buffer. Then it
205 stores the first part in the ->extra buffer to be used in the next call
206 when more data is provided.
207*/
208
209static CURLcode ws_decode(struct Curl_easy *data,
210 unsigned char *inbuf, size_t inlen,
211 size_t *headlen, size_t *olen,
212 curl_off_t *oleft,
213 unsigned int *flags)
214{
215 bool fin;
216 unsigned char opcode;
217 curl_off_t total;
218 size_t dataindex = 2;
219 curl_off_t payloadsize;
220
221 *olen = *headlen = 0;
222
223 if(inlen < 2) {
224 /* the smallest possible frame is two bytes */
225 infof(data, "WS: plen == %u, EAGAIN", (int)inlen);
226 return CURLE_AGAIN;
227 }
228
229 fin = inbuf[0] & WSBIT_FIN;
230 opcode = inbuf[0] & WSBIT_OPCODE_MASK;
231 infof(data, "WS:%d received FIN bit %u", __LINE__, (int)fin);
232 *flags = 0;
233 switch(opcode) {
234 case WSBIT_OPCODE_CONT:
235 if(!fin)
236 *flags |= CURLWS_CONT;
237 infof(data, "WS: received OPCODE CONT");
238 break;
239 case WSBIT_OPCODE_TEXT:
240 infof(data, "WS: received OPCODE TEXT");
241 *flags |= CURLWS_TEXT;
242 break;
243 case WSBIT_OPCODE_BIN:
244 infof(data, "WS: received OPCODE BINARY");
245 *flags |= CURLWS_BINARY;
246 break;
247 case WSBIT_OPCODE_CLOSE:
248 infof(data, "WS: received OPCODE CLOSE");
249 *flags |= CURLWS_CLOSE;
250 break;
251 case WSBIT_OPCODE_PING:
252 infof(data, "WS: received OPCODE PING");
253 *flags |= CURLWS_PING;
254 break;
255 case WSBIT_OPCODE_PONG:
256 infof(data, "WS: received OPCODE PONG");
257 *flags |= CURLWS_PONG;
258 break;
259 default:
260 failf(data, "WS: unknown opcode: %x", opcode);
261 return CURLE_RECV_ERROR;
262 }
263
264 if(inbuf[1] & WSBIT_MASK) {
265 /* A client MUST close a connection if it detects a masked frame. */
266 failf(data, "WS: masked input frame");
267 return CURLE_RECV_ERROR;
268 }
269 payloadsize = inbuf[1];
270 if(payloadsize == 126) {
271 if(inlen < 4) {
272 infof(data, "WS:%d plen == %u, EAGAIN", __LINE__, (int)inlen);
273 return CURLE_AGAIN; /* not enough data available */
274 }
275 payloadsize = (inbuf[2] << 8) | inbuf[3];
276 dataindex += 2;
277 }
278 else if(payloadsize == 127) {
279 /* 64 bit payload size */
280 if(inlen < 10)
281 return CURLE_AGAIN;
282 if(inbuf[2] & 80) {
283 failf(data, "WS: too large frame");
284 return CURLE_RECV_ERROR;
285 }
286 dataindex += 8;
287 payloadsize = ((curl_off_t)inbuf[2] << 56) |
288 (curl_off_t)inbuf[3] << 48 |
289 (curl_off_t)inbuf[4] << 40 |
290 (curl_off_t)inbuf[5] << 32 |
291 (curl_off_t)inbuf[6] << 24 |
292 (curl_off_t)inbuf[7] << 16 |
293 (curl_off_t)inbuf[8] << 8 |
294 inbuf[9];
295 }
296
297 /* point to the payload */
298 *headlen = dataindex;
299 total = dataindex + payloadsize;
300 if(total > (curl_off_t)inlen) {
301 /* buffer contains partial frame */
302 *olen = inlen - dataindex; /* bytes to write out */
303 *oleft = total - inlen; /* bytes yet to come (for this frame) */
304 payloadsize = total - dataindex;
305 }
306 else {
307 /* we have the complete frame (`total` bytes) in buffer */
308 *olen = payloadsize; /* bytes to write out */
309 *oleft = 0; /* bytes yet to come (for this frame) */
310 }
311
312 infof(data, "WS: received %Ou bytes payload (%Ou left, buflen was %zu)",
313 payloadsize, *oleft, inlen);
314 return CURLE_OK;
315}
316
317/* Curl_ws_writecb() is the write callback for websocket traffic. The
318 websocket data is provided to this raw, in chunks. This function should
319 handle/decode the data and call the "real" underlying callback accordingly.
320*/
321size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */,
322 size_t nitems, void *userp)
323{
324 struct HTTP *ws = (struct HTTP *)userp;
325 struct Curl_easy *data = ws->ws.data;
326 struct websocket *wsp = &data->req.p.http->ws;
327 void *writebody_ptr = data->set.out;
328 if(data->set.ws_raw_mode)
329 return data->set.fwrite_func(buffer, size, nitems, writebody_ptr);
330 else if(nitems) {
331 size_t wrote = 0, headlen;
332 CURLcode result;
333
334 if(buffer) {
335 result = Curl_dyn_addn(&wsp->buf, buffer, nitems);
336 if(result) {
337 infof(data, "WS: error adding data to buffer %d", (int)result);
338 return nitems - 1;
339 }
340 buffer = NULL;
341 }
342
343 while(Curl_dyn_len(&wsp->buf)) {
344 unsigned char *wsbuf = Curl_dyn_uptr(&wsp->buf);
345 size_t buflen = Curl_dyn_len(&wsp->buf);
346 size_t write_len = 0;
347 size_t consumed = 0;
348
349 if(!ws->ws.frame.bytesleft) {
350 unsigned int recvflags;
351 curl_off_t fb_left;
352
353 result = ws_decode(data, wsbuf, buflen,
354 &headlen, &write_len, &fb_left, &recvflags);
355 if(result == CURLE_AGAIN)
356 /* insufficient amount of data, keep it for later.
357 * we pretend to have written all since we have a copy */
358 return nitems;
359 else if(result) {
360 infof(data, "WS: decode error %d", (int)result);
361 return nitems - 1;
362 }
363 consumed += headlen;
364 wsbuf += headlen;
365 buflen -= headlen;
366
367 /* New frame. store details about the frame to be reachable with
368 curl_ws_meta() from within the write callback */
369 ws->ws.frame.age = 0;
370 ws->ws.frame.offset = 0;
371 ws->ws.frame.flags = recvflags;
372 ws->ws.frame.bytesleft = fb_left;
373 }
374 else {
375 /* continuing frame */
376 write_len = (size_t)ws->ws.frame.bytesleft;
377 if(write_len > buflen)
378 write_len = buflen;
379 ws->ws.frame.offset += write_len;
380 ws->ws.frame.bytesleft -= write_len;
381 }
382 if((ws->ws.frame.flags & CURLWS_PING) && !ws->ws.frame.bytesleft) {
383 /* auto-respond to PINGs, only works for single-frame payloads atm */
384 size_t bytes;
385 infof(data, "WS: auto-respond to PING with a PONG");
386 /* send back the exact same content as a PONG */
387 result = curl_ws_send(data, wsbuf, write_len,
388 &bytes, 0, CURLWS_PONG);
389 if(result)
390 return result;
391 }
392 else if(write_len || !wsp->frame.bytesleft) {
393 /* deliver the decoded frame to the user callback */
394 Curl_set_in_callback(data, true);
395 wrote = data->set.fwrite_func((char *)wsbuf, 1,
396 write_len, writebody_ptr);
397 Curl_set_in_callback(data, false);
398 if(wrote != write_len)
399 return 0;
400 }
401 /* get rid of the buffered data consumed */
402 consumed += write_len;
403 ws_decode_shift(data, consumed);
404 }
405 }
406 return nitems;
407}
408
409CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
410 size_t buflen, size_t *nread,
411 struct curl_ws_frame **metap)
412{
413 CURLcode result;
414 struct websocket *wsp = &data->req.p.http->ws;
415 bool done = FALSE; /* not filled passed buffer yet */
416
417 *nread = 0;
418 *metap = NULL;
419 /* get a download buffer */
420 result = Curl_preconnect(data);
421 if(result)
422 return result;
423
424 while(!done) {
425 size_t datalen;
426 unsigned int recvflags;
427
428 if(!wsp->stillblen) {
429 /* try to get more data */
430 size_t n;
431 result = curl_easy_recv(data, data->state.buffer,
432 data->set.buffer_size, &n);
433 if(result)
434 return result;
435 if(!n) {
436 /* connection closed */
437 infof(data, "connection expectedly closed?");
438 return CURLE_GOT_NOTHING;
439 }
440 wsp->stillb = data->state.buffer;
441 wsp->stillblen = n;
442 }
443
444 infof(data, "WS: %u bytes left to decode", (int)wsp->stillblen);
445 if(!wsp->frame.bytesleft) {
446 size_t headlen;
447 curl_off_t oleft;
448 /* detect new frame */
449 result = ws_decode(data, (unsigned char *)wsp->stillb, wsp->stillblen,
450 &headlen, &datalen, &oleft, &recvflags);
451 if(result == CURLE_AGAIN)
452 /* a packet fragment only */
453 break;
454 else if(result)
455 return result;
456 if(datalen > buflen) {
457 size_t diff = datalen - buflen;
458 datalen = buflen;
459 oleft += diff;
460 }
461 wsp->stillb += headlen;
462 wsp->stillblen -= headlen;
463 wsp->frame.offset = 0;
464 wsp->frame.bytesleft = oleft;
465 wsp->frame.flags = recvflags;
466 }
467 else {
468 /* existing frame, remaining payload handling */
469 datalen = wsp->frame.bytesleft;
470 if(datalen > wsp->stillblen)
471 datalen = wsp->stillblen;
472 if(datalen > buflen)
473 datalen = buflen;
474
475 wsp->frame.offset += wsp->frame.len;
476 wsp->frame.bytesleft -= datalen;
477 }
478 wsp->frame.len = datalen;
479
480 /* auto-respond to PINGs */
481 if((wsp->frame.flags & CURLWS_PING) && !wsp->frame.bytesleft) {
482 size_t nsent = 0;
483 infof(data, "WS: auto-respond to PING with a PONG, %zu bytes payload",
484 datalen);
485 /* send back the exact same content as a PONG */
486 result = curl_ws_send(data, wsp->stillb, datalen, &nsent, 0,
487 CURLWS_PONG);
488 if(result)
489 return result;
490 infof(data, "WS: bytesleft %zu datalen %zu",
491 wsp->frame.bytesleft, datalen);
492 /* we handled the data part of the PING, advance over that */
493 wsp->stillb += nsent;
494 wsp->stillblen -= nsent;
495 }
496 else if(datalen) {
497 /* copy the payload to the user buffer */
498 memcpy(buffer, wsp->stillb, datalen);
499 *nread = datalen;
500 done = TRUE;
501
502 wsp->stillblen -= datalen;
503 if(wsp->stillblen)
504 wsp->stillb += datalen;
505 else {
506 wsp->stillb = NULL;
507 }
508 }
509 }
510 *metap = &wsp->frame;
511 return CURLE_OK;
512}
513
514static void ws_xor(struct Curl_easy *data,
515 const unsigned char *source,
516 unsigned char *dest,
517 size_t len)
518{
519 struct websocket *wsp = &data->req.p.http->ws;
520 size_t i;
521 /* append payload after the mask, XOR appropriately */
522 for(i = 0; i < len; i++) {
523 dest[i] = source[i] ^ wsp->mask[wsp->xori];
524 wsp->xori++;
525 wsp->xori &= 3;
526 }
527}
528
529/***
530 RFC 6455 Section 5.2
531
532 0 1 2 3
533 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
534 +-+-+-+-+-------+-+-------------+-------------------------------+
535 |F|R|R|R| opcode|M| Payload len | Extended payload length |
536 |I|S|S|S| (4) |A| (7) | (16/64) |
537 |N|V|V|V| |S| | (if payload len==126/127) |
538 | |1|2|3| |K| | |
539 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
540 | Extended payload length continued, if payload len == 127 |
541 + - - - - - - - - - - - - - - - +-------------------------------+
542 | |Masking-key, if MASK set to 1 |
543 +-------------------------------+-------------------------------+
544 | Masking-key (continued) | Payload Data |
545 +-------------------------------- - - - - - - - - - - - - - - - +
546 : Payload Data continued ... :
547 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
548 | Payload Data continued ... |
549 +---------------------------------------------------------------+
550*/
551
552static size_t ws_packethead(struct Curl_easy *data,
553 size_t len, unsigned int flags)
554{
555 struct HTTP *ws = data->req.p.http;
556 unsigned char *out = (unsigned char *)data->state.ulbuf;
557 unsigned char firstbyte = 0;
558 int outi;
559 unsigned char opcode;
560 if(flags & CURLWS_TEXT) {
561 opcode = WSBIT_OPCODE_TEXT;
562 infof(data, "WS: send OPCODE TEXT");
563 }
564 else if(flags & CURLWS_CLOSE) {
565 opcode = WSBIT_OPCODE_CLOSE;
566 infof(data, "WS: send OPCODE CLOSE");
567 }
568 else if(flags & CURLWS_PING) {
569 opcode = WSBIT_OPCODE_PING;
570 infof(data, "WS: send OPCODE PING");
571 }
572 else if(flags & CURLWS_PONG) {
573 opcode = WSBIT_OPCODE_PONG;
574 infof(data, "WS: send OPCODE PONG");
575 }
576 else {
577 opcode = WSBIT_OPCODE_BIN;
578 infof(data, "WS: send OPCODE BINARY");
579 }
580
581 if(!(flags & CURLWS_CONT)) {
582 if(!ws->ws.contfragment)
583 /* not marked as continuing, this is the final fragment */
584 firstbyte |= WSBIT_FIN | opcode;
585 else
586 /* marked as continuing, this is the final fragment; set CONT
587 opcode and FIN bit */
588 firstbyte |= WSBIT_FIN | WSBIT_OPCODE_CONT;
589
590 ws->ws.contfragment = FALSE;
591 infof(data, "WS: set FIN");
592 }
593 else if(ws->ws.contfragment) {
594 /* the previous fragment was not a final one and this isn't either, keep a
595 CONT opcode and no FIN bit */
596 firstbyte |= WSBIT_OPCODE_CONT;
597 infof(data, "WS: keep CONT, no FIN");
598 }
599 else {
600 firstbyte = opcode;
601 ws->ws.contfragment = TRUE;
602 infof(data, "WS: set CONT, no FIN");
603 }
604 out[0] = firstbyte;
605 if(len > 65535) {
606 out[1] = 127 | WSBIT_MASK;
607 out[2] = (len >> 8) & 0xff;
608 out[3] = len & 0xff;
609 outi = 10;
610 }
611 else if(len > 126) {
612 out[1] = 126 | WSBIT_MASK;
613 out[2] = (len >> 8) & 0xff;
614 out[3] = len & 0xff;
615 outi = 4;
616 }
617 else {
618 out[1] = (unsigned char)len | WSBIT_MASK;
619 outi = 2;
620 }
621
622 infof(data, "WS: send FIN bit %u (byte %02x)",
623 firstbyte & WSBIT_FIN ? 1 : 0,
624 firstbyte);
625 infof(data, "WS: send payload len %u", (int)len);
626
627 /* 4 bytes mask */
628 memcpy(&out[outi], &ws->ws.mask, 4);
629
630 if(data->set.upload_buffer_size < (len + 10))
631 return 0;
632
633 /* pass over the mask */
634 outi += 4;
635
636 ws->ws.xori = 0;
637 /* return packet size */
638 return outi;
639}
640
641CURL_EXTERN CURLcode curl_ws_send(struct Curl_easy *data, const void *buffer,
642 size_t buflen, size_t *sent,
643 curl_off_t totalsize,
644 unsigned int sendflags)
645{
646 CURLcode result;
647 size_t headlen;
648 char *out;
649 ssize_t written;
650 struct websocket *wsp = &data->req.p.http->ws;
651
652 if(!data->set.ws_raw_mode) {
653 result = Curl_get_upload_buffer(data);
654 if(result)
655 return result;
656 }
657 else {
658 if(totalsize || sendflags)
659 return CURLE_BAD_FUNCTION_ARGUMENT;
660 }
661
662 if(data->set.ws_raw_mode) {
663 if(!buflen)
664 /* nothing to do */
665 return CURLE_OK;
666 /* raw mode sends exactly what was requested, and this is from within
667 the write callback */
668 if(Curl_is_in_callback(data)) {
669 if(!data->conn) {
670 failf(data, "No associated connection");
671 return CURLE_SEND_ERROR;
672 }
673 result = Curl_write(data, data->conn->writesockfd, buffer, buflen,
674 &written);
675 }
676 else
677 result = Curl_senddata(data, buffer, buflen, &written);
678
679 infof(data, "WS: wanted to send %zu bytes, sent %zu bytes",
680 buflen, written);
681 *sent = written;
682 return result;
683 }
684
685 if(buflen > (data->set.upload_buffer_size - 10))
686 /* don't do more than this in one go */
687 buflen = data->set.upload_buffer_size - 10;
688
689 if(sendflags & CURLWS_OFFSET) {
690 if(totalsize) {
691 /* a frame series 'totalsize' bytes big, this is the first */
692 headlen = ws_packethead(data, totalsize, sendflags);
693 wsp->sleft = totalsize - buflen;
694 }
695 else {
696 headlen = 0;
697 if((curl_off_t)buflen > wsp->sleft) {
698 infof(data, "WS: unaligned frame size (sending %zu instead of %zu)",
699 buflen, wsp->sleft);
700 wsp->sleft = 0;
701 }
702 else
703 wsp->sleft -= buflen;
704 }
705 }
706 else
707 headlen = ws_packethead(data, buflen, sendflags);
708
709 /* headlen is the size of the frame header */
710 out = data->state.ulbuf;
711 if(buflen)
712 /* for PING and PONG etc there might not be a payload */
713 ws_xor(data, buffer, (unsigned char *)out + headlen, buflen);
714
715 if(data->set.connect_only)
716 result = Curl_senddata(data, out, buflen + headlen, &written);
717 else
718 result = Curl_write(data, data->conn->writesockfd, out,
719 buflen + headlen, &written);
720
721 infof(data, "WS: wanted to send %zu bytes, sent %zu bytes",
722 headlen + buflen, written);
723
724 if(!result) {
725 /* the *sent number only counts "payload", excluding the header */
726 if((size_t)written > headlen)
727 *sent = written - headlen;
728 else
729 *sent = 0;
730 }
731 return result;
732}
733
734void Curl_ws_done(struct Curl_easy *data)
735{
736 struct websocket *wsp = &data->req.p.http->ws;
737 DEBUGASSERT(wsp);
738 Curl_dyn_free(&wsp->buf);
739}
740
741CURLcode Curl_ws_disconnect(struct Curl_easy *data,
742 struct connectdata *conn,
743 bool dead_connection)
744{
745 struct ws_conn *wsc = &conn->proto.ws;
746 (void)data;
747 (void)dead_connection;
748 Curl_dyn_free(&wsc->early);
749 return CURLE_OK;
750}
751
752CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
753{
754 /* we only return something for websocket, called from within the callback
755 when not using raw mode */
756 if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) && data->req.p.http &&
757 !data->set.ws_raw_mode)
758 return &data->req.p.http->ws.frame;
759 return NULL;
760}
761
762#else
763
764CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen,
765 size_t *nread,
766 struct curl_ws_frame **metap)
767{
768 (void)curl;
769 (void)buffer;
770 (void)buflen;
771 (void)nread;
772 (void)metap;
773 return CURLE_NOT_BUILT_IN;
774}
775
776CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer,
777 size_t buflen, size_t *sent,
778 curl_off_t framesize,
779 unsigned int sendflags)
780{
781 (void)curl;
782 (void)buffer;
783 (void)buflen;
784 (void)sent;
785 (void)framesize;
786 (void)sendflags;
787 return CURLE_NOT_BUILT_IN;
788}
789
790CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
791{
792 (void)data;
793 return NULL;
794}
795#endif /* USE_WEBSOCKETS */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette