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