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