VirtualBox

source: vbox/trunk/src/libs/curl-8.3.0/lib/c-hyper.c@ 101259

Last change on this file since 101259 was 101127, checked in by vboxsync, 19 months ago

curl-8.3.0: Applied and adjusted our curl changes to 8.0.1. bugref:10526

  • Property svn:eol-style set to native
File size: 36.3 KB
Line 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <[email protected]>, 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.haxx.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
25#include "curl_setup.h"
26
27#if !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER)
28
29#ifdef HAVE_NETINET_IN_H
30#include <netinet/in.h>
31#endif
32
33#ifdef HAVE_NETDB_H
34#include <netdb.h>
35#endif
36#ifdef HAVE_ARPA_INET_H
37#include <arpa/inet.h>
38#endif
39#ifdef HAVE_NET_IF_H
40#include <net/if.h>
41#endif
42#ifdef HAVE_SYS_IOCTL_H
43#include <sys/ioctl.h>
44#endif
45
46#ifdef HAVE_SYS_PARAM_H
47#include <sys/param.h>
48#endif
49
50#include <hyper.h>
51#include "urldata.h"
52#include "sendf.h"
53#include "transfer.h"
54#include "multiif.h"
55#include "progress.h"
56#include "content_encoding.h"
57#include "ws.h"
58
59/* The last 3 #include files should be in this order */
60#include "curl_printf.h"
61#include "curl_memory.h"
62#include "memdebug.h"
63
64typedef enum {
65 USERDATA_NOT_SET = 0, /* for tasks with no userdata set; must be zero */
66 USERDATA_RESP_BODY
67} userdata_t;
68
69size_t Curl_hyper_recv(void *userp, hyper_context *ctx,
70 uint8_t *buf, size_t buflen)
71{
72 struct Curl_easy *data = userp;
73 struct connectdata *conn = data->conn;
74 CURLcode result;
75 ssize_t nread;
76 DEBUGASSERT(conn);
77 (void)ctx;
78
79 DEBUGF(infof(data, "Curl_hyper_recv(%zu)", buflen));
80 result = Curl_read(data, conn->sockfd, (char *)buf, buflen, &nread);
81 if(result == CURLE_AGAIN) {
82 /* would block, register interest */
83 DEBUGF(infof(data, "Curl_hyper_recv(%zu) -> EAGAIN", buflen));
84 if(data->hyp.read_waker)
85 hyper_waker_free(data->hyp.read_waker);
86 data->hyp.read_waker = hyper_context_waker(ctx);
87 if(!data->hyp.read_waker) {
88 failf(data, "Couldn't make the read hyper_context_waker");
89 return HYPER_IO_ERROR;
90 }
91 return HYPER_IO_PENDING;
92 }
93 else if(result) {
94 failf(data, "Curl_read failed");
95 return HYPER_IO_ERROR;
96 }
97 DEBUGF(infof(data, "Curl_hyper_recv(%zu) -> %zd", buflen, nread));
98 return (size_t)nread;
99}
100
101size_t Curl_hyper_send(void *userp, hyper_context *ctx,
102 const uint8_t *buf, size_t buflen)
103{
104 struct Curl_easy *data = userp;
105 struct connectdata *conn = data->conn;
106 CURLcode result;
107 ssize_t nwrote;
108
109 DEBUGF(infof(data, "Curl_hyper_send(%zu)", buflen));
110 result = Curl_write(data, conn->sockfd, (void *)buf, buflen, &nwrote);
111 if(!result && !nwrote)
112 result = CURLE_AGAIN;
113 if(result == CURLE_AGAIN) {
114 DEBUGF(infof(data, "Curl_hyper_send(%zu) -> EAGAIN", buflen));
115 /* would block, register interest */
116 if(data->hyp.write_waker)
117 hyper_waker_free(data->hyp.write_waker);
118 data->hyp.write_waker = hyper_context_waker(ctx);
119 if(!data->hyp.write_waker) {
120 failf(data, "Couldn't make the write hyper_context_waker");
121 return HYPER_IO_ERROR;
122 }
123 return HYPER_IO_PENDING;
124 }
125 else if(result) {
126 failf(data, "Curl_write failed");
127 return HYPER_IO_ERROR;
128 }
129 DEBUGF(infof(data, "Curl_hyper_send(%zu) -> %zd", buflen, nwrote));
130 return (size_t)nwrote;
131}
132
133static int hyper_each_header(void *userdata,
134 const uint8_t *name,
135 size_t name_len,
136 const uint8_t *value,
137 size_t value_len)
138{
139 struct Curl_easy *data = (struct Curl_easy *)userdata;
140 size_t len;
141 char *headp;
142 CURLcode result;
143 int writetype;
144
145 if(name_len + value_len + 2 > CURL_MAX_HTTP_HEADER) {
146 failf(data, "Too long response header");
147 data->state.hresult = CURLE_OUT_OF_MEMORY;
148 return HYPER_ITER_BREAK;
149 }
150
151 if(!data->req.bytecount)
152 Curl_pgrsTime(data, TIMER_STARTTRANSFER);
153
154 Curl_dyn_reset(&data->state.headerb);
155 if(name_len) {
156 if(Curl_dyn_addf(&data->state.headerb, "%.*s: %.*s\r\n",
157 (int) name_len, name, (int) value_len, value))
158 return HYPER_ITER_BREAK;
159 }
160 else {
161 if(Curl_dyn_addn(&data->state.headerb, STRCONST("\r\n")))
162 return HYPER_ITER_BREAK;
163 }
164 len = Curl_dyn_len(&data->state.headerb);
165 headp = Curl_dyn_ptr(&data->state.headerb);
166
167 result = Curl_http_header(data, data->conn, headp);
168 if(result) {
169 data->state.hresult = result;
170 return HYPER_ITER_BREAK;
171 }
172
173 Curl_debug(data, CURLINFO_HEADER_IN, headp, len);
174
175 if(!data->state.hconnect || !data->set.suppress_connect_headers) {
176 writetype = CLIENTWRITE_HEADER;
177 if(data->set.include_header)
178 writetype |= CLIENTWRITE_BODY;
179 if(data->state.hconnect)
180 writetype |= CLIENTWRITE_CONNECT;
181 if(data->req.httpcode/100 == 1)
182 writetype |= CLIENTWRITE_1XX;
183 result = Curl_client_write(data, writetype, headp, len);
184 if(result) {
185 data->state.hresult = CURLE_ABORTED_BY_CALLBACK;
186 return HYPER_ITER_BREAK;
187 }
188 }
189
190 result = Curl_bump_headersize(data, len, FALSE);
191 if(result) {
192 data->state.hresult = result;
193 return HYPER_ITER_BREAK;
194 }
195 return HYPER_ITER_CONTINUE;
196}
197
198static int hyper_body_chunk(void *userdata, const hyper_buf *chunk)
199{
200 char *buf = (char *)hyper_buf_bytes(chunk);
201 size_t len = hyper_buf_len(chunk);
202 struct Curl_easy *data = (struct Curl_easy *)userdata;
203 struct SingleRequest *k = &data->req;
204 CURLcode result = CURLE_OK;
205
206 if(0 == k->bodywrites++) {
207 bool done = FALSE;
208#if defined(USE_NTLM)
209 struct connectdata *conn = data->conn;
210 if(conn->bits.close &&
211 (((data->req.httpcode == 401) &&
212 (conn->http_ntlm_state == NTLMSTATE_TYPE2)) ||
213 ((data->req.httpcode == 407) &&
214 (conn->proxy_ntlm_state == NTLMSTATE_TYPE2)))) {
215 infof(data, "Connection closed while negotiating NTLM");
216 data->state.authproblem = TRUE;
217 Curl_safefree(data->req.newurl);
218 }
219#endif
220 if(data->state.expect100header) {
221 Curl_expire_done(data, EXPIRE_100_TIMEOUT);
222 if(data->req.httpcode < 400) {
223 k->exp100 = EXP100_SEND_DATA;
224 if(data->hyp.exp100_waker) {
225 hyper_waker_wake(data->hyp.exp100_waker);
226 data->hyp.exp100_waker = NULL;
227 }
228 }
229 else { /* >= 4xx */
230 k->exp100 = EXP100_FAILED;
231 }
232 }
233 if(data->state.hconnect && (data->req.httpcode/100 != 2) &&
234 data->state.authproxy.done) {
235 done = TRUE;
236 result = CURLE_OK;
237 }
238 else
239 result = Curl_http_firstwrite(data, data->conn, &done);
240 if(result || done) {
241 infof(data, "Return early from hyper_body_chunk");
242 data->state.hresult = result;
243 return HYPER_ITER_BREAK;
244 }
245 }
246 if(k->ignorebody)
247 return HYPER_ITER_CONTINUE;
248 if(0 == len)
249 return HYPER_ITER_CONTINUE;
250 Curl_debug(data, CURLINFO_DATA_IN, buf, len);
251 if(!data->set.http_ce_skip && k->writer_stack)
252 /* content-encoded data */
253 result = Curl_unencode_write(data, k->writer_stack, buf, len);
254 else
255 result = Curl_client_write(data, CLIENTWRITE_BODY, buf, len);
256
257 if(result) {
258 data->state.hresult = result;
259 return HYPER_ITER_BREAK;
260 }
261
262 data->req.bytecount += len;
263 Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
264 return HYPER_ITER_CONTINUE;
265}
266
267/*
268 * Hyper does not consider the status line, the first line in an HTTP/1
269 * response, to be a header. The libcurl API does. This function sends the
270 * status line in the header callback. */
271static CURLcode status_line(struct Curl_easy *data,
272 struct connectdata *conn,
273 uint16_t http_status,
274 int http_version,
275 const uint8_t *reason, size_t rlen)
276{
277 CURLcode result;
278 size_t len;
279 const char *vstr;
280 int writetype;
281 vstr = http_version == HYPER_HTTP_VERSION_1_1 ? "1.1" :
282 (http_version == HYPER_HTTP_VERSION_2 ? "2" : "1.0");
283
284 /* We need to set 'httpcodeq' for functions that check the response code in
285 a single place. */
286 data->req.httpcode = http_status;
287
288 if(data->state.hconnect)
289 /* CONNECT */
290 data->info.httpproxycode = http_status;
291 else {
292 conn->httpversion =
293 http_version == HYPER_HTTP_VERSION_1_1 ? 11 :
294 (http_version == HYPER_HTTP_VERSION_2 ? 20 : 10);
295 if(http_version == HYPER_HTTP_VERSION_1_0)
296 data->state.httpwant = CURL_HTTP_VERSION_1_0;
297
298 result = Curl_http_statusline(data, conn);
299 if(result)
300 return result;
301 }
302
303 Curl_dyn_reset(&data->state.headerb);
304
305 result = Curl_dyn_addf(&data->state.headerb, "HTTP/%s %03d %.*s\r\n",
306 vstr,
307 (int)http_status,
308 (int)rlen, reason);
309 if(result)
310 return result;
311 len = Curl_dyn_len(&data->state.headerb);
312 Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&data->state.headerb),
313 len);
314
315 if(!data->state.hconnect || !data->set.suppress_connect_headers) {
316 writetype = CLIENTWRITE_HEADER|CLIENTWRITE_STATUS;
317 if(data->set.include_header)
318 writetype |= CLIENTWRITE_BODY;
319 result = Curl_client_write(data, writetype,
320 Curl_dyn_ptr(&data->state.headerb), len);
321 if(result)
322 return result;
323 }
324 result = Curl_bump_headersize(data, len, FALSE);
325 return result;
326}
327
328/*
329 * Hyper does not pass on the last empty response header. The libcurl API
330 * does. This function sends an empty header in the header callback.
331 */
332static CURLcode empty_header(struct Curl_easy *data)
333{
334 CURLcode result = Curl_http_size(data);
335 if(!result) {
336 result = hyper_each_header(data, NULL, 0, NULL, 0) ?
337 CURLE_WRITE_ERROR : CURLE_OK;
338 if(result)
339 failf(data, "hyperstream: couldn't pass blank header");
340 }
341 return result;
342}
343
344CURLcode Curl_hyper_stream(struct Curl_easy *data,
345 struct connectdata *conn,
346 int *didwhat,
347 bool *done,
348 int select_res)
349{
350 hyper_response *resp = NULL;
351 uint16_t http_status;
352 int http_version;
353 hyper_headers *headers = NULL;
354 hyper_body *resp_body = NULL;
355 struct hyptransfer *h = &data->hyp;
356 hyper_task *task;
357 hyper_task *foreach;
358 const uint8_t *reasonp;
359 size_t reason_len;
360 CURLcode result = CURLE_OK;
361 struct SingleRequest *k = &data->req;
362 (void)conn;
363
364 if(k->exp100 > EXP100_SEND_DATA) {
365 struct curltime now = Curl_now();
366 timediff_t ms = Curl_timediff(now, k->start100);
367 if(ms >= data->set.expect_100_timeout) {
368 /* we've waited long enough, continue anyway */
369 k->exp100 = EXP100_SEND_DATA;
370 k->keepon |= KEEP_SEND;
371 Curl_expire_done(data, EXPIRE_100_TIMEOUT);
372 infof(data, "Done waiting for 100-continue");
373 if(data->hyp.exp100_waker) {
374 hyper_waker_wake(data->hyp.exp100_waker);
375 data->hyp.exp100_waker = NULL;
376 }
377 }
378 }
379
380 if(select_res & CURL_CSELECT_IN) {
381 if(h->read_waker)
382 hyper_waker_wake(h->read_waker);
383 h->read_waker = NULL;
384 }
385 if(select_res & CURL_CSELECT_OUT) {
386 if(h->write_waker)
387 hyper_waker_wake(h->write_waker);
388 h->write_waker = NULL;
389 }
390
391 *done = FALSE;
392 do {
393 hyper_task_return_type t;
394 task = hyper_executor_poll(h->exec);
395 if(!task) {
396 *didwhat = KEEP_RECV;
397 break;
398 }
399 t = hyper_task_type(task);
400 if(t == HYPER_TASK_ERROR) {
401 hyper_error *hypererr = hyper_task_value(task);
402 hyper_task_free(task);
403 if(data->state.hresult) {
404 /* override Hyper's view, might not even be an error */
405 result = data->state.hresult;
406 infof(data, "hyperstream is done (by early callback)");
407 }
408 else {
409 uint8_t errbuf[256];
410 size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
411 hyper_code code = hyper_error_code(hypererr);
412 failf(data, "Hyper: [%d] %.*s", (int)code, (int)errlen, errbuf);
413 switch(code) {
414 case HYPERE_ABORTED_BY_CALLBACK:
415 result = CURLE_OK;
416 break;
417 case HYPERE_UNEXPECTED_EOF:
418 if(!data->req.bytecount)
419 result = CURLE_GOT_NOTHING;
420 else
421 result = CURLE_RECV_ERROR;
422 break;
423 case HYPERE_INVALID_PEER_MESSAGE:
424 /* bump headerbytecount to avoid the count remaining at zero and
425 appearing to not having read anything from the peer at all */
426 data->req.headerbytecount++;
427 result = CURLE_UNSUPPORTED_PROTOCOL; /* maybe */
428 break;
429 default:
430 result = CURLE_RECV_ERROR;
431 break;
432 }
433 }
434 *done = TRUE;
435 hyper_error_free(hypererr);
436 break;
437 }
438 else if(t == HYPER_TASK_EMPTY) {
439 void *userdata = hyper_task_userdata(task);
440 hyper_task_free(task);
441 if((userdata_t)userdata == USERDATA_RESP_BODY) {
442 /* end of transfer */
443 *done = TRUE;
444 infof(data, "hyperstream is done");
445 if(!k->bodywrites) {
446 /* hyper doesn't always call the body write callback */
447 bool stilldone;
448 result = Curl_http_firstwrite(data, data->conn, &stilldone);
449 }
450 break;
451 }
452 else {
453 /* A background task for hyper; ignore */
454 continue;
455 }
456 }
457
458 DEBUGASSERT(HYPER_TASK_RESPONSE);
459
460 resp = hyper_task_value(task);
461 hyper_task_free(task);
462
463 *didwhat = KEEP_RECV;
464 if(!resp) {
465 failf(data, "hyperstream: couldn't get response");
466 return CURLE_RECV_ERROR;
467 }
468
469 http_status = hyper_response_status(resp);
470 http_version = hyper_response_version(resp);
471 reasonp = hyper_response_reason_phrase(resp);
472 reason_len = hyper_response_reason_phrase_len(resp);
473
474 if(http_status == 417 && data->state.expect100header) {
475 infof(data, "Got 417 while waiting for a 100");
476 data->state.disableexpect = TRUE;
477 data->req.newurl = strdup(data->state.url);
478 Curl_done_sending(data, k);
479 }
480
481 result = status_line(data, conn,
482 http_status, http_version, reasonp, reason_len);
483 if(result)
484 break;
485
486 headers = hyper_response_headers(resp);
487 if(!headers) {
488 failf(data, "hyperstream: couldn't get response headers");
489 result = CURLE_RECV_ERROR;
490 break;
491 }
492
493 /* the headers are already received */
494 hyper_headers_foreach(headers, hyper_each_header, data);
495 if(data->state.hresult) {
496 result = data->state.hresult;
497 break;
498 }
499
500 result = empty_header(data);
501 if(result)
502 break;
503
504 k->deductheadercount =
505 (100 <= http_status && 199 >= http_status)?k->headerbytecount:0;
506#ifdef USE_WEBSOCKETS
507 if(k->upgr101 == UPGR101_WS) {
508 if(http_status == 101) {
509 /* verify the response */
510 result = Curl_ws_accept(data, NULL, 0);
511 if(result)
512 return result;
513 }
514 else {
515 failf(data, "Expected 101, got %u", k->httpcode);
516 result = CURLE_HTTP_RETURNED_ERROR;
517 break;
518 }
519 }
520#endif
521
522 /* Curl_http_auth_act() checks what authentication methods that are
523 * available and decides which one (if any) to use. It will set 'newurl'
524 * if an auth method was picked. */
525 result = Curl_http_auth_act(data);
526 if(result)
527 break;
528
529 resp_body = hyper_response_body(resp);
530 if(!resp_body) {
531 failf(data, "hyperstream: couldn't get response body");
532 result = CURLE_RECV_ERROR;
533 break;
534 }
535 foreach = hyper_body_foreach(resp_body, hyper_body_chunk, data);
536 if(!foreach) {
537 failf(data, "hyperstream: body foreach failed");
538 result = CURLE_OUT_OF_MEMORY;
539 break;
540 }
541 hyper_task_set_userdata(foreach, (void *)USERDATA_RESP_BODY);
542 if(HYPERE_OK != hyper_executor_push(h->exec, foreach)) {
543 failf(data, "Couldn't hyper_executor_push the body-foreach");
544 result = CURLE_OUT_OF_MEMORY;
545 break;
546 }
547
548 hyper_response_free(resp);
549 resp = NULL;
550 } while(1);
551 if(resp)
552 hyper_response_free(resp);
553 return result;
554}
555
556static CURLcode debug_request(struct Curl_easy *data,
557 const char *method,
558 const char *path,
559 bool h2)
560{
561 char *req = aprintf("%s %s HTTP/%s\r\n", method, path,
562 h2?"2":"1.1");
563 if(!req)
564 return CURLE_OUT_OF_MEMORY;
565 Curl_debug(data, CURLINFO_HEADER_OUT, req, strlen(req));
566 free(req);
567 return CURLE_OK;
568}
569
570/*
571 * Given a full header line "name: value" (optional CRLF in the input, should
572 * be in the output), add to Hyper and send to the debug callback.
573 *
574 * Supports multiple headers.
575 */
576
577CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers,
578 const char *line)
579{
580 const char *p;
581 const char *n;
582 size_t nlen;
583 const char *v;
584 size_t vlen;
585 bool newline = TRUE;
586 int numh = 0;
587
588 if(!line)
589 return CURLE_OK;
590 n = line;
591 do {
592 size_t linelen = 0;
593
594 p = strchr(n, ':');
595 if(!p)
596 /* this is fine if we already added at least one header */
597 return numh ? CURLE_OK : CURLE_BAD_FUNCTION_ARGUMENT;
598 nlen = p - n;
599 p++; /* move past the colon */
600 while(*p == ' ')
601 p++;
602 v = p;
603 p = strchr(v, '\r');
604 if(!p) {
605 p = strchr(v, '\n');
606 if(p)
607 linelen = 1; /* LF only */
608 else {
609 p = strchr(v, '\0');
610 newline = FALSE; /* no newline */
611 }
612 }
613 else
614 linelen = 2; /* CRLF ending */
615 linelen += (p - n);
616 vlen = p - v;
617
618 if(HYPERE_OK != hyper_headers_add(headers, (uint8_t *)n, nlen,
619 (uint8_t *)v, vlen)) {
620 failf(data, "hyper refused to add header '%s'", line);
621 return CURLE_OUT_OF_MEMORY;
622 }
623 if(data->set.verbose) {
624 char *ptr = NULL;
625 if(!newline) {
626 ptr = aprintf("%.*s\r\n", (int)linelen, line);
627 if(!ptr)
628 return CURLE_OUT_OF_MEMORY;
629 Curl_debug(data, CURLINFO_HEADER_OUT, ptr, linelen + 2);
630 free(ptr);
631 }
632 else
633 Curl_debug(data, CURLINFO_HEADER_OUT, (char *)n, linelen);
634 }
635 numh++;
636 n += linelen;
637 } while(newline);
638 return CURLE_OK;
639}
640
641static CURLcode request_target(struct Curl_easy *data,
642 struct connectdata *conn,
643 const char *method,
644 bool h2,
645 hyper_request *req)
646{
647 CURLcode result;
648 struct dynbuf r;
649
650 Curl_dyn_init(&r, DYN_HTTP_REQUEST);
651
652 result = Curl_http_target(data, conn, &r);
653 if(result)
654 return result;
655
656 if(h2 && hyper_request_set_uri_parts(req,
657 /* scheme */
658 (uint8_t *)data->state.up.scheme,
659 strlen(data->state.up.scheme),
660 /* authority */
661 (uint8_t *)conn->host.name,
662 strlen(conn->host.name),
663 /* path_and_query */
664 (uint8_t *)Curl_dyn_uptr(&r),
665 Curl_dyn_len(&r))) {
666 failf(data, "error setting uri parts to hyper");
667 result = CURLE_OUT_OF_MEMORY;
668 }
669 else if(!h2 && hyper_request_set_uri(req, (uint8_t *)Curl_dyn_uptr(&r),
670 Curl_dyn_len(&r))) {
671 failf(data, "error setting uri to hyper");
672 result = CURLE_OUT_OF_MEMORY;
673 }
674 else
675 result = debug_request(data, method, Curl_dyn_ptr(&r), h2);
676
677 Curl_dyn_free(&r);
678
679 return result;
680}
681
682static int uploadpostfields(void *userdata, hyper_context *ctx,
683 hyper_buf **chunk)
684{
685 struct Curl_easy *data = (struct Curl_easy *)userdata;
686 (void)ctx;
687 if(data->req.exp100 > EXP100_SEND_DATA) {
688 if(data->req.exp100 == EXP100_FAILED)
689 return HYPER_POLL_ERROR;
690
691 /* still waiting confirmation */
692 if(data->hyp.exp100_waker)
693 hyper_waker_free(data->hyp.exp100_waker);
694 data->hyp.exp100_waker = hyper_context_waker(ctx);
695 return HYPER_POLL_PENDING;
696 }
697 if(data->req.upload_done)
698 *chunk = NULL; /* nothing more to deliver */
699 else {
700 /* send everything off in a single go */
701 hyper_buf *copy = hyper_buf_copy(data->set.postfields,
702 (size_t)data->req.p.http->postsize);
703 if(copy)
704 *chunk = copy;
705 else {
706 data->state.hresult = CURLE_OUT_OF_MEMORY;
707 return HYPER_POLL_ERROR;
708 }
709 /* increasing the writebytecount here is a little premature but we
710 don't know exactly when the body is sent */
711 data->req.writebytecount += (size_t)data->req.p.http->postsize;
712 Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
713 data->req.upload_done = TRUE;
714 }
715 return HYPER_POLL_READY;
716}
717
718static int uploadstreamed(void *userdata, hyper_context *ctx,
719 hyper_buf **chunk)
720{
721 size_t fillcount;
722 struct Curl_easy *data = (struct Curl_easy *)userdata;
723 struct connectdata *conn = (struct connectdata *)data->conn;
724 CURLcode result;
725 (void)ctx;
726
727 if(data->req.exp100 > EXP100_SEND_DATA) {
728 if(data->req.exp100 == EXP100_FAILED)
729 return HYPER_POLL_ERROR;
730
731 /* still waiting confirmation */
732 if(data->hyp.exp100_waker)
733 hyper_waker_free(data->hyp.exp100_waker);
734 data->hyp.exp100_waker = hyper_context_waker(ctx);
735 return HYPER_POLL_PENDING;
736 }
737
738 if(data->req.upload_chunky && conn->bits.authneg) {
739 fillcount = 0;
740 data->req.upload_chunky = FALSE;
741 result = CURLE_OK;
742 }
743 else {
744 result = Curl_fillreadbuffer(data, data->set.upload_buffer_size,
745 &fillcount);
746 }
747 if(result) {
748 data->state.hresult = result;
749 return HYPER_POLL_ERROR;
750 }
751 if(!fillcount) {
752 if((data->req.keepon & KEEP_SEND_PAUSE) != KEEP_SEND_PAUSE)
753 /* done! */
754 *chunk = NULL;
755 else {
756 /* paused, save a waker */
757 if(data->hyp.send_body_waker)
758 hyper_waker_free(data->hyp.send_body_waker);
759 data->hyp.send_body_waker = hyper_context_waker(ctx);
760 return HYPER_POLL_PENDING;
761 }
762 }
763 else {
764 hyper_buf *copy = hyper_buf_copy((uint8_t *)data->state.ulbuf, fillcount);
765 if(copy)
766 *chunk = copy;
767 else {
768 data->state.hresult = CURLE_OUT_OF_MEMORY;
769 return HYPER_POLL_ERROR;
770 }
771 /* increasing the writebytecount here is a little premature but we
772 don't know exactly when the body is sent */
773 data->req.writebytecount += fillcount;
774 Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
775 }
776 return HYPER_POLL_READY;
777}
778
779/*
780 * bodysend() sets up headers in the outgoing request for an HTTP transfer that
781 * sends a body
782 */
783
784static CURLcode bodysend(struct Curl_easy *data,
785 struct connectdata *conn,
786 hyper_headers *headers,
787 hyper_request *hyperreq,
788 Curl_HttpReq httpreq)
789{
790 struct HTTP *http = data->req.p.http;
791 CURLcode result = CURLE_OK;
792 struct dynbuf req;
793 if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD))
794 Curl_pgrsSetUploadSize(data, 0); /* no request body */
795 else {
796 hyper_body *body;
797 Curl_dyn_init(&req, DYN_HTTP_REQUEST);
798 result = Curl_http_bodysend(data, conn, &req, httpreq);
799
800 if(!result)
801 result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req));
802
803 Curl_dyn_free(&req);
804
805 body = hyper_body_new();
806 hyper_body_set_userdata(body, data);
807 if(data->set.postfields)
808 hyper_body_set_data_func(body, uploadpostfields);
809 else {
810 result = Curl_get_upload_buffer(data);
811 if(result) {
812 hyper_body_free(body);
813 return result;
814 }
815 /* init the "upload from here" pointer */
816 data->req.upload_fromhere = data->state.ulbuf;
817 hyper_body_set_data_func(body, uploadstreamed);
818 }
819 if(HYPERE_OK != hyper_request_set_body(hyperreq, body)) {
820 /* fail */
821 result = CURLE_OUT_OF_MEMORY;
822 }
823 }
824 http->sending = HTTPSEND_BODY;
825 return result;
826}
827
828static CURLcode cookies(struct Curl_easy *data,
829 struct connectdata *conn,
830 hyper_headers *headers)
831{
832 struct dynbuf req;
833 CURLcode result;
834 Curl_dyn_init(&req, DYN_HTTP_REQUEST);
835
836 result = Curl_http_cookies(data, conn, &req);
837 if(!result)
838 result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req));
839 Curl_dyn_free(&req);
840 return result;
841}
842
843/* called on 1xx responses */
844static void http1xx_cb(void *arg, struct hyper_response *resp)
845{
846 struct Curl_easy *data = (struct Curl_easy *)arg;
847 hyper_headers *headers = NULL;
848 CURLcode result = CURLE_OK;
849 uint16_t http_status;
850 int http_version;
851 const uint8_t *reasonp;
852 size_t reason_len;
853
854 infof(data, "Got HTTP 1xx informational");
855
856 http_status = hyper_response_status(resp);
857 http_version = hyper_response_version(resp);
858 reasonp = hyper_response_reason_phrase(resp);
859 reason_len = hyper_response_reason_phrase_len(resp);
860
861 result = status_line(data, data->conn,
862 http_status, http_version, reasonp, reason_len);
863 if(!result) {
864 headers = hyper_response_headers(resp);
865 if(!headers) {
866 failf(data, "hyperstream: couldn't get 1xx response headers");
867 result = CURLE_RECV_ERROR;
868 }
869 }
870 data->state.hresult = result;
871
872 if(!result) {
873 /* the headers are already received */
874 hyper_headers_foreach(headers, hyper_each_header, data);
875 /* this callback also sets data->state.hresult on error */
876
877 if(empty_header(data))
878 result = CURLE_OUT_OF_MEMORY;
879 }
880
881 if(data->state.hresult)
882 infof(data, "ERROR in 1xx, bail out");
883}
884
885/*
886 * Curl_http() gets called from the generic multi_do() function when an HTTP
887 * request is to be performed. This creates and sends a properly constructed
888 * HTTP request.
889 */
890CURLcode Curl_http(struct Curl_easy *data, bool *done)
891{
892 struct connectdata *conn = data->conn;
893 struct hyptransfer *h = &data->hyp;
894 hyper_io *io = NULL;
895 hyper_clientconn_options *options = NULL;
896 hyper_task *task = NULL; /* for the handshake */
897 hyper_task *sendtask = NULL; /* for the send */
898 hyper_clientconn *client = NULL;
899 hyper_request *req = NULL;
900 hyper_headers *headers = NULL;
901 hyper_task *handshake = NULL;
902 CURLcode result;
903 const char *p_accept; /* Accept: string */
904 const char *method;
905 Curl_HttpReq httpreq;
906 bool h2 = FALSE;
907 const char *te = NULL; /* transfer-encoding */
908 hyper_code rc;
909
910 /* Always consider the DO phase done after this function call, even if there
911 may be parts of the request that is not yet sent, since we can deal with
912 the rest of the request in the PERFORM phase. */
913 *done = TRUE;
914
915 infof(data, "Time for the Hyper dance");
916 memset(h, 0, sizeof(struct hyptransfer));
917
918 result = Curl_http_host(data, conn);
919 if(result)
920 return result;
921
922 Curl_http_method(data, conn, &method, &httpreq);
923
924 /* setup the authentication headers */
925 {
926 char *pq = NULL;
927 if(data->state.up.query) {
928 pq = aprintf("%s?%s", data->state.up.path, data->state.up.query);
929 if(!pq)
930 return CURLE_OUT_OF_MEMORY;
931 }
932 result = Curl_http_output_auth(data, conn, method, httpreq,
933 (pq ? pq : data->state.up.path), FALSE);
934 free(pq);
935 if(result)
936 return result;
937 }
938
939 result = Curl_http_resume(data, conn, httpreq);
940 if(result)
941 return result;
942
943 result = Curl_http_range(data, httpreq);
944 if(result)
945 return result;
946
947 result = Curl_http_useragent(data);
948 if(result)
949 return result;
950
951 io = hyper_io_new();
952 if(!io) {
953 failf(data, "Couldn't create hyper IO");
954 result = CURLE_OUT_OF_MEMORY;
955 goto error;
956 }
957 /* tell Hyper how to read/write network data */
958 hyper_io_set_userdata(io, data);
959 hyper_io_set_read(io, Curl_hyper_recv);
960 hyper_io_set_write(io, Curl_hyper_send);
961
962 /* create an executor to poll futures */
963 if(!h->exec) {
964 h->exec = hyper_executor_new();
965 if(!h->exec) {
966 failf(data, "Couldn't create hyper executor");
967 result = CURLE_OUT_OF_MEMORY;
968 goto error;
969 }
970 }
971
972 options = hyper_clientconn_options_new();
973 if(!options) {
974 failf(data, "Couldn't create hyper client options");
975 result = CURLE_OUT_OF_MEMORY;
976 goto error;
977 }
978 if(conn->alpn == CURL_HTTP_VERSION_2) {
979 hyper_clientconn_options_http2(options, 1);
980 h2 = TRUE;
981 }
982 hyper_clientconn_options_set_preserve_header_case(options, 1);
983 hyper_clientconn_options_set_preserve_header_order(options, 1);
984 hyper_clientconn_options_http1_allow_multiline_headers(options, 1);
985
986 hyper_clientconn_options_exec(options, h->exec);
987
988 /* "Both the `io` and the `options` are consumed in this function call" */
989 handshake = hyper_clientconn_handshake(io, options);
990 if(!handshake) {
991 failf(data, "Couldn't create hyper client handshake");
992 result = CURLE_OUT_OF_MEMORY;
993 goto error;
994 }
995 io = NULL;
996 options = NULL;
997
998 if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) {
999 failf(data, "Couldn't hyper_executor_push the handshake");
1000 result = CURLE_OUT_OF_MEMORY;
1001 goto error;
1002 }
1003 handshake = NULL; /* ownership passed on */
1004
1005 task = hyper_executor_poll(h->exec);
1006 if(!task) {
1007 failf(data, "Couldn't hyper_executor_poll the handshake");
1008 result = CURLE_OUT_OF_MEMORY;
1009 goto error;
1010 }
1011
1012 client = hyper_task_value(task);
1013 hyper_task_free(task);
1014
1015 req = hyper_request_new();
1016 if(!req) {
1017 failf(data, "Couldn't hyper_request_new");
1018 result = CURLE_OUT_OF_MEMORY;
1019 goto error;
1020 }
1021
1022 if(!Curl_use_http_1_1plus(data, conn)) {
1023 if(HYPERE_OK != hyper_request_set_version(req,
1024 HYPER_HTTP_VERSION_1_0)) {
1025 failf(data, "error setting HTTP version");
1026 result = CURLE_OUT_OF_MEMORY;
1027 goto error;
1028 }
1029 }
1030 else {
1031 if(!h2 && !data->state.disableexpect) {
1032 data->state.expect100header = TRUE;
1033 }
1034 }
1035
1036 if(hyper_request_set_method(req, (uint8_t *)method, strlen(method))) {
1037 failf(data, "error setting method");
1038 result = CURLE_OUT_OF_MEMORY;
1039 goto error;
1040 }
1041
1042 result = request_target(data, conn, method, h2, req);
1043 if(result)
1044 goto error;
1045
1046 headers = hyper_request_headers(req);
1047 if(!headers) {
1048 failf(data, "hyper_request_headers");
1049 result = CURLE_OUT_OF_MEMORY;
1050 goto error;
1051 }
1052
1053 rc = hyper_request_on_informational(req, http1xx_cb, data);
1054 if(rc) {
1055 result = CURLE_OUT_OF_MEMORY;
1056 goto error;
1057 }
1058
1059 result = Curl_http_body(data, conn, httpreq, &te);
1060 if(result)
1061 goto error;
1062
1063 if(!h2) {
1064 if(data->state.aptr.host) {
1065 result = Curl_hyper_header(data, headers, data->state.aptr.host);
1066 if(result)
1067 goto error;
1068 }
1069 }
1070 else {
1071 /* For HTTP/2, we show the Host: header as if we sent it, to make it look
1072 like for HTTP/1 but it isn't actually sent since :authority is then
1073 used. */
1074 Curl_debug(data, CURLINFO_HEADER_OUT, data->state.aptr.host,
1075 strlen(data->state.aptr.host));
1076 }
1077
1078 if(data->state.aptr.proxyuserpwd) {
1079 result = Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd);
1080 if(result)
1081 goto error;
1082 }
1083
1084 if(data->state.aptr.userpwd) {
1085 result = Curl_hyper_header(data, headers, data->state.aptr.userpwd);
1086 if(result)
1087 goto error;
1088 }
1089
1090 if((data->state.use_range && data->state.aptr.rangeline)) {
1091 result = Curl_hyper_header(data, headers, data->state.aptr.rangeline);
1092 if(result)
1093 goto error;
1094 }
1095
1096 if(data->set.str[STRING_USERAGENT] &&
1097 *data->set.str[STRING_USERAGENT] &&
1098 data->state.aptr.uagent) {
1099 result = Curl_hyper_header(data, headers, data->state.aptr.uagent);
1100 if(result)
1101 goto error;
1102 }
1103
1104 p_accept = Curl_checkheaders(data,
1105 STRCONST("Accept"))?NULL:"Accept: */*\r\n";
1106 if(p_accept) {
1107 result = Curl_hyper_header(data, headers, p_accept);
1108 if(result)
1109 goto error;
1110 }
1111 if(te) {
1112 result = Curl_hyper_header(data, headers, te);
1113 if(result)
1114 goto error;
1115 }
1116
1117#ifndef CURL_DISABLE_ALTSVC
1118 if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) {
1119 char *altused = aprintf("Alt-Used: %s:%d\r\n",
1120 conn->conn_to_host.name, conn->conn_to_port);
1121 if(!altused) {
1122 result = CURLE_OUT_OF_MEMORY;
1123 goto error;
1124 }
1125 result = Curl_hyper_header(data, headers, altused);
1126 if(result)
1127 goto error;
1128 free(altused);
1129 }
1130#endif
1131
1132#ifndef CURL_DISABLE_PROXY
1133 if(conn->bits.httpproxy && !conn->bits.tunnel_proxy &&
1134 !Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
1135 !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) {
1136 result = Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive");
1137 if(result)
1138 goto error;
1139 }
1140#endif
1141
1142 Curl_safefree(data->state.aptr.ref);
1143 if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) {
1144 data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer);
1145 if(!data->state.aptr.ref)
1146 result = CURLE_OUT_OF_MEMORY;
1147 else
1148 result = Curl_hyper_header(data, headers, data->state.aptr.ref);
1149 if(result)
1150 goto error;
1151 }
1152
1153#ifdef HAVE_LIBZ
1154 /* we only consider transfer-encoding magic if libz support is built-in */
1155 result = Curl_transferencode(data);
1156 if(result)
1157 goto error;
1158 result = Curl_hyper_header(data, headers, data->state.aptr.te);
1159 if(result)
1160 goto error;
1161#endif
1162
1163 if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
1164 data->set.str[STRING_ENCODING]) {
1165 Curl_safefree(data->state.aptr.accept_encoding);
1166 data->state.aptr.accept_encoding =
1167 aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
1168 if(!data->state.aptr.accept_encoding)
1169 result = CURLE_OUT_OF_MEMORY;
1170 else
1171 result = Curl_hyper_header(data, headers,
1172 data->state.aptr.accept_encoding);
1173 if(result)
1174 goto error;
1175 }
1176 else
1177 Curl_safefree(data->state.aptr.accept_encoding);
1178
1179 result = cookies(data, conn, headers);
1180 if(result)
1181 goto error;
1182
1183 if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
1184 result = Curl_ws_request(data, headers);
1185
1186 result = Curl_add_timecondition(data, headers);
1187 if(result)
1188 goto error;
1189
1190 result = Curl_add_custom_headers(data, FALSE, headers);
1191 if(result)
1192 goto error;
1193
1194 result = bodysend(data, conn, headers, req, httpreq);
1195 if(result)
1196 goto error;
1197
1198 Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2);
1199
1200 if(data->req.upload_chunky && conn->bits.authneg) {
1201 data->req.upload_chunky = TRUE;
1202 }
1203 else {
1204 data->req.upload_chunky = FALSE;
1205 }
1206 sendtask = hyper_clientconn_send(client, req);
1207 if(!sendtask) {
1208 failf(data, "hyper_clientconn_send");
1209 result = CURLE_OUT_OF_MEMORY;
1210 goto error;
1211 }
1212 req = NULL;
1213
1214 if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
1215 failf(data, "Couldn't hyper_executor_push the send");
1216 result = CURLE_OUT_OF_MEMORY;
1217 goto error;
1218 }
1219 sendtask = NULL; /* ownership passed on */
1220
1221 hyper_clientconn_free(client);
1222 client = NULL;
1223
1224 if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) {
1225 /* HTTP GET/HEAD download */
1226 Curl_pgrsSetUploadSize(data, 0); /* nothing */
1227 Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1);
1228 }
1229 conn->datastream = Curl_hyper_stream;
1230 if(data->state.expect100header)
1231 /* Timeout count starts now since with Hyper we don't know exactly when
1232 the full request has been sent. */
1233 data->req.start100 = Curl_now();
1234
1235 /* clear userpwd and proxyuserpwd to avoid reusing old credentials
1236 * from reused connections */
1237 Curl_safefree(data->state.aptr.userpwd);
1238 Curl_safefree(data->state.aptr.proxyuserpwd);
1239 return CURLE_OK;
1240error:
1241 DEBUGASSERT(result);
1242 if(io)
1243 hyper_io_free(io);
1244
1245 if(options)
1246 hyper_clientconn_options_free(options);
1247
1248 if(handshake)
1249 hyper_task_free(handshake);
1250
1251 if(client)
1252 hyper_clientconn_free(client);
1253
1254 if(req)
1255 hyper_request_free(req);
1256
1257 return result;
1258}
1259
1260void Curl_hyper_done(struct Curl_easy *data)
1261{
1262 struct hyptransfer *h = &data->hyp;
1263 if(h->exec) {
1264 hyper_executor_free(h->exec);
1265 h->exec = NULL;
1266 }
1267 if(h->read_waker) {
1268 hyper_waker_free(h->read_waker);
1269 h->read_waker = NULL;
1270 }
1271 if(h->write_waker) {
1272 hyper_waker_free(h->write_waker);
1273 h->write_waker = NULL;
1274 }
1275 if(h->exp100_waker) {
1276 hyper_waker_free(h->exp100_waker);
1277 h->exp100_waker = NULL;
1278 }
1279}
1280
1281#endif /* !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) */
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