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