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