VirtualBox

source: vbox/trunk/src/libs/curl-8.7.1/lib/c-hyper.c@ 104773

Last change on this file since 104773 was 104083, checked in by vboxsync, 9 months ago

curl-8.7.1: Applied and adjusted our curl changes to 8.4.0. bugref:10639

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

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