VirtualBox

source: vbox/trunk/src/libs/curl-8.11.1/lib/c-hyper.c@ 108333

最後變更 在這個檔案從108333是 108048,由 vboxsync 提交於 7 週 前

curl-8.11.1: Applied and adjusted our curl changes to 8.7.1. jiraref:VBP-1535

  • 屬性 svn:eol-style 設為 native
檔案大小: 34.8 KB
 
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/* 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, FALSE, &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, 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(!k->bodywritten) {
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: could not 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 /* If there is still something to upload, wake it to give it
356 * another try. */
357 hyper_waker_wake(data->hyp.send_body_waker);
358 data->hyp.send_body_waker = NULL;
359 }
360
361 if(select_res & CURL_CSELECT_IN) {
362 if(h->read_waker)
363 hyper_waker_wake(h->read_waker);
364 h->read_waker = NULL;
365 }
366 if(select_res & CURL_CSELECT_OUT) {
367 if(h->write_waker)
368 hyper_waker_wake(h->write_waker);
369 h->write_waker = NULL;
370 }
371
372 while(1) {
373 hyper_task_return_type t;
374 task = hyper_executor_poll(h->exec);
375 if(!task) {
376 *didwhat = KEEP_RECV;
377 break;
378 }
379 t = hyper_task_type(task);
380 if(t == HYPER_TASK_ERROR) {
381 hyper_error *hypererr = hyper_task_value(task);
382 hyper_task_free(task);
383 if(data->state.hresult) {
384 /* override Hyper's view, might not even be an error */
385 result = data->state.hresult;
386 infof(data, "hyperstream is done (by early callback)");
387 }
388 else {
389 uint8_t errbuf[256];
390 size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
391 hyper_code code = hyper_error_code(hypererr);
392 failf(data, "Hyper: [%d] %.*s", (int)code, (int)errlen, errbuf);
393 switch(code) {
394 case HYPERE_ABORTED_BY_CALLBACK:
395 result = CURLE_OK;
396 goto out;
397 case HYPERE_UNEXPECTED_EOF:
398 if(!data->req.bytecount)
399 result = CURLE_GOT_NOTHING;
400 else
401 result = CURLE_RECV_ERROR;
402 goto out;
403 case HYPERE_INVALID_PEER_MESSAGE:
404 /* bump headerbytecount to avoid the count remaining at zero and
405 appearing to not having read anything from the peer at all */
406 data->req.headerbytecount++;
407 result = CURLE_UNSUPPORTED_PROTOCOL; /* maybe */
408 goto out;
409 default:
410 result = CURLE_RECV_ERROR;
411 goto out;
412 }
413 }
414 data->req.done = TRUE;
415 hyper_error_free(hypererr);
416 break;
417 }
418 else if(t == HYPER_TASK_EMPTY) {
419 void *userdata = hyper_task_userdata(task);
420 hyper_task_free(task);
421 if(userdata == (void *)USERDATA_RESP_BODY) {
422 /* end of transfer */
423 data->req.done = TRUE;
424 infof(data, "hyperstream is done");
425 if(!k->bodywritten) {
426 /* hyper does not always call the body write callback */
427 result = Curl_http_firstwrite(data);
428 }
429 break;
430 }
431 else {
432 /* A background task for hyper; ignore */
433 DEBUGF(infof(data, "hyper: some background task done"));
434 continue;
435 }
436 }
437 else if(t == HYPER_TASK_RESPONSE) {
438 resp = hyper_task_value(task);
439 hyper_task_free(task);
440
441 *didwhat = KEEP_RECV;
442 if(!resp) {
443 failf(data, "hyperstream: could not get response");
444 result = CURLE_RECV_ERROR;
445 goto out;
446 }
447
448 http_status = hyper_response_status(resp);
449 http_version = hyper_response_version(resp);
450 reasonp = hyper_response_reason_phrase(resp);
451 reason_len = hyper_response_reason_phrase_len(resp);
452
453 if(http_status == 417 && Curl_http_exp100_is_selected(data)) {
454 infof(data, "Got 417 while waiting for a 100");
455 data->state.disableexpect = TRUE;
456 data->req.newurl = strdup(data->state.url);
457 Curl_req_abort_sending(data);
458 }
459
460 result = status_line(data, conn,
461 http_status, http_version, reasonp, reason_len);
462 if(result)
463 goto out;
464
465 headers = hyper_response_headers(resp);
466 if(!headers) {
467 failf(data, "hyperstream: could not get response headers");
468 result = CURLE_RECV_ERROR;
469 goto out;
470 }
471
472 /* the headers are already received */
473 hyper_headers_foreach(headers, hyper_each_header, data);
474 if(data->state.hresult) {
475 result = data->state.hresult;
476 goto out;
477 }
478
479 result = empty_header(data);
480 if(result)
481 goto out;
482
483 k->deductheadercount =
484 (100 <= http_status && 199 >= http_status) ? k->headerbytecount : 0;
485#ifndef CURL_DISABLE_WEBSOCKETS
486 if(k->upgr101 == UPGR101_WS) {
487 if(http_status == 101) {
488 /* verify the response */
489 result = Curl_ws_accept(data, NULL, 0);
490 if(result)
491 goto out;
492 }
493 else {
494 failf(data, "Expected 101, got %u", k->httpcode);
495 result = CURLE_HTTP_RETURNED_ERROR;
496 goto out;
497 }
498 }
499#endif
500
501 /* Curl_http_auth_act() checks what authentication methods that are
502 * available and decides which one (if any) to use. It will set 'newurl'
503 * if an auth method was picked. */
504 result = Curl_http_auth_act(data);
505 if(result)
506 goto out;
507
508 resp_body = hyper_response_body(resp);
509 if(!resp_body) {
510 failf(data, "hyperstream: could not get response body");
511 result = CURLE_RECV_ERROR;
512 goto out;
513 }
514 foreach = hyper_body_foreach(resp_body, hyper_body_chunk, data);
515 if(!foreach) {
516 failf(data, "hyperstream: body foreach failed");
517 result = CURLE_OUT_OF_MEMORY;
518 goto out;
519 }
520 hyper_task_set_userdata(foreach, (void *)USERDATA_RESP_BODY);
521 if(HYPERE_OK != hyper_executor_push(h->exec, foreach)) {
522 failf(data, "Couldn't hyper_executor_push the body-foreach");
523 result = CURLE_OUT_OF_MEMORY;
524 goto out;
525 }
526
527 hyper_response_free(resp);
528 resp = NULL;
529 }
530 else {
531 DEBUGF(infof(data, "hyper: unhandled tasktype %x", t));
532 }
533 } /* while(1) */
534
535 if(!result && Curl_xfer_needs_flush(data)) {
536 DEBUGF(infof(data, "Curl_hyper_stream(), connection needs flush"));
537 result = Curl_xfer_flush(data);
538 }
539
540out:
541 DEBUGF(infof(data, "Curl_hyper_stream() -> %d", result));
542 if(resp)
543 hyper_response_free(resp);
544 return result;
545}
546
547static CURLcode debug_request(struct Curl_easy *data,
548 const char *method,
549 const char *path)
550{
551 char *req = aprintf("%s %s HTTP/1.1\r\n", method, path);
552 if(!req)
553 return CURLE_OUT_OF_MEMORY;
554 Curl_debug(data, CURLINFO_HEADER_OUT, req, strlen(req));
555 free(req);
556 return CURLE_OK;
557}
558
559/*
560 * Given a full header line "name: value" (optional CRLF in the input, should
561 * be in the output), add to Hyper and send to the debug callback.
562 *
563 * Supports multiple headers.
564 */
565
566CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers,
567 const char *line)
568{
569 const char *p;
570 const char *n;
571 size_t nlen;
572 const char *v;
573 size_t vlen;
574 bool newline = TRUE;
575 int numh = 0;
576
577 if(!line)
578 return CURLE_OK;
579 n = line;
580 do {
581 size_t linelen = 0;
582
583 p = strchr(n, ':');
584 if(!p)
585 /* this is fine if we already added at least one header */
586 return numh ? CURLE_OK : CURLE_BAD_FUNCTION_ARGUMENT;
587 nlen = p - n;
588 p++; /* move past the colon */
589 while(*p == ' ')
590 p++;
591 v = p;
592 p = strchr(v, '\r');
593 if(!p) {
594 p = strchr(v, '\n');
595 if(p)
596 linelen = 1; /* LF only */
597 else {
598 p = strchr(v, '\0');
599 newline = FALSE; /* no newline */
600 }
601 }
602 else
603 linelen = 2; /* CRLF ending */
604 linelen += (p - n);
605 vlen = p - v;
606
607 if(HYPERE_OK != hyper_headers_add(headers, (uint8_t *)n, nlen,
608 (uint8_t *)v, vlen)) {
609 failf(data, "hyper refused to add header '%s'", line);
610 return CURLE_OUT_OF_MEMORY;
611 }
612 if(data->set.verbose) {
613 char *ptr = NULL;
614 if(!newline) {
615 ptr = aprintf("%.*s\r\n", (int)linelen, line);
616 if(!ptr)
617 return CURLE_OUT_OF_MEMORY;
618 Curl_debug(data, CURLINFO_HEADER_OUT, ptr, linelen + 2);
619 free(ptr);
620 }
621 else
622 Curl_debug(data, CURLINFO_HEADER_OUT, (char *)n, linelen);
623 }
624 numh++;
625 n += linelen;
626 } while(newline);
627 return CURLE_OK;
628}
629
630static CURLcode request_target(struct Curl_easy *data,
631 struct connectdata *conn,
632 const char *method,
633 hyper_request *req)
634{
635 CURLcode result;
636 struct dynbuf r;
637
638 Curl_dyn_init(&r, DYN_HTTP_REQUEST);
639
640 result = Curl_http_target(data, conn, &r);
641 if(result)
642 return result;
643
644 if(hyper_request_set_uri(req, (uint8_t *)Curl_dyn_uptr(&r),
645 Curl_dyn_len(&r))) {
646 failf(data, "error setting uri to hyper");
647 result = CURLE_OUT_OF_MEMORY;
648 }
649 else
650 result = debug_request(data, method, Curl_dyn_ptr(&r));
651
652 Curl_dyn_free(&r);
653
654 return result;
655}
656
657static int uploadstreamed(void *userdata, hyper_context *ctx,
658 hyper_buf **chunk)
659{
660 size_t fillcount;
661 struct Curl_easy *data = (struct Curl_easy *)userdata;
662 CURLcode result;
663 char *xfer_ulbuf;
664 size_t xfer_ulblen;
665 bool eos;
666 int rc = HYPER_POLL_ERROR;
667 (void)ctx;
668
669 result = Curl_multi_xfer_ulbuf_borrow(data, &xfer_ulbuf, &xfer_ulblen);
670 if(result)
671 goto out;
672
673 result = Curl_client_read(data, xfer_ulbuf, xfer_ulblen, &fillcount, &eos);
674 if(result)
675 goto out;
676
677 if(fillcount) {
678 hyper_buf *copy = hyper_buf_copy((uint8_t *)xfer_ulbuf, fillcount);
679 if(copy)
680 *chunk = copy;
681 else {
682 result = CURLE_OUT_OF_MEMORY;
683 goto out;
684 }
685 /* increasing the writebytecount here is a little premature but we
686 do not know exactly when the body is sent */
687 data->req.writebytecount += fillcount;
688 if(eos)
689 data->req.eos_read = TRUE;
690 Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
691 rc = HYPER_POLL_READY;
692 }
693 else if(eos) {
694 data->req.eos_read = TRUE;
695 *chunk = NULL;
696 rc = HYPER_POLL_READY;
697 }
698 else {
699 /* paused, save a waker */
700 if(data->hyp.send_body_waker)
701 hyper_waker_free(data->hyp.send_body_waker);
702 data->hyp.send_body_waker = hyper_context_waker(ctx);
703 rc = HYPER_POLL_PENDING;
704 }
705
706 if(!data->req.upload_done && data->req.eos_read) {
707 DEBUGF(infof(data, "hyper: uploadstreamed(), upload is done"));
708 result = Curl_req_set_upload_done(data);
709 }
710
711out:
712 Curl_multi_xfer_ulbuf_release(data, xfer_ulbuf);
713 data->state.hresult = result;
714 DEBUGF(infof(data, "hyper: uploadstreamed() -> %d", result));
715 return rc;
716}
717
718/*
719 * finalize_request() sets up last headers and optional body settings
720 */
721static CURLcode finalize_request(struct Curl_easy *data,
722 hyper_headers *headers,
723 hyper_request *hyperreq,
724 Curl_HttpReq httpreq)
725{
726 CURLcode result = CURLE_OK;
727 struct dynbuf req;
728 if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) {
729 Curl_pgrsSetUploadSize(data, 0); /* no request body */
730 }
731 else {
732 hyper_body *body;
733 Curl_dyn_init(&req, DYN_HTTP_REQUEST);
734 result = Curl_http_req_complete(data, &req, httpreq);
735 if(result)
736 return result;
737
738 /* if the "complete" above did produce more than the closing line,
739 parse the added headers */
740 if(Curl_dyn_len(&req) != 2 || strcmp(Curl_dyn_ptr(&req), "\r\n")) {
741 result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req));
742 if(result)
743 return result;
744 }
745
746 Curl_dyn_free(&req);
747
748 body = hyper_body_new();
749 hyper_body_set_userdata(body, data);
750 hyper_body_set_data_func(body, uploadstreamed);
751
752 if(HYPERE_OK != hyper_request_set_body(hyperreq, body)) {
753 /* fail */
754 result = CURLE_OUT_OF_MEMORY;
755 }
756 }
757
758 return cr_hyper_add(data);
759}
760
761static CURLcode cookies(struct Curl_easy *data,
762 struct connectdata *conn,
763 hyper_headers *headers)
764{
765 struct dynbuf req;
766 CURLcode result;
767 Curl_dyn_init(&req, DYN_HTTP_REQUEST);
768
769 result = Curl_http_cookies(data, conn, &req);
770 if(!result)
771 result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req));
772 Curl_dyn_free(&req);
773 return result;
774}
775
776/* called on 1xx responses */
777static void http1xx_cb(void *arg, struct hyper_response *resp)
778{
779 struct Curl_easy *data = (struct Curl_easy *)arg;
780 hyper_headers *headers = NULL;
781 CURLcode result = CURLE_OK;
782 uint16_t http_status;
783 int http_version;
784 const uint8_t *reasonp;
785 size_t reason_len;
786
787 infof(data, "Got HTTP 1xx informational");
788
789 http_status = hyper_response_status(resp);
790 http_version = hyper_response_version(resp);
791 reasonp = hyper_response_reason_phrase(resp);
792 reason_len = hyper_response_reason_phrase_len(resp);
793
794 result = status_line(data, data->conn,
795 http_status, http_version, reasonp, reason_len);
796 if(!result) {
797 headers = hyper_response_headers(resp);
798 if(!headers) {
799 failf(data, "hyperstream: could not get 1xx response headers");
800 result = CURLE_RECV_ERROR;
801 }
802 }
803 data->state.hresult = result;
804
805 if(!result) {
806 /* the headers are already received */
807 hyper_headers_foreach(headers, hyper_each_header, data);
808 /* this callback also sets data->state.hresult on error */
809
810 if(empty_header(data))
811 result = CURLE_OUT_OF_MEMORY;
812 }
813
814 if(data->state.hresult)
815 infof(data, "ERROR in 1xx, bail out");
816}
817
818/*
819 * Curl_http() gets called from the generic multi_do() function when an HTTP
820 * request is to be performed. This creates and sends a properly constructed
821 * HTTP request.
822 */
823CURLcode Curl_http(struct Curl_easy *data, bool *done)
824{
825 struct connectdata *conn = data->conn;
826 struct hyptransfer *h = &data->hyp;
827 hyper_io *io = NULL;
828 hyper_clientconn_options *options = NULL;
829 hyper_task *task = NULL; /* for the handshake */
830 hyper_task *sendtask = NULL; /* for the send */
831 hyper_clientconn *client = NULL;
832 hyper_request *req = NULL;
833 hyper_headers *headers = NULL;
834 hyper_task *handshake = NULL;
835 CURLcode result;
836 const char *p_accept; /* Accept: string */
837 const char *method;
838 Curl_HttpReq httpreq;
839 const char *te = NULL; /* transfer-encoding */
840 hyper_code rc;
841
842 /* Always consider the DO phase done after this function call, even if there
843 may be parts of the request that is not yet sent, since we can deal with
844 the rest of the request in the PERFORM phase. */
845 *done = TRUE;
846 result = Curl_client_start(data);
847 if(result)
848 goto out;
849
850 /* Add collecting of headers written to client. For a new connection,
851 * we might have done that already, but reuse
852 * or multiplex needs it here as well. */
853 result = Curl_headers_init(data);
854 if(result)
855 goto out;
856
857 infof(data, "Time for the Hyper dance");
858 memset(h, 0, sizeof(struct hyptransfer));
859
860 result = Curl_http_host(data, conn);
861 if(result)
862 goto out;
863
864 Curl_http_method(data, conn, &method, &httpreq);
865
866 DEBUGASSERT(data->req.bytecount == 0);
867
868 /* setup the authentication headers */
869 {
870 char *pq = NULL;
871 if(data->state.up.query) {
872 pq = aprintf("%s?%s", data->state.up.path, data->state.up.query);
873 if(!pq) {
874 result = CURLE_OUT_OF_MEMORY;
875 goto out;
876 }
877 }
878 result = Curl_http_output_auth(data, conn, method, httpreq,
879 (pq ? pq : data->state.up.path), FALSE);
880 free(pq);
881 if(result)
882 goto out;
883 }
884
885 result = Curl_http_req_set_reader(data, httpreq, &te);
886 if(result)
887 goto out;
888
889 result = Curl_http_range(data, httpreq);
890 if(result)
891 goto out;
892
893 result = Curl_http_useragent(data);
894 if(result)
895 goto out;
896
897 io = hyper_io_new();
898 if(!io) {
899 failf(data, "Couldn't create hyper IO");
900 result = CURLE_OUT_OF_MEMORY;
901 goto out;
902 }
903 /* tell Hyper how to read/write network data */
904 h->io_ctx.data = data;
905 h->io_ctx.sockindex = FIRSTSOCKET;
906 hyper_io_set_userdata(io, &h->io_ctx);
907 hyper_io_set_read(io, Curl_hyper_recv);
908 hyper_io_set_write(io, Curl_hyper_send);
909
910 /* create an executor to poll futures */
911 if(!h->exec) {
912 h->exec = hyper_executor_new();
913 if(!h->exec) {
914 failf(data, "Couldn't create hyper executor");
915 result = CURLE_OUT_OF_MEMORY;
916 goto out;
917 }
918 }
919
920 options = hyper_clientconn_options_new();
921 if(!options) {
922 failf(data, "Couldn't create hyper client options");
923 result = CURLE_OUT_OF_MEMORY;
924 goto out;
925 }
926 if(conn->alpn == CURL_HTTP_VERSION_2) {
927 failf(data, "ALPN protocol h2 not supported with Hyper");
928 result = CURLE_UNSUPPORTED_PROTOCOL;
929 goto out;
930 }
931 hyper_clientconn_options_set_preserve_header_case(options, 1);
932 hyper_clientconn_options_set_preserve_header_order(options, 1);
933 hyper_clientconn_options_http1_allow_multiline_headers(options, 1);
934
935 hyper_clientconn_options_exec(options, h->exec);
936
937 /* "Both the `io` and the `options` are consumed in this function call" */
938 handshake = hyper_clientconn_handshake(io, options);
939 if(!handshake) {
940 failf(data, "Couldn't create hyper client handshake");
941 result = CURLE_OUT_OF_MEMORY;
942 goto out;
943 }
944 io = NULL;
945 options = NULL;
946
947 if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) {
948 failf(data, "Couldn't hyper_executor_push the handshake");
949 result = CURLE_OUT_OF_MEMORY;
950 goto out;
951 }
952 handshake = NULL; /* ownership passed on */
953
954 task = hyper_executor_poll(h->exec);
955 if(!task) {
956 failf(data, "Couldn't hyper_executor_poll the handshake");
957 result = CURLE_OUT_OF_MEMORY;
958 goto out;
959 }
960
961 client = hyper_task_value(task);
962 hyper_task_free(task);
963
964 req = hyper_request_new();
965 if(!req) {
966 failf(data, "Couldn't hyper_request_new");
967 result = CURLE_OUT_OF_MEMORY;
968 goto out;
969 }
970
971 if(!Curl_use_http_1_1plus(data, conn)) {
972 if(HYPERE_OK != hyper_request_set_version(req,
973 HYPER_HTTP_VERSION_1_0)) {
974 failf(data, "error setting HTTP version");
975 result = CURLE_OUT_OF_MEMORY;
976 goto out;
977 }
978 }
979
980 if(hyper_request_set_method(req, (uint8_t *)method, strlen(method))) {
981 failf(data, "error setting method");
982 result = CURLE_OUT_OF_MEMORY;
983 goto out;
984 }
985
986 result = request_target(data, conn, method, req);
987 if(result)
988 goto out;
989
990 headers = hyper_request_headers(req);
991 if(!headers) {
992 failf(data, "hyper_request_headers");
993 result = CURLE_OUT_OF_MEMORY;
994 goto out;
995 }
996
997 rc = hyper_request_on_informational(req, http1xx_cb, data);
998 if(rc) {
999 result = CURLE_OUT_OF_MEMORY;
1000 goto out;
1001 }
1002
1003 if(data->state.aptr.host) {
1004 result = Curl_hyper_header(data, headers, data->state.aptr.host);
1005 if(result)
1006 goto out;
1007 }
1008
1009#ifndef CURL_DISABLE_PROXY
1010 if(data->state.aptr.proxyuserpwd) {
1011 result = Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd);
1012 if(result)
1013 goto out;
1014 }
1015#endif
1016
1017 if(data->state.aptr.userpwd) {
1018 result = Curl_hyper_header(data, headers, data->state.aptr.userpwd);
1019 if(result)
1020 goto out;
1021 }
1022
1023 if((data->state.use_range && data->state.aptr.rangeline)) {
1024 result = Curl_hyper_header(data, headers, data->state.aptr.rangeline);
1025 if(result)
1026 goto out;
1027 }
1028
1029 if(data->set.str[STRING_USERAGENT] &&
1030 *data->set.str[STRING_USERAGENT] &&
1031 data->state.aptr.uagent) {
1032 result = Curl_hyper_header(data, headers, data->state.aptr.uagent);
1033 if(result)
1034 goto out;
1035 }
1036
1037 p_accept = Curl_checkheaders(data,
1038 STRCONST("Accept")) ? NULL : "Accept: */*\r\n";
1039 if(p_accept) {
1040 result = Curl_hyper_header(data, headers, p_accept);
1041 if(result)
1042 goto out;
1043 }
1044 if(te) {
1045 result = Curl_hyper_header(data, headers, te);
1046 if(result)
1047 goto out;
1048 }
1049
1050#ifndef CURL_DISABLE_ALTSVC
1051 if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) {
1052 char *altused = aprintf("Alt-Used: %s:%d\r\n",
1053 conn->conn_to_host.name, conn->conn_to_port);
1054 if(!altused) {
1055 result = CURLE_OUT_OF_MEMORY;
1056 goto out;
1057 }
1058 result = Curl_hyper_header(data, headers, altused);
1059 if(result)
1060 goto out;
1061 free(altused);
1062 }
1063#endif
1064
1065#ifndef CURL_DISABLE_PROXY
1066 if(conn->bits.httpproxy && !conn->bits.tunnel_proxy &&
1067 !Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
1068 !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) {
1069 result = Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive");
1070 if(result)
1071 goto out;
1072 }
1073#endif
1074
1075 Curl_safefree(data->state.aptr.ref);
1076 if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) {
1077 data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer);
1078 if(!data->state.aptr.ref)
1079 result = CURLE_OUT_OF_MEMORY;
1080 else
1081 result = Curl_hyper_header(data, headers, data->state.aptr.ref);
1082 if(result)
1083 goto out;
1084 }
1085
1086#ifdef HAVE_LIBZ
1087 /* we only consider transfer-encoding magic if libz support is built-in */
1088 result = Curl_transferencode(data);
1089 if(result)
1090 goto out;
1091 result = Curl_hyper_header(data, headers, data->state.aptr.te);
1092 if(result)
1093 goto out;
1094#endif
1095
1096 if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
1097 data->set.str[STRING_ENCODING]) {
1098 Curl_safefree(data->state.aptr.accept_encoding);
1099 data->state.aptr.accept_encoding =
1100 aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
1101 if(!data->state.aptr.accept_encoding)
1102 result = CURLE_OUT_OF_MEMORY;
1103 else
1104 result = Curl_hyper_header(data, headers,
1105 data->state.aptr.accept_encoding);
1106 if(result)
1107 goto out;
1108 }
1109 else
1110 Curl_safefree(data->state.aptr.accept_encoding);
1111
1112 result = cookies(data, conn, headers);
1113 if(result)
1114 goto out;
1115
1116 if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
1117 result = Curl_ws_request(data, headers);
1118
1119 result = Curl_add_timecondition(data, headers);
1120 if(result)
1121 goto out;
1122
1123 result = Curl_add_custom_headers(data, FALSE, headers);
1124 if(result)
1125 goto out;
1126
1127 result = finalize_request(data, headers, req, httpreq);
1128 if(result)
1129 goto out;
1130
1131 Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2);
1132
1133 if(data->req.upload_chunky && data->req.authneg) {
1134 data->req.upload_chunky = TRUE;
1135 }
1136 else {
1137 data->req.upload_chunky = FALSE;
1138 }
1139 sendtask = hyper_clientconn_send(client, req);
1140 if(!sendtask) {
1141 failf(data, "hyper_clientconn_send");
1142 result = CURLE_OUT_OF_MEMORY;
1143 goto out;
1144 }
1145 req = NULL;
1146
1147 if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
1148 failf(data, "Couldn't hyper_executor_push the send");
1149 result = CURLE_OUT_OF_MEMORY;
1150 goto out;
1151 }
1152 sendtask = NULL; /* ownership passed on */
1153
1154 hyper_clientconn_free(client);
1155 client = NULL;
1156
1157 if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) {
1158 /* HTTP GET/HEAD download */
1159 Curl_pgrsSetUploadSize(data, 0); /* nothing */
1160 result = Curl_req_set_upload_done(data);
1161 if(result)
1162 goto out;
1163 }
1164
1165 Curl_xfer_setup1(data, CURL_XFER_SENDRECV, -1, TRUE);
1166 conn->datastream = Curl_hyper_stream;
1167
1168 /* clear userpwd and proxyuserpwd to avoid reusing old credentials
1169 * from reused connections */
1170 Curl_safefree(data->state.aptr.userpwd);
1171#ifndef CURL_DISABLE_PROXY
1172 Curl_safefree(data->state.aptr.proxyuserpwd);
1173#endif
1174
1175out:
1176 if(result) {
1177 if(io)
1178 hyper_io_free(io);
1179 if(options)
1180 hyper_clientconn_options_free(options);
1181 if(handshake)
1182 hyper_task_free(handshake);
1183 if(client)
1184 hyper_clientconn_free(client);
1185 if(req)
1186 hyper_request_free(req);
1187 }
1188 return result;
1189}
1190
1191void Curl_hyper_done(struct Curl_easy *data)
1192{
1193 struct hyptransfer *h = &data->hyp;
1194 if(h->exec) {
1195 hyper_executor_free(h->exec);
1196 h->exec = NULL;
1197 }
1198 if(h->read_waker) {
1199 hyper_waker_free(h->read_waker);
1200 h->read_waker = NULL;
1201 }
1202 if(h->write_waker) {
1203 hyper_waker_free(h->write_waker);
1204 h->write_waker = NULL;
1205 }
1206 if(h->send_body_waker) {
1207 hyper_waker_free(h->send_body_waker);
1208 h->send_body_waker = NULL;
1209 }
1210}
1211
1212static CURLcode cr_hyper_unpause(struct Curl_easy *data,
1213 struct Curl_creader *reader)
1214{
1215 (void)reader;
1216 if(data->hyp.send_body_waker) {
1217 hyper_waker_wake(data->hyp.send_body_waker);
1218 data->hyp.send_body_waker = NULL;
1219 }
1220 return CURLE_OK;
1221}
1222
1223/* Hyper client reader, handling unpausing */
1224static const struct Curl_crtype cr_hyper_protocol = {
1225 "cr-hyper",
1226 Curl_creader_def_init,
1227 Curl_creader_def_read,
1228 Curl_creader_def_close,
1229 Curl_creader_def_needs_rewind,
1230 Curl_creader_def_total_length,
1231 Curl_creader_def_resume_from,
1232 Curl_creader_def_rewind,
1233 cr_hyper_unpause,
1234 Curl_creader_def_is_paused,
1235 Curl_creader_def_done,
1236 sizeof(struct Curl_creader)
1237};
1238
1239static CURLcode cr_hyper_add(struct Curl_easy *data)
1240{
1241 struct Curl_creader *reader = NULL;
1242 CURLcode result;
1243
1244 result = Curl_creader_create(&reader, data, &cr_hyper_protocol,
1245 CURL_CR_PROTOCOL);
1246 if(!result)
1247 result = Curl_creader_add(data, reader);
1248
1249 if(result && reader)
1250 Curl_creader_free(data, reader);
1251 return result;
1252}
1253
1254#endif /* !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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