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