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
71 static CURLcode cr_hyper_add(struct Curl_easy *data);
72
73 typedef enum {
74 USERDATA_NOT_SET = 0, /* for tasks with no userdata set; must be zero */
75 USERDATA_RESP_BODY
76 } userdata_t;
77
Curl_hyper_recv(void * userp,hyper_context * ctx,uint8_t * buf,size_t buflen)78 size_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
Curl_hyper_send(void * userp,hyper_context * ctx,const uint8_t * buf,size_t buflen)112 size_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
hyper_each_header(void * userdata,const uint8_t * name,size_t name_len,const uint8_t * value,size_t value_len)143 static 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
hyper_body_chunk(void * userdata,const hyper_buf * chunk)201 static 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. */
status_line(struct Curl_easy * data,struct connectdata * conn,uint16_t http_status,int http_version,const uint8_t * reason,size_t rlen)261 static 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 */
empty_header(struct Curl_easy * data)320 static 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
Curl_hyper_stream(struct Curl_easy * data,struct connectdata * conn,int * didwhat,int select_res)335 CURLcode 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
debug_request(struct Curl_easy * data,const char * method,const char * path)533 static 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
Curl_hyper_header(struct Curl_easy * data,hyper_headers * headers,const char * line)552 CURLcode 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
request_target(struct Curl_easy * data,struct connectdata * conn,const char * method,hyper_request * req)616 static 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
uploadstreamed(void * userdata,hyper_context * ctx,hyper_buf ** chunk)643 static 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
689 out:
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 */
finalize_request(struct Curl_easy * data,hyper_headers * headers,hyper_request * hyperreq,Curl_HttpReq httpreq)698 static 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
cookies(struct Curl_easy * data,struct connectdata * conn,hyper_headers * headers)737 static 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 */
http1xx_cb(void * arg,struct hyper_response * resp)753 static 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 */
Curl_http(struct Curl_easy * data,bool * done)799 CURLcode 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 #ifndef CURL_DISABLE_PROXY
984 if(data->state.aptr.proxyuserpwd) {
985 result = Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd);
986 if(result)
987 goto error;
988 }
989 #endif
990
991 if(data->state.aptr.userpwd) {
992 result = Curl_hyper_header(data, headers, data->state.aptr.userpwd);
993 if(result)
994 goto error;
995 }
996
997 if((data->state.use_range && data->state.aptr.rangeline)) {
998 result = Curl_hyper_header(data, headers, data->state.aptr.rangeline);
999 if(result)
1000 goto error;
1001 }
1002
1003 if(data->set.str[STRING_USERAGENT] &&
1004 *data->set.str[STRING_USERAGENT] &&
1005 data->state.aptr.uagent) {
1006 result = Curl_hyper_header(data, headers, data->state.aptr.uagent);
1007 if(result)
1008 goto error;
1009 }
1010
1011 p_accept = Curl_checkheaders(data,
1012 STRCONST("Accept"))?NULL:"Accept: */*\r\n";
1013 if(p_accept) {
1014 result = Curl_hyper_header(data, headers, p_accept);
1015 if(result)
1016 goto error;
1017 }
1018 if(te) {
1019 result = Curl_hyper_header(data, headers, te);
1020 if(result)
1021 goto error;
1022 }
1023
1024 #ifndef CURL_DISABLE_ALTSVC
1025 if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) {
1026 char *altused = aprintf("Alt-Used: %s:%d\r\n",
1027 conn->conn_to_host.name, conn->conn_to_port);
1028 if(!altused) {
1029 result = CURLE_OUT_OF_MEMORY;
1030 goto error;
1031 }
1032 result = Curl_hyper_header(data, headers, altused);
1033 if(result)
1034 goto error;
1035 free(altused);
1036 }
1037 #endif
1038
1039 #ifndef CURL_DISABLE_PROXY
1040 if(conn->bits.httpproxy && !conn->bits.tunnel_proxy &&
1041 !Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
1042 !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) {
1043 result = Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive");
1044 if(result)
1045 goto error;
1046 }
1047 #endif
1048
1049 Curl_safefree(data->state.aptr.ref);
1050 if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) {
1051 data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer);
1052 if(!data->state.aptr.ref)
1053 result = CURLE_OUT_OF_MEMORY;
1054 else
1055 result = Curl_hyper_header(data, headers, data->state.aptr.ref);
1056 if(result)
1057 goto error;
1058 }
1059
1060 #ifdef HAVE_LIBZ
1061 /* we only consider transfer-encoding magic if libz support is built-in */
1062 result = Curl_transferencode(data);
1063 if(result)
1064 goto error;
1065 result = Curl_hyper_header(data, headers, data->state.aptr.te);
1066 if(result)
1067 goto error;
1068 #endif
1069
1070 if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
1071 data->set.str[STRING_ENCODING]) {
1072 Curl_safefree(data->state.aptr.accept_encoding);
1073 data->state.aptr.accept_encoding =
1074 aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
1075 if(!data->state.aptr.accept_encoding)
1076 result = CURLE_OUT_OF_MEMORY;
1077 else
1078 result = Curl_hyper_header(data, headers,
1079 data->state.aptr.accept_encoding);
1080 if(result)
1081 goto error;
1082 }
1083 else
1084 Curl_safefree(data->state.aptr.accept_encoding);
1085
1086 result = cookies(data, conn, headers);
1087 if(result)
1088 goto error;
1089
1090 if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
1091 result = Curl_ws_request(data, headers);
1092
1093 result = Curl_add_timecondition(data, headers);
1094 if(result)
1095 goto error;
1096
1097 result = Curl_add_custom_headers(data, FALSE, headers);
1098 if(result)
1099 goto error;
1100
1101 result = finalize_request(data, headers, req, httpreq);
1102 if(result)
1103 goto error;
1104
1105 Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2);
1106
1107 if(data->req.upload_chunky && data->req.authneg) {
1108 data->req.upload_chunky = TRUE;
1109 }
1110 else {
1111 data->req.upload_chunky = FALSE;
1112 }
1113 sendtask = hyper_clientconn_send(client, req);
1114 if(!sendtask) {
1115 failf(data, "hyper_clientconn_send");
1116 result = CURLE_OUT_OF_MEMORY;
1117 goto error;
1118 }
1119 req = NULL;
1120
1121 if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
1122 failf(data, "Couldn't hyper_executor_push the send");
1123 result = CURLE_OUT_OF_MEMORY;
1124 goto error;
1125 }
1126 sendtask = NULL; /* ownership passed on */
1127
1128 hyper_clientconn_free(client);
1129 client = NULL;
1130
1131 if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) {
1132 /* HTTP GET/HEAD download */
1133 Curl_pgrsSetUploadSize(data, 0); /* nothing */
1134 }
1135
1136 Curl_xfer_setup(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET);
1137 conn->datastream = Curl_hyper_stream;
1138
1139 /* clear userpwd and proxyuserpwd to avoid reusing old credentials
1140 * from reused connections */
1141 Curl_safefree(data->state.aptr.userpwd);
1142 #ifndef CURL_DISABLE_PROXY
1143 Curl_safefree(data->state.aptr.proxyuserpwd);
1144 #endif
1145 return CURLE_OK;
1146 error:
1147 DEBUGASSERT(result);
1148 if(io)
1149 hyper_io_free(io);
1150
1151 if(options)
1152 hyper_clientconn_options_free(options);
1153
1154 if(handshake)
1155 hyper_task_free(handshake);
1156
1157 if(client)
1158 hyper_clientconn_free(client);
1159
1160 if(req)
1161 hyper_request_free(req);
1162
1163 return result;
1164 }
1165
Curl_hyper_done(struct Curl_easy * data)1166 void Curl_hyper_done(struct Curl_easy *data)
1167 {
1168 struct hyptransfer *h = &data->hyp;
1169 if(h->exec) {
1170 hyper_executor_free(h->exec);
1171 h->exec = NULL;
1172 }
1173 if(h->read_waker) {
1174 hyper_waker_free(h->read_waker);
1175 h->read_waker = NULL;
1176 }
1177 if(h->write_waker) {
1178 hyper_waker_free(h->write_waker);
1179 h->write_waker = NULL;
1180 }
1181 if(h->send_body_waker) {
1182 hyper_waker_free(h->send_body_waker);
1183 h->send_body_waker = NULL;
1184 }
1185 }
1186
cr_hyper_unpause(struct Curl_easy * data,struct Curl_creader * reader)1187 static CURLcode cr_hyper_unpause(struct Curl_easy *data,
1188 struct Curl_creader *reader)
1189 {
1190 (void)reader;
1191 if(data->hyp.send_body_waker) {
1192 hyper_waker_wake(data->hyp.send_body_waker);
1193 data->hyp.send_body_waker = NULL;
1194 }
1195 return CURLE_OK;
1196 }
1197
1198 /* Hyper client reader, handling unpausing */
1199 static const struct Curl_crtype cr_hyper_protocol = {
1200 "cr-hyper",
1201 Curl_creader_def_init,
1202 Curl_creader_def_read,
1203 Curl_creader_def_close,
1204 Curl_creader_def_needs_rewind,
1205 Curl_creader_def_total_length,
1206 Curl_creader_def_resume_from,
1207 Curl_creader_def_rewind,
1208 cr_hyper_unpause,
1209 Curl_creader_def_done,
1210 sizeof(struct Curl_creader)
1211 };
1212
cr_hyper_add(struct Curl_easy * data)1213 static CURLcode cr_hyper_add(struct Curl_easy *data)
1214 {
1215 struct Curl_creader *reader = NULL;
1216 CURLcode result;
1217
1218 result = Curl_creader_create(&reader, data, &cr_hyper_protocol,
1219 CURL_CR_PROTOCOL);
1220 if(!result)
1221 result = Curl_creader_add(data, reader);
1222
1223 if(result && reader)
1224 Curl_creader_free(data, reader);
1225 return result;
1226 }
1227
1228 #endif /* !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) */
1229