• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2019, 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  ***************************************************************************/
22 
23 #include "curl_setup.h"
24 
25 #ifdef USE_NGHTTP2
26 #include <nghttp2/nghttp2.h>
27 #include "urldata.h"
28 #include "http2.h"
29 #include "http.h"
30 #include "sendf.h"
31 #include "select.h"
32 #include "curl_base64.h"
33 #include "strcase.h"
34 #include "multiif.h"
35 #include "url.h"
36 #include "connect.h"
37 #include "strtoofft.h"
38 #include "strdup.h"
39 /* The last 3 #include files should be in this order */
40 #include "curl_printf.h"
41 #include "curl_memory.h"
42 #include "memdebug.h"
43 
44 #define H2_BUFSIZE 32768
45 
46 #if (NGHTTP2_VERSION_NUM < 0x010000)
47 #error too old nghttp2 version, upgrade!
48 #endif
49 
50 #if (NGHTTP2_VERSION_NUM > 0x010800)
51 #define NGHTTP2_HAS_HTTP2_STRERROR 1
52 #endif
53 
54 #if (NGHTTP2_VERSION_NUM >= 0x010900)
55 /* nghttp2_session_callbacks_set_error_callback is present in nghttp2 1.9.0 or
56    later */
57 #define NGHTTP2_HAS_ERROR_CALLBACK 1
58 #else
59 #define nghttp2_session_callbacks_set_error_callback(x,y)
60 #endif
61 
62 #if (NGHTTP2_VERSION_NUM >= 0x010c00)
63 #define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1
64 #endif
65 
66 #define HTTP2_HUGE_WINDOW_SIZE (1 << 30)
67 
68 #ifdef DEBUG_HTTP2
69 #define H2BUGF(x) x
70 #else
71 #define H2BUGF(x) do { } WHILE_FALSE
72 #endif
73 
74 
75 static ssize_t http2_recv(struct connectdata *conn, int sockindex,
76                           char *mem, size_t len, CURLcode *err);
77 static bool http2_connisdead(struct connectdata *conn);
78 static int h2_session_send(struct Curl_easy *data,
79                            nghttp2_session *h2);
80 static int h2_process_pending_input(struct connectdata *conn,
81                                     struct http_conn *httpc,
82                                     CURLcode *err);
83 
84 /*
85  * Curl_http2_init_state() is called when the easy handle is created and
86  * allows for HTTP/2 specific init of state.
87  */
Curl_http2_init_state(struct UrlState * state)88 void Curl_http2_init_state(struct UrlState *state)
89 {
90   state->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
91 }
92 
93 /*
94  * Curl_http2_init_userset() is called when the easy handle is created and
95  * allows for HTTP/2 specific user-set fields.
96  */
Curl_http2_init_userset(struct UserDefined * set)97 void Curl_http2_init_userset(struct UserDefined *set)
98 {
99   set->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
100 }
101 
http2_perform_getsock(const struct connectdata * conn,curl_socket_t * sock,int numsocks)102 static int http2_perform_getsock(const struct connectdata *conn,
103                                  curl_socket_t *sock, /* points to
104                                                          numsocks
105                                                          number of
106                                                          sockets */
107                                  int numsocks)
108 {
109   const struct http_conn *c = &conn->proto.httpc;
110   struct SingleRequest *k = &conn->data->req;
111   int bitmap = GETSOCK_BLANK;
112   (void)numsocks;
113 
114   /* TODO We should check underlying socket state if it is SSL socket
115      because of renegotiation. */
116   sock[0] = conn->sock[FIRSTSOCKET];
117 
118   /* in a HTTP/2 connection we can basically always get a frame so we should
119      always be ready for one */
120   bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
121 
122   /* we're still uploading or the HTTP/2 layer wants to send data */
123   if(((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) ||
124      nghttp2_session_want_write(c->h2))
125     bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
126 
127   return bitmap;
128 }
129 
http2_getsock(struct connectdata * conn,curl_socket_t * sock,int numsocks)130 static int http2_getsock(struct connectdata *conn,
131                          curl_socket_t *sock, /* points to numsocks
132                                                  number of sockets */
133                          int numsocks)
134 {
135   return http2_perform_getsock(conn, sock, numsocks);
136 }
137 
138 /*
139  * http2_stream_free() free HTTP2 stream related data
140  */
http2_stream_free(struct HTTP * http)141 static void http2_stream_free(struct HTTP *http)
142 {
143   if(http) {
144     Curl_add_buffer_free(&http->header_recvbuf);
145     Curl_add_buffer_free(&http->trailer_recvbuf);
146     for(; http->push_headers_used > 0; --http->push_headers_used) {
147       free(http->push_headers[http->push_headers_used - 1]);
148     }
149     free(http->push_headers);
150     http->push_headers = NULL;
151   }
152 }
153 
154 /*
155  * Disconnects *a* connection used for HTTP/2. It might be an old one from the
156  * connection cache and not the "main" one. Don't touch the easy handle!
157  */
158 
http2_disconnect(struct connectdata * conn,bool dead_connection)159 static CURLcode http2_disconnect(struct connectdata *conn,
160                                  bool dead_connection)
161 {
162   struct http_conn *c = &conn->proto.httpc;
163   (void)dead_connection;
164 
165   H2BUGF(infof(conn->data, "HTTP/2 DISCONNECT starts now\n"));
166 
167   nghttp2_session_del(c->h2);
168   Curl_safefree(c->inbuf);
169 
170   H2BUGF(infof(conn->data, "HTTP/2 DISCONNECT done\n"));
171 
172   return CURLE_OK;
173 }
174 
175 /*
176  * The server may send us data at any point (e.g. PING frames). Therefore,
177  * we cannot assume that an HTTP/2 socket is dead just because it is readable.
178  *
179  * Instead, if it is readable, run Curl_connalive() to peek at the socket
180  * and distinguish between closed and data.
181  */
http2_connisdead(struct connectdata * conn)182 static bool http2_connisdead(struct connectdata *conn)
183 {
184   int sval;
185   bool dead = TRUE;
186 
187   if(conn->bits.close)
188     return TRUE;
189 
190   sval = SOCKET_READABLE(conn->sock[FIRSTSOCKET], 0);
191   if(sval == 0) {
192     /* timeout */
193     dead = FALSE;
194   }
195   else if(sval & CURL_CSELECT_ERR) {
196     /* socket is in an error state */
197     dead = TRUE;
198   }
199   else if(sval & CURL_CSELECT_IN) {
200     /* readable with no error. could still be closed */
201     dead = !Curl_connalive(conn);
202     if(!dead) {
203       /* This happens before we've sent off a request and the connection is
204          not in use by any other transfer, there shouldn't be any data here,
205          only "protocol frames" */
206       CURLcode result;
207       struct http_conn *httpc = &conn->proto.httpc;
208       ssize_t nread = -1;
209       if(httpc->recv_underlying)
210         /* if called "too early", this pointer isn't setup yet! */
211         nread = ((Curl_recv *)httpc->recv_underlying)(
212           conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
213       if(nread != -1) {
214         infof(conn->data,
215               "%d bytes stray data read before trying h2 connection\n",
216               (int)nread);
217         httpc->nread_inbuf = 0;
218         httpc->inbuflen = nread;
219         (void)h2_process_pending_input(conn, httpc, &result);
220       }
221       else
222         /* the read failed so let's say this is dead anyway */
223         dead = TRUE;
224     }
225   }
226 
227   return dead;
228 }
229 
http2_conncheck(struct connectdata * check,unsigned int checks_to_perform)230 static unsigned int http2_conncheck(struct connectdata *check,
231                                     unsigned int checks_to_perform)
232 {
233   unsigned int ret_val = CONNRESULT_NONE;
234   struct http_conn *c = &check->proto.httpc;
235   int rc;
236   bool send_frames = false;
237 
238   if(checks_to_perform & CONNCHECK_ISDEAD) {
239     if(http2_connisdead(check))
240       ret_val |= CONNRESULT_DEAD;
241   }
242 
243   if(checks_to_perform & CONNCHECK_KEEPALIVE) {
244     struct curltime now = Curl_now();
245     time_t elapsed = Curl_timediff(now, check->keepalive);
246 
247     if(elapsed > check->upkeep_interval_ms) {
248       /* Perform an HTTP/2 PING */
249       rc = nghttp2_submit_ping(c->h2, 0, ZERO_NULL);
250       if(!rc) {
251         /* Successfully added a PING frame to the session. Need to flag this
252            so the frame is sent. */
253         send_frames = true;
254       }
255       else {
256        failf(check->data, "nghttp2_submit_ping() failed: %s(%d)",
257              nghttp2_strerror(rc), rc);
258       }
259 
260       check->keepalive = now;
261     }
262   }
263 
264   if(send_frames) {
265     rc = nghttp2_session_send(c->h2);
266     if(rc)
267       failf(check->data, "nghttp2_session_send() failed: %s(%d)",
268             nghttp2_strerror(rc), rc);
269   }
270 
271   return ret_val;
272 }
273 
274 /* called from Curl_http_setup_conn */
Curl_http2_setup_req(struct Curl_easy * data)275 void Curl_http2_setup_req(struct Curl_easy *data)
276 {
277   struct HTTP *http = data->req.protop;
278 
279   http->nread_header_recvbuf = 0;
280   http->bodystarted = FALSE;
281   http->status_code = -1;
282   http->pausedata = NULL;
283   http->pauselen = 0;
284   http->closed = FALSE;
285   http->close_handled = FALSE;
286   http->mem = data->state.buffer;
287   http->len = data->set.buffer_size;
288   http->memlen = 0;
289 }
290 
291 /* called from Curl_http_setup_conn */
Curl_http2_setup_conn(struct connectdata * conn)292 void Curl_http2_setup_conn(struct connectdata *conn)
293 {
294   conn->proto.httpc.settings.max_concurrent_streams =
295     DEFAULT_MAX_CONCURRENT_STREAMS;
296   conn->proto.httpc.error_code = NGHTTP2_NO_ERROR;
297 }
298 
299 /*
300  * HTTP2 handler interface. This isn't added to the general list of protocols
301  * but will be used at run-time when the protocol is dynamically switched from
302  * HTTP to HTTP2.
303  */
304 static const struct Curl_handler Curl_handler_http2 = {
305   "HTTP",                               /* scheme */
306   ZERO_NULL,                            /* setup_connection */
307   Curl_http,                            /* do_it */
308   Curl_http_done,                       /* done */
309   ZERO_NULL,                            /* do_more */
310   ZERO_NULL,                            /* connect_it */
311   ZERO_NULL,                            /* connecting */
312   ZERO_NULL,                            /* doing */
313   http2_getsock,                        /* proto_getsock */
314   http2_getsock,                        /* doing_getsock */
315   ZERO_NULL,                            /* domore_getsock */
316   http2_perform_getsock,                /* perform_getsock */
317   http2_disconnect,                     /* disconnect */
318   ZERO_NULL,                            /* readwrite */
319   http2_conncheck,                      /* connection_check */
320   PORT_HTTP,                            /* defport */
321   CURLPROTO_HTTP,                       /* protocol */
322   PROTOPT_STREAM                        /* flags */
323 };
324 
325 static const struct Curl_handler Curl_handler_http2_ssl = {
326   "HTTPS",                              /* scheme */
327   ZERO_NULL,                            /* setup_connection */
328   Curl_http,                            /* do_it */
329   Curl_http_done,                       /* done */
330   ZERO_NULL,                            /* do_more */
331   ZERO_NULL,                            /* connect_it */
332   ZERO_NULL,                            /* connecting */
333   ZERO_NULL,                            /* doing */
334   http2_getsock,                        /* proto_getsock */
335   http2_getsock,                        /* doing_getsock */
336   ZERO_NULL,                            /* domore_getsock */
337   http2_perform_getsock,                /* perform_getsock */
338   http2_disconnect,                     /* disconnect */
339   ZERO_NULL,                            /* readwrite */
340   http2_conncheck,                      /* connection_check */
341   PORT_HTTP,                            /* defport */
342   CURLPROTO_HTTPS,                      /* protocol */
343   PROTOPT_SSL | PROTOPT_STREAM          /* flags */
344 };
345 
346 /*
347  * Store nghttp2 version info in this buffer, Prefix with a space.  Return
348  * total length written.
349  */
Curl_http2_ver(char * p,size_t len)350 int Curl_http2_ver(char *p, size_t len)
351 {
352   nghttp2_info *h2 = nghttp2_version(0);
353   return msnprintf(p, len, " nghttp2/%s", h2->version_str);
354 }
355 
356 /* HTTP/2 error code to name based on the Error Code Registry.
357 https://tools.ietf.org/html/rfc7540#page-77
358 nghttp2_error_code enums are identical.
359 */
http2_strerror(uint32_t err)360 static const char *http2_strerror(uint32_t err)
361 {
362 #ifndef NGHTTP2_HAS_HTTP2_STRERROR
363   const char *str[] = {
364     "NO_ERROR",             /* 0x0 */
365     "PROTOCOL_ERROR",       /* 0x1 */
366     "INTERNAL_ERROR",       /* 0x2 */
367     "FLOW_CONTROL_ERROR",   /* 0x3 */
368     "SETTINGS_TIMEOUT",     /* 0x4 */
369     "STREAM_CLOSED",        /* 0x5 */
370     "FRAME_SIZE_ERROR",     /* 0x6 */
371     "REFUSED_STREAM",       /* 0x7 */
372     "CANCEL",               /* 0x8 */
373     "COMPRESSION_ERROR",    /* 0x9 */
374     "CONNECT_ERROR",        /* 0xA */
375     "ENHANCE_YOUR_CALM",    /* 0xB */
376     "INADEQUATE_SECURITY",  /* 0xC */
377     "HTTP_1_1_REQUIRED"     /* 0xD */
378   };
379   return (err < sizeof(str) / sizeof(str[0])) ? str[err] : "unknown";
380 #else
381   return nghttp2_http2_strerror(err);
382 #endif
383 }
384 
385 /*
386  * The implementation of nghttp2_send_callback type. Here we write |data| with
387  * size |length| to the network and return the number of bytes actually
388  * written. See the documentation of nghttp2_send_callback for the details.
389  */
send_callback(nghttp2_session * h2,const uint8_t * data,size_t length,int flags,void * userp)390 static ssize_t send_callback(nghttp2_session *h2,
391                              const uint8_t *data, size_t length, int flags,
392                              void *userp)
393 {
394   struct connectdata *conn = (struct connectdata *)userp;
395   struct http_conn *c = &conn->proto.httpc;
396   ssize_t written;
397   CURLcode result = CURLE_OK;
398 
399   (void)h2;
400   (void)flags;
401 
402   if(!c->send_underlying)
403     /* called before setup properly! */
404     return NGHTTP2_ERR_CALLBACK_FAILURE;
405 
406   written = ((Curl_send*)c->send_underlying)(conn, FIRSTSOCKET,
407                                              data, length, &result);
408 
409   if(result == CURLE_AGAIN) {
410     return NGHTTP2_ERR_WOULDBLOCK;
411   }
412 
413   if(written == -1) {
414     failf(conn->data, "Failed sending HTTP2 data");
415     return NGHTTP2_ERR_CALLBACK_FAILURE;
416   }
417 
418   if(!written)
419     return NGHTTP2_ERR_WOULDBLOCK;
420 
421   return written;
422 }
423 
424 
425 /* We pass a pointer to this struct in the push callback, but the contents of
426    the struct are hidden from the user. */
427 struct curl_pushheaders {
428   struct Curl_easy *data;
429   const nghttp2_push_promise *frame;
430 };
431 
432 /*
433  * push header access function. Only to be used from within the push callback
434  */
curl_pushheader_bynum(struct curl_pushheaders * h,size_t num)435 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
436 {
437   /* Verify that we got a good easy handle in the push header struct, mostly to
438      detect rubbish input fast(er). */
439   if(!h || !GOOD_EASY_HANDLE(h->data))
440     return NULL;
441   else {
442     struct HTTP *stream = h->data->req.protop;
443     if(num < stream->push_headers_used)
444       return stream->push_headers[num];
445   }
446   return NULL;
447 }
448 
449 /*
450  * push header access function. Only to be used from within the push callback
451  */
curl_pushheader_byname(struct curl_pushheaders * h,const char * header)452 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
453 {
454   /* Verify that we got a good easy handle in the push header struct,
455      mostly to detect rubbish input fast(er). Also empty header name
456      is just a rubbish too. We have to allow ":" at the beginning of
457      the header, but header == ":" must be rejected. If we have ':' in
458      the middle of header, it could be matched in middle of the value,
459      this is because we do prefix match.*/
460   if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] ||
461      !strcmp(header, ":") || strchr(header + 1, ':'))
462     return NULL;
463   else {
464     struct HTTP *stream = h->data->req.protop;
465     size_t len = strlen(header);
466     size_t i;
467     for(i = 0; i<stream->push_headers_used; i++) {
468       if(!strncmp(header, stream->push_headers[i], len)) {
469         /* sub-match, make sure that it is followed by a colon */
470         if(stream->push_headers[i][len] != ':')
471           continue;
472         return &stream->push_headers[i][len + 1];
473       }
474     }
475   }
476   return NULL;
477 }
478 
479 /*
480  * This specific transfer on this connection has been "drained".
481  */
drained_transfer(struct Curl_easy * data,struct http_conn * httpc)482 static void drained_transfer(struct Curl_easy *data,
483                              struct http_conn *httpc)
484 {
485   DEBUGASSERT(httpc->drain_total >= data->state.drain);
486   httpc->drain_total -= data->state.drain;
487   data->state.drain = 0;
488 }
489 
490 /*
491  * Mark this transfer to get "drained".
492  */
drain_this(struct Curl_easy * data,struct http_conn * httpc)493 static void drain_this(struct Curl_easy *data,
494                        struct http_conn *httpc)
495 {
496   data->state.drain++;
497   httpc->drain_total++;
498   DEBUGASSERT(httpc->drain_total >= data->state.drain);
499 }
500 
duphandle(struct Curl_easy * data)501 static struct Curl_easy *duphandle(struct Curl_easy *data)
502 {
503   struct Curl_easy *second = curl_easy_duphandle(data);
504   if(second) {
505     /* setup the request struct */
506     struct HTTP *http = calloc(1, sizeof(struct HTTP));
507     if(!http) {
508       (void)Curl_close(second);
509       second = NULL;
510     }
511     else {
512       second->req.protop = http;
513       http->header_recvbuf = Curl_add_buffer_init();
514       if(!http->header_recvbuf) {
515         free(http);
516         (void)Curl_close(second);
517         second = NULL;
518       }
519       else {
520         Curl_http2_setup_req(second);
521         second->state.stream_weight = data->state.stream_weight;
522       }
523     }
524   }
525   return second;
526 }
527 
528 
push_promise(struct Curl_easy * data,struct connectdata * conn,const nghttp2_push_promise * frame)529 static int push_promise(struct Curl_easy *data,
530                         struct connectdata *conn,
531                         const nghttp2_push_promise *frame)
532 {
533   int rv;
534   H2BUGF(infof(data, "PUSH_PROMISE received, stream %u!\n",
535                frame->promised_stream_id));
536   if(data->multi->push_cb) {
537     struct HTTP *stream;
538     struct HTTP *newstream;
539     struct curl_pushheaders heads;
540     CURLMcode rc;
541     struct http_conn *httpc;
542     size_t i;
543     /* clone the parent */
544     struct Curl_easy *newhandle = duphandle(data);
545     if(!newhandle) {
546       infof(data, "failed to duplicate handle\n");
547       rv = 1; /* FAIL HARD */
548       goto fail;
549     }
550 
551     heads.data = data;
552     heads.frame = frame;
553     /* ask the application */
554     H2BUGF(infof(data, "Got PUSH_PROMISE, ask application!\n"));
555 
556     stream = data->req.protop;
557     if(!stream) {
558       failf(data, "Internal NULL stream!\n");
559       (void)Curl_close(newhandle);
560       rv = 1;
561       goto fail;
562     }
563 
564     Curl_set_in_callback(data, true);
565     rv = data->multi->push_cb(data, newhandle,
566                               stream->push_headers_used, &heads,
567                               data->multi->push_userp);
568     Curl_set_in_callback(data, false);
569 
570     /* free the headers again */
571     for(i = 0; i<stream->push_headers_used; i++)
572       free(stream->push_headers[i]);
573     free(stream->push_headers);
574     stream->push_headers = NULL;
575     stream->push_headers_used = 0;
576 
577     if(rv) {
578       /* denied, kill off the new handle again */
579       http2_stream_free(newhandle->req.protop);
580       newhandle->req.protop = NULL;
581       (void)Curl_close(newhandle);
582       goto fail;
583     }
584 
585     newstream = newhandle->req.protop;
586     newstream->stream_id = frame->promised_stream_id;
587     newhandle->req.maxdownload = -1;
588     newhandle->req.size = -1;
589 
590     /* approved, add to the multi handle and immediately switch to PERFORM
591        state with the given connection !*/
592     rc = Curl_multi_add_perform(data->multi, newhandle, conn);
593     if(rc) {
594       infof(data, "failed to add handle to multi\n");
595       http2_stream_free(newhandle->req.protop);
596       newhandle->req.protop = NULL;
597       Curl_close(newhandle);
598       rv = 1;
599       goto fail;
600     }
601 
602     httpc = &conn->proto.httpc;
603     rv = nghttp2_session_set_stream_user_data(httpc->h2,
604                                               frame->promised_stream_id,
605                                               newhandle);
606     if(rv) {
607       infof(data, "failed to set user_data for stream %d\n",
608             frame->promised_stream_id);
609       DEBUGASSERT(0);
610       goto fail;
611     }
612   }
613   else {
614     H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n"));
615     rv = 1;
616   }
617   fail:
618   return rv;
619 }
620 
621 /*
622  * multi_connchanged() is called to tell that there is a connection in
623  * this multi handle that has changed state (pipelining become possible, the
624  * number of allowed streams changed or similar), and a subsequent use of this
625  * multi handle should move CONNECT_PEND handles back to CONNECT to have them
626  * retry.
627  */
multi_connchanged(struct Curl_multi * multi)628 static void multi_connchanged(struct Curl_multi *multi)
629 {
630   multi->recheckstate = TRUE;
631 }
632 
on_frame_recv(nghttp2_session * session,const nghttp2_frame * frame,void * userp)633 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
634                          void *userp)
635 {
636   struct connectdata *conn = (struct connectdata *)userp;
637   struct http_conn *httpc = &conn->proto.httpc;
638   struct Curl_easy *data_s = NULL;
639   struct HTTP *stream = NULL;
640   int rv;
641   size_t left, ncopy;
642   int32_t stream_id = frame->hd.stream_id;
643   CURLcode result;
644 
645   if(!stream_id) {
646     /* stream ID zero is for connection-oriented stuff */
647     if(frame->hd.type == NGHTTP2_SETTINGS) {
648       uint32_t max_conn = httpc->settings.max_concurrent_streams;
649       H2BUGF(infof(conn->data, "Got SETTINGS\n"));
650       httpc->settings.max_concurrent_streams =
651         nghttp2_session_get_remote_settings(
652           session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
653       httpc->settings.enable_push =
654         nghttp2_session_get_remote_settings(
655           session, NGHTTP2_SETTINGS_ENABLE_PUSH);
656       H2BUGF(infof(conn->data, "MAX_CONCURRENT_STREAMS == %d\n",
657                    httpc->settings.max_concurrent_streams));
658       H2BUGF(infof(conn->data, "ENABLE_PUSH == %s\n",
659                    httpc->settings.enable_push?"TRUE":"false"));
660       if(max_conn != httpc->settings.max_concurrent_streams) {
661         /* only signal change if the value actually changed */
662         infof(conn->data,
663               "Connection state changed (MAX_CONCURRENT_STREAMS == %u)!\n",
664               httpc->settings.max_concurrent_streams);
665         multi_connchanged(conn->data->multi);
666       }
667     }
668     return 0;
669   }
670   data_s = nghttp2_session_get_stream_user_data(session, stream_id);
671   if(!data_s) {
672     H2BUGF(infof(conn->data,
673                  "No Curl_easy associated with stream: %x\n",
674                  stream_id));
675     return 0;
676   }
677 
678   stream = data_s->req.protop;
679   if(!stream) {
680     H2BUGF(infof(data_s, "No proto pointer for stream: %x\n",
681                  stream_id));
682     return NGHTTP2_ERR_CALLBACK_FAILURE;
683   }
684 
685   H2BUGF(infof(data_s, "on_frame_recv() header %x stream %x\n",
686                frame->hd.type, stream_id));
687 
688   switch(frame->hd.type) {
689   case NGHTTP2_DATA:
690     /* If body started on this stream, then receiving DATA is illegal. */
691     if(!stream->bodystarted) {
692       rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
693                                      stream_id, NGHTTP2_PROTOCOL_ERROR);
694 
695       if(nghttp2_is_fatal(rv)) {
696         return NGHTTP2_ERR_CALLBACK_FAILURE;
697       }
698     }
699     break;
700   case NGHTTP2_HEADERS:
701     if(stream->bodystarted) {
702       /* Only valid HEADERS after body started is trailer HEADERS.  We
703          buffer them in on_header callback. */
704       break;
705     }
706 
707     /* nghttp2 guarantees that :status is received, and we store it to
708        stream->status_code. Fuzzing has proven this can still be reached
709        without status code having been set. */
710     if(stream->status_code == -1)
711       return NGHTTP2_ERR_CALLBACK_FAILURE;
712 
713     /* Only final status code signals the end of header */
714     if(stream->status_code / 100 != 1) {
715       stream->bodystarted = TRUE;
716       stream->status_code = -1;
717     }
718 
719     result = Curl_add_buffer(&stream->header_recvbuf, "\r\n", 2);
720     if(result)
721       return NGHTTP2_ERR_CALLBACK_FAILURE;
722 
723     left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
724     ncopy = CURLMIN(stream->len, left);
725 
726     memcpy(&stream->mem[stream->memlen],
727            stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
728            ncopy);
729     stream->nread_header_recvbuf += ncopy;
730 
731     H2BUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n",
732                  ncopy, stream_id, stream->mem));
733 
734     stream->len -= ncopy;
735     stream->memlen += ncopy;
736 
737     drain_this(data_s, httpc);
738     {
739       /* get the pointer from userp again since it was re-assigned above */
740       struct connectdata *conn_s = (struct connectdata *)userp;
741 
742       /* if we receive data for another handle, wake that up */
743       if(conn_s->data != data_s)
744         Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
745     }
746     break;
747   case NGHTTP2_PUSH_PROMISE:
748     rv = push_promise(data_s, conn, &frame->push_promise);
749     if(rv) { /* deny! */
750       rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
751                                      frame->push_promise.promised_stream_id,
752                                      NGHTTP2_CANCEL);
753       if(nghttp2_is_fatal(rv)) {
754         return rv;
755       }
756     }
757     break;
758   default:
759     H2BUGF(infof(data_s, "Got frame type %x for stream %u!\n",
760                  frame->hd.type, stream_id));
761     break;
762   }
763   return 0;
764 }
765 
on_data_chunk_recv(nghttp2_session * session,uint8_t flags,int32_t stream_id,const uint8_t * data,size_t len,void * userp)766 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
767                               int32_t stream_id,
768                               const uint8_t *data, size_t len, void *userp)
769 {
770   struct HTTP *stream;
771   struct Curl_easy *data_s;
772   size_t nread;
773   struct connectdata *conn = (struct connectdata *)userp;
774   (void)session;
775   (void)flags;
776   (void)data;
777 
778   DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
779 
780   /* get the stream from the hash based on Stream ID */
781   data_s = nghttp2_session_get_stream_user_data(session, stream_id);
782   if(!data_s)
783     /* Receiving a Stream ID not in the hash should not happen, this is an
784        internal error more than anything else! */
785     return NGHTTP2_ERR_CALLBACK_FAILURE;
786 
787   stream = data_s->req.protop;
788   if(!stream)
789     return NGHTTP2_ERR_CALLBACK_FAILURE;
790 
791   nread = CURLMIN(stream->len, len);
792   memcpy(&stream->mem[stream->memlen], data, nread);
793 
794   stream->len -= nread;
795   stream->memlen += nread;
796 
797   drain_this(data_s, &conn->proto.httpc);
798 
799   /* if we receive data for another handle, wake that up */
800   if(conn->data != data_s)
801     Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
802 
803   H2BUGF(infof(data_s, "%zu data received for stream %u "
804                "(%zu left in buffer %p, total %zu)\n",
805                nread, stream_id,
806                stream->len, stream->mem,
807                stream->memlen));
808 
809   if(nread < len) {
810     stream->pausedata = data + nread;
811     stream->pauselen = len - nread;
812     H2BUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
813                  ", stream %u\n",
814                  len - nread, stream_id));
815     data_s->conn->proto.httpc.pause_stream_id = stream_id;
816 
817     return NGHTTP2_ERR_PAUSE;
818   }
819 
820   /* pause execution of nghttp2 if we received data for another handle
821      in order to process them first. */
822   if(conn->data != data_s) {
823     data_s->conn->proto.httpc.pause_stream_id = stream_id;
824 
825     return NGHTTP2_ERR_PAUSE;
826   }
827 
828   return 0;
829 }
830 
on_stream_close(nghttp2_session * session,int32_t stream_id,uint32_t error_code,void * userp)831 static int on_stream_close(nghttp2_session *session, int32_t stream_id,
832                            uint32_t error_code, void *userp)
833 {
834   struct Curl_easy *data_s;
835   struct HTTP *stream;
836   struct connectdata *conn = (struct connectdata *)userp;
837   int rv;
838   (void)session;
839   (void)stream_id;
840 
841   if(stream_id) {
842     struct http_conn *httpc;
843     /* get the stream from the hash based on Stream ID, stream ID zero is for
844        connection-oriented stuff */
845     data_s = nghttp2_session_get_stream_user_data(session, stream_id);
846     if(!data_s) {
847       /* We could get stream ID not in the hash.  For example, if we
848          decided to reject stream (e.g., PUSH_PROMISE). */
849       return 0;
850     }
851     H2BUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u\n",
852                  http2_strerror(error_code), error_code, stream_id));
853     stream = data_s->req.protop;
854     if(!stream)
855       return NGHTTP2_ERR_CALLBACK_FAILURE;
856 
857     stream->closed = TRUE;
858     httpc = &conn->proto.httpc;
859     drain_this(data_s, httpc);
860     httpc->error_code = error_code;
861 
862     /* remove the entry from the hash as the stream is now gone */
863     rv = nghttp2_session_set_stream_user_data(session, stream_id, 0);
864     if(rv) {
865       infof(data_s, "http/2: failed to clear user_data for stream %d!\n",
866             stream_id);
867       DEBUGASSERT(0);
868     }
869     if(stream_id == httpc->pause_stream_id) {
870       H2BUGF(infof(data_s, "Stopped the pause stream!\n"));
871       httpc->pause_stream_id = 0;
872     }
873     H2BUGF(infof(data_s, "Removed stream %u hash!\n", stream_id));
874     stream->stream_id = 0; /* cleared */
875   }
876   return 0;
877 }
878 
on_begin_headers(nghttp2_session * session,const nghttp2_frame * frame,void * userp)879 static int on_begin_headers(nghttp2_session *session,
880                             const nghttp2_frame *frame, void *userp)
881 {
882   struct HTTP *stream;
883   struct Curl_easy *data_s = NULL;
884   (void)userp;
885 
886   data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
887   if(!data_s) {
888     return 0;
889   }
890 
891   H2BUGF(infof(data_s, "on_begin_headers() was called\n"));
892 
893   if(frame->hd.type != NGHTTP2_HEADERS) {
894     return 0;
895   }
896 
897   stream = data_s->req.protop;
898   if(!stream || !stream->bodystarted) {
899     return 0;
900   }
901 
902   if(!stream->trailer_recvbuf) {
903     stream->trailer_recvbuf = Curl_add_buffer_init();
904     if(!stream->trailer_recvbuf) {
905       return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
906     }
907   }
908   return 0;
909 }
910 
911 /* Decode HTTP status code.  Returns -1 if no valid status code was
912    decoded. */
decode_status_code(const uint8_t * value,size_t len)913 static int decode_status_code(const uint8_t *value, size_t len)
914 {
915   int i;
916   int res;
917 
918   if(len != 3) {
919     return -1;
920   }
921 
922   res = 0;
923 
924   for(i = 0; i < 3; ++i) {
925     char c = value[i];
926 
927     if(c < '0' || c > '9') {
928       return -1;
929     }
930 
931     res *= 10;
932     res += c - '0';
933   }
934 
935   return res;
936 }
937 
938 /* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
on_header(nghttp2_session * session,const nghttp2_frame * frame,const uint8_t * name,size_t namelen,const uint8_t * value,size_t valuelen,uint8_t flags,void * userp)939 static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
940                      const uint8_t *name, size_t namelen,
941                      const uint8_t *value, size_t valuelen,
942                      uint8_t flags,
943                      void *userp)
944 {
945   struct HTTP *stream;
946   struct Curl_easy *data_s;
947   int32_t stream_id = frame->hd.stream_id;
948   struct connectdata *conn = (struct connectdata *)userp;
949   CURLcode result;
950   (void)flags;
951 
952   DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
953 
954   /* get the stream from the hash based on Stream ID */
955   data_s = nghttp2_session_get_stream_user_data(session, stream_id);
956   if(!data_s)
957     /* Receiving a Stream ID not in the hash should not happen, this is an
958        internal error more than anything else! */
959     return NGHTTP2_ERR_CALLBACK_FAILURE;
960 
961   stream = data_s->req.protop;
962   if(!stream) {
963     failf(data_s, "Internal NULL stream! 5\n");
964     return NGHTTP2_ERR_CALLBACK_FAILURE;
965   }
966 
967   /* Store received PUSH_PROMISE headers to be used when the subsequent
968      PUSH_PROMISE callback comes */
969   if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
970     char *h;
971 
972     if(!strcmp(":authority", (const char *)name)) {
973       /* psuedo headers are lower case */
974       int rc = 0;
975       char *check = aprintf("%s:%d", conn->host.name, conn->remote_port);
976       if(!check)
977         /* no memory */
978         return NGHTTP2_ERR_CALLBACK_FAILURE;
979       if(!Curl_strcasecompare(check, (const char *)value)) {
980         /* This is push is not for the same authority that was asked for in
981          * the URL. RFC 7540 section 8.2 says: "A client MUST treat a
982          * PUSH_PROMISE for which the server is not authoritative as a stream
983          * error of type PROTOCOL_ERROR."
984          */
985         (void)nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
986                                         stream_id, NGHTTP2_PROTOCOL_ERROR);
987         rc = NGHTTP2_ERR_CALLBACK_FAILURE;
988       }
989       free(check);
990       if(rc)
991         return rc;
992     }
993 
994     if(!stream->push_headers) {
995       stream->push_headers_alloc = 10;
996       stream->push_headers = malloc(stream->push_headers_alloc *
997                                     sizeof(char *));
998       if(!stream->push_headers)
999         return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1000       stream->push_headers_used = 0;
1001     }
1002     else if(stream->push_headers_used ==
1003             stream->push_headers_alloc) {
1004       char **headp;
1005       stream->push_headers_alloc *= 2;
1006       headp = Curl_saferealloc(stream->push_headers,
1007                                stream->push_headers_alloc * sizeof(char *));
1008       if(!headp) {
1009         stream->push_headers = NULL;
1010         return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1011       }
1012       stream->push_headers = headp;
1013     }
1014     h = aprintf("%s:%s", name, value);
1015     if(h)
1016       stream->push_headers[stream->push_headers_used++] = h;
1017     return 0;
1018   }
1019 
1020   if(stream->bodystarted) {
1021     /* This is trailer fields. */
1022     /* 4 is for ": " and "\r\n". */
1023     uint32_t n = (uint32_t)(namelen + valuelen + 4);
1024 
1025     H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s\n", namelen, name, valuelen,
1026                  value));
1027 
1028     result = Curl_add_buffer(&stream->trailer_recvbuf, &n, sizeof(n));
1029     if(result)
1030       return NGHTTP2_ERR_CALLBACK_FAILURE;
1031     result = Curl_add_buffer(&stream->trailer_recvbuf, name, namelen);
1032     if(result)
1033       return NGHTTP2_ERR_CALLBACK_FAILURE;
1034     result = Curl_add_buffer(&stream->trailer_recvbuf, ": ", 2);
1035     if(result)
1036       return NGHTTP2_ERR_CALLBACK_FAILURE;
1037     result = Curl_add_buffer(&stream->trailer_recvbuf, value, valuelen);
1038     if(result)
1039       return NGHTTP2_ERR_CALLBACK_FAILURE;
1040     result = Curl_add_buffer(&stream->trailer_recvbuf, "\r\n\0", 3);
1041     if(result)
1042       return NGHTTP2_ERR_CALLBACK_FAILURE;
1043 
1044     return 0;
1045   }
1046 
1047   if(namelen == sizeof(":status") - 1 &&
1048      memcmp(":status", name, namelen) == 0) {
1049     /* nghttp2 guarantees :status is received first and only once, and
1050        value is 3 digits status code, and decode_status_code always
1051        succeeds. */
1052     stream->status_code = decode_status_code(value, valuelen);
1053     DEBUGASSERT(stream->status_code != -1);
1054 
1055     result = Curl_add_buffer(&stream->header_recvbuf, "HTTP/2 ", 7);
1056     if(result)
1057       return NGHTTP2_ERR_CALLBACK_FAILURE;
1058     result = Curl_add_buffer(&stream->header_recvbuf, value, valuelen);
1059     if(result)
1060       return NGHTTP2_ERR_CALLBACK_FAILURE;
1061     /* the space character after the status code is mandatory */
1062     result = Curl_add_buffer(&stream->header_recvbuf, " \r\n", 3);
1063     if(result)
1064       return NGHTTP2_ERR_CALLBACK_FAILURE;
1065     /* if we receive data for another handle, wake that up */
1066     if(conn->data != data_s)
1067       Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
1068 
1069     H2BUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)\n",
1070                  stream->status_code, data_s));
1071     return 0;
1072   }
1073 
1074   /* nghttp2 guarantees that namelen > 0, and :status was already
1075      received, and this is not pseudo-header field . */
1076   /* convert to a HTTP1-style header */
1077   result = Curl_add_buffer(&stream->header_recvbuf, name, namelen);
1078   if(result)
1079     return NGHTTP2_ERR_CALLBACK_FAILURE;
1080   result = Curl_add_buffer(&stream->header_recvbuf, ": ", 2);
1081   if(result)
1082     return NGHTTP2_ERR_CALLBACK_FAILURE;
1083   result = Curl_add_buffer(&stream->header_recvbuf, value, valuelen);
1084   if(result)
1085     return NGHTTP2_ERR_CALLBACK_FAILURE;
1086   result = Curl_add_buffer(&stream->header_recvbuf, "\r\n", 2);
1087   if(result)
1088     return NGHTTP2_ERR_CALLBACK_FAILURE;
1089   /* if we receive data for another handle, wake that up */
1090   if(conn->data != data_s)
1091     Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
1092 
1093   H2BUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
1094                value));
1095 
1096   return 0; /* 0 is successful */
1097 }
1098 
data_source_read_callback(nghttp2_session * session,int32_t stream_id,uint8_t * buf,size_t length,uint32_t * data_flags,nghttp2_data_source * source,void * userp)1099 static ssize_t data_source_read_callback(nghttp2_session *session,
1100                                          int32_t stream_id,
1101                                          uint8_t *buf, size_t length,
1102                                          uint32_t *data_flags,
1103                                          nghttp2_data_source *source,
1104                                          void *userp)
1105 {
1106   struct Curl_easy *data_s;
1107   struct HTTP *stream = NULL;
1108   size_t nread;
1109   (void)source;
1110   (void)userp;
1111 
1112   if(stream_id) {
1113     /* get the stream from the hash based on Stream ID, stream ID zero is for
1114        connection-oriented stuff */
1115     data_s = nghttp2_session_get_stream_user_data(session, stream_id);
1116     if(!data_s)
1117       /* Receiving a Stream ID not in the hash should not happen, this is an
1118          internal error more than anything else! */
1119       return NGHTTP2_ERR_CALLBACK_FAILURE;
1120 
1121     stream = data_s->req.protop;
1122     if(!stream)
1123       return NGHTTP2_ERR_CALLBACK_FAILURE;
1124   }
1125   else
1126     return NGHTTP2_ERR_INVALID_ARGUMENT;
1127 
1128   nread = CURLMIN(stream->upload_len, length);
1129   if(nread > 0) {
1130     memcpy(buf, stream->upload_mem, nread);
1131     stream->upload_mem += nread;
1132     stream->upload_len -= nread;
1133     if(data_s->state.infilesize != -1)
1134       stream->upload_left -= nread;
1135   }
1136 
1137   if(stream->upload_left == 0)
1138     *data_flags = NGHTTP2_DATA_FLAG_EOF;
1139   else if(nread == 0)
1140     return NGHTTP2_ERR_DEFERRED;
1141 
1142   H2BUGF(infof(data_s, "data_source_read_callback: "
1143                "returns %zu bytes stream %u\n",
1144                nread, stream_id));
1145 
1146   return nread;
1147 }
1148 
1149 #if defined(NGHTTP2_HAS_ERROR_CALLBACK) &&      \
1150   !defined(CURL_DISABLE_VERBOSE_STRINGS)
error_callback(nghttp2_session * session,const char * msg,size_t len,void * userp)1151 static int error_callback(nghttp2_session *session,
1152                           const char *msg,
1153                           size_t len,
1154                           void *userp)
1155 {
1156   struct connectdata *conn = (struct connectdata *)userp;
1157   (void)session;
1158   infof(conn->data, "http2 error: %.*s\n", len, msg);
1159   return 0;
1160 }
1161 #endif
1162 
populate_settings(struct connectdata * conn,struct http_conn * httpc)1163 static void populate_settings(struct connectdata *conn,
1164                               struct http_conn *httpc)
1165 {
1166   nghttp2_settings_entry *iv = httpc->local_settings;
1167 
1168   iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
1169   iv[0].value = 100;
1170 
1171   iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
1172   iv[1].value = HTTP2_HUGE_WINDOW_SIZE;
1173 
1174   iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
1175   iv[2].value = conn->data->multi->push_cb != NULL;
1176 
1177   httpc->local_settings_num = 3;
1178 }
1179 
Curl_http2_done(struct connectdata * conn,bool premature)1180 void Curl_http2_done(struct connectdata *conn, bool premature)
1181 {
1182   struct Curl_easy *data = conn->data;
1183   struct HTTP *http = data->req.protop;
1184   struct http_conn *httpc = &conn->proto.httpc;
1185 
1186   /* there might be allocated resources done before this got the 'h2' pointer
1187      setup */
1188   if(http->header_recvbuf) {
1189     Curl_add_buffer_free(&http->header_recvbuf);
1190     Curl_add_buffer_free(&http->trailer_recvbuf);
1191     if(http->push_headers) {
1192       /* if they weren't used and then freed before */
1193       for(; http->push_headers_used > 0; --http->push_headers_used) {
1194         free(http->push_headers[http->push_headers_used - 1]);
1195       }
1196       free(http->push_headers);
1197       http->push_headers = NULL;
1198     }
1199   }
1200 
1201   if(!httpc->h2) /* not HTTP/2 ? */
1202     return;
1203 
1204   if(data->state.drain)
1205     drained_transfer(data, httpc);
1206 
1207   if(premature) {
1208     /* RST_STREAM */
1209     if(!nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE,
1210                                   http->stream_id, NGHTTP2_STREAM_CLOSED))
1211       (void)nghttp2_session_send(httpc->h2);
1212 
1213     if(http->stream_id == httpc->pause_stream_id) {
1214       infof(data, "stopped the pause stream!\n");
1215       httpc->pause_stream_id = 0;
1216     }
1217   }
1218   /* -1 means unassigned and 0 means cleared */
1219   if(http->stream_id > 0) {
1220     int rv = nghttp2_session_set_stream_user_data(httpc->h2,
1221                                                   http->stream_id, 0);
1222     if(rv) {
1223       infof(data, "http/2: failed to clear user_data for stream %d!\n",
1224             http->stream_id);
1225       DEBUGASSERT(0);
1226     }
1227     http->stream_id = 0;
1228   }
1229 }
1230 
1231 /*
1232  * Initialize nghttp2 for a Curl connection
1233  */
http2_init(struct connectdata * conn)1234 static CURLcode http2_init(struct connectdata *conn)
1235 {
1236   if(!conn->proto.httpc.h2) {
1237     int rc;
1238     nghttp2_session_callbacks *callbacks;
1239 
1240     conn->proto.httpc.inbuf = malloc(H2_BUFSIZE);
1241     if(conn->proto.httpc.inbuf == NULL)
1242       return CURLE_OUT_OF_MEMORY;
1243 
1244     rc = nghttp2_session_callbacks_new(&callbacks);
1245 
1246     if(rc) {
1247       failf(conn->data, "Couldn't initialize nghttp2 callbacks!");
1248       return CURLE_OUT_OF_MEMORY; /* most likely at least */
1249     }
1250 
1251     /* nghttp2_send_callback */
1252     nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
1253     /* nghttp2_on_frame_recv_callback */
1254     nghttp2_session_callbacks_set_on_frame_recv_callback
1255       (callbacks, on_frame_recv);
1256     /* nghttp2_on_data_chunk_recv_callback */
1257     nghttp2_session_callbacks_set_on_data_chunk_recv_callback
1258       (callbacks, on_data_chunk_recv);
1259     /* nghttp2_on_stream_close_callback */
1260     nghttp2_session_callbacks_set_on_stream_close_callback
1261       (callbacks, on_stream_close);
1262     /* nghttp2_on_begin_headers_callback */
1263     nghttp2_session_callbacks_set_on_begin_headers_callback
1264       (callbacks, on_begin_headers);
1265     /* nghttp2_on_header_callback */
1266     nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
1267 
1268 #ifndef CURL_DISABLE_VERBOSE_STRINGS
1269     nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
1270 #endif
1271 
1272     /* The nghttp2 session is not yet setup, do it */
1273     rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
1274 
1275     nghttp2_session_callbacks_del(callbacks);
1276 
1277     if(rc) {
1278       failf(conn->data, "Couldn't initialize nghttp2!");
1279       return CURLE_OUT_OF_MEMORY; /* most likely at least */
1280     }
1281   }
1282   return CURLE_OK;
1283 }
1284 
1285 /*
1286  * Append headers to ask for a HTTP1.1 to HTTP2 upgrade.
1287  */
Curl_http2_request_upgrade(Curl_send_buffer * req,struct connectdata * conn)1288 CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req,
1289                                     struct connectdata *conn)
1290 {
1291   CURLcode result;
1292   ssize_t binlen;
1293   char *base64;
1294   size_t blen;
1295   struct SingleRequest *k = &conn->data->req;
1296   uint8_t *binsettings = conn->proto.httpc.binsettings;
1297   struct http_conn *httpc = &conn->proto.httpc;
1298 
1299   populate_settings(conn, httpc);
1300 
1301   /* this returns number of bytes it wrote */
1302   binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
1303                                          httpc->local_settings,
1304                                          httpc->local_settings_num);
1305   if(!binlen) {
1306     failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
1307     Curl_add_buffer_free(&req);
1308     return CURLE_FAILED_INIT;
1309   }
1310   conn->proto.httpc.binlen = binlen;
1311 
1312   result = Curl_base64url_encode(conn->data, (const char *)binsettings, binlen,
1313                                  &base64, &blen);
1314   if(result) {
1315     Curl_add_buffer_free(&req);
1316     return result;
1317   }
1318 
1319   result = Curl_add_bufferf(&req,
1320                             "Connection: Upgrade, HTTP2-Settings\r\n"
1321                             "Upgrade: %s\r\n"
1322                             "HTTP2-Settings: %s\r\n",
1323                             NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
1324   free(base64);
1325 
1326   k->upgr101 = UPGR101_REQUESTED;
1327 
1328   return result;
1329 }
1330 
1331 /*
1332  * Returns nonzero if current HTTP/2 session should be closed.
1333  */
should_close_session(struct http_conn * httpc)1334 static int should_close_session(struct http_conn *httpc)
1335 {
1336   return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) &&
1337     !nghttp2_session_want_write(httpc->h2);
1338 }
1339 
1340 /*
1341  * h2_process_pending_input() processes pending input left in
1342  * httpc->inbuf.  Then, call h2_session_send() to send pending data.
1343  * This function returns 0 if it succeeds, or -1 and error code will
1344  * be assigned to *err.
1345  */
h2_process_pending_input(struct connectdata * conn,struct http_conn * httpc,CURLcode * err)1346 static int h2_process_pending_input(struct connectdata *conn,
1347                                     struct http_conn *httpc,
1348                                     CURLcode *err)
1349 {
1350   ssize_t nread;
1351   char *inbuf;
1352   ssize_t rv;
1353   struct Curl_easy *data = conn->data;
1354 
1355   nread = httpc->inbuflen - httpc->nread_inbuf;
1356   inbuf = httpc->inbuf + httpc->nread_inbuf;
1357 
1358   rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
1359   if(rv < 0) {
1360     failf(data,
1361           "h2_process_pending_input: nghttp2_session_mem_recv() returned "
1362           "%zd:%s\n", rv, nghttp2_strerror((int)rv));
1363     *err = CURLE_RECV_ERROR;
1364     return -1;
1365   }
1366 
1367   if(nread == rv) {
1368     H2BUGF(infof(data,
1369                  "h2_process_pending_input: All data in connection buffer "
1370                  "processed\n"));
1371     httpc->inbuflen = 0;
1372     httpc->nread_inbuf = 0;
1373   }
1374   else {
1375     httpc->nread_inbuf += rv;
1376     H2BUGF(infof(data,
1377                  "h2_process_pending_input: %zu bytes left in connection "
1378                  "buffer\n",
1379                  httpc->inbuflen - httpc->nread_inbuf));
1380   }
1381 
1382   rv = h2_session_send(data, httpc->h2);
1383   if(rv != 0) {
1384     *err = CURLE_SEND_ERROR;
1385     return -1;
1386   }
1387 
1388   if(should_close_session(httpc)) {
1389     H2BUGF(infof(data,
1390                  "h2_process_pending_input: nothing to do in this session\n"));
1391     if(httpc->error_code)
1392       *err = CURLE_HTTP2;
1393     else {
1394       /* not an error per se, but should still close the connection */
1395       connclose(conn, "GOAWAY received");
1396       *err = CURLE_OK;
1397     }
1398     return -1;
1399   }
1400 
1401   return 0;
1402 }
1403 
1404 /*
1405  * Called from transfer.c:done_sending when we stop uploading.
1406  */
Curl_http2_done_sending(struct connectdata * conn)1407 CURLcode Curl_http2_done_sending(struct connectdata *conn)
1408 {
1409   CURLcode result = CURLE_OK;
1410 
1411   if((conn->handler == &Curl_handler_http2_ssl) ||
1412      (conn->handler == &Curl_handler_http2)) {
1413     /* make sure this is only attempted for HTTP/2 transfers */
1414 
1415     struct HTTP *stream = conn->data->req.protop;
1416 
1417     if(stream->upload_left) {
1418       /* If the stream still thinks there's data left to upload. */
1419       struct http_conn *httpc = &conn->proto.httpc;
1420       nghttp2_session *h2 = httpc->h2;
1421 
1422       stream->upload_left = 0; /* DONE! */
1423 
1424       /* resume sending here to trigger the callback to get called again so
1425          that it can signal EOF to nghttp2 */
1426       (void)nghttp2_session_resume_data(h2, stream->stream_id);
1427 
1428       (void)h2_process_pending_input(conn, httpc, &result);
1429     }
1430   }
1431   return result;
1432 }
1433 
http2_handle_stream_close(struct connectdata * conn,struct Curl_easy * data,struct HTTP * stream,CURLcode * err)1434 static ssize_t http2_handle_stream_close(struct connectdata *conn,
1435                                          struct Curl_easy *data,
1436                                          struct HTTP *stream, CURLcode *err)
1437 {
1438   char *trailer_pos, *trailer_end;
1439   CURLcode result;
1440   struct http_conn *httpc = &conn->proto.httpc;
1441 
1442   if(httpc->pause_stream_id == stream->stream_id) {
1443     httpc->pause_stream_id = 0;
1444   }
1445 
1446   drained_transfer(data, httpc);
1447 
1448   if(httpc->pause_stream_id == 0) {
1449     if(h2_process_pending_input(conn, httpc, err) != 0) {
1450       return -1;
1451     }
1452   }
1453 
1454   DEBUGASSERT(data->state.drain == 0);
1455 
1456   /* Reset to FALSE to prevent infinite loop in readwrite_data function. */
1457   stream->closed = FALSE;
1458   if(httpc->error_code == NGHTTP2_REFUSED_STREAM) {
1459     H2BUGF(infof(data, "REFUSED_STREAM (%d), try again on a new connection!\n",
1460                  stream->stream_id));
1461     connclose(conn, "REFUSED_STREAM"); /* don't use this anymore */
1462     data->state.refused_stream = TRUE;
1463     *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */
1464     return -1;
1465   }
1466   else if(httpc->error_code != NGHTTP2_NO_ERROR) {
1467     failf(data, "HTTP/2 stream %d was not closed cleanly: %s (err %u)",
1468           stream->stream_id, http2_strerror(httpc->error_code),
1469           httpc->error_code);
1470     *err = CURLE_HTTP2_STREAM;
1471     return -1;
1472   }
1473 
1474   if(!stream->bodystarted) {
1475     failf(data, "HTTP/2 stream %d was closed cleanly, but before getting "
1476           " all response header fields, treated as error",
1477           stream->stream_id);
1478     *err = CURLE_HTTP2_STREAM;
1479     return -1;
1480   }
1481 
1482   if(stream->trailer_recvbuf && stream->trailer_recvbuf->buffer) {
1483     trailer_pos = stream->trailer_recvbuf->buffer;
1484     trailer_end = trailer_pos + stream->trailer_recvbuf->size_used;
1485 
1486     for(; trailer_pos < trailer_end;) {
1487       uint32_t n;
1488       memcpy(&n, trailer_pos, sizeof(n));
1489       trailer_pos += sizeof(n);
1490 
1491       result = Curl_client_write(conn, CLIENTWRITE_HEADER, trailer_pos, n);
1492       if(result) {
1493         *err = result;
1494         return -1;
1495       }
1496 
1497       trailer_pos += n + 1;
1498     }
1499   }
1500 
1501   stream->close_handled = TRUE;
1502 
1503   H2BUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close\n"));
1504   return 0;
1505 }
1506 
1507 /*
1508  * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
1509  * and dependency to the peer. It also stores the updated values in the state
1510  * struct.
1511  */
1512 
h2_pri_spec(struct Curl_easy * data,nghttp2_priority_spec * pri_spec)1513 static void h2_pri_spec(struct Curl_easy *data,
1514                         nghttp2_priority_spec *pri_spec)
1515 {
1516   struct HTTP *depstream = (data->set.stream_depends_on?
1517                             data->set.stream_depends_on->req.protop:NULL);
1518   int32_t depstream_id = depstream? depstream->stream_id:0;
1519   nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_weight,
1520                              data->set.stream_depends_e);
1521   data->state.stream_weight = data->set.stream_weight;
1522   data->state.stream_depends_e = data->set.stream_depends_e;
1523   data->state.stream_depends_on = data->set.stream_depends_on;
1524 }
1525 
1526 /*
1527  * h2_session_send() checks if there's been an update in the priority /
1528  * dependency settings and if so it submits a PRIORITY frame with the updated
1529  * info.
1530  */
h2_session_send(struct Curl_easy * data,nghttp2_session * h2)1531 static int h2_session_send(struct Curl_easy *data,
1532                            nghttp2_session *h2)
1533 {
1534   struct HTTP *stream = data->req.protop;
1535   if((data->set.stream_weight != data->state.stream_weight) ||
1536      (data->set.stream_depends_e != data->state.stream_depends_e) ||
1537      (data->set.stream_depends_on != data->state.stream_depends_on) ) {
1538     /* send new weight and/or dependency */
1539     nghttp2_priority_spec pri_spec;
1540     int rv;
1541 
1542     h2_pri_spec(data, &pri_spec);
1543 
1544     H2BUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)\n",
1545                  stream->stream_id, data));
1546     rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
1547                                  &pri_spec);
1548     if(rv)
1549       return rv;
1550   }
1551 
1552   return nghttp2_session_send(h2);
1553 }
1554 
http2_recv(struct connectdata * conn,int sockindex,char * mem,size_t len,CURLcode * err)1555 static ssize_t http2_recv(struct connectdata *conn, int sockindex,
1556                           char *mem, size_t len, CURLcode *err)
1557 {
1558   CURLcode result = CURLE_OK;
1559   ssize_t rv;
1560   ssize_t nread;
1561   struct http_conn *httpc = &conn->proto.httpc;
1562   struct Curl_easy *data = conn->data;
1563   struct HTTP *stream = data->req.protop;
1564 
1565   (void)sockindex; /* we always do HTTP2 on sockindex 0 */
1566 
1567   if(should_close_session(httpc)) {
1568     H2BUGF(infof(data,
1569                  "http2_recv: nothing to do in this session\n"));
1570     *err = CURLE_HTTP2;
1571     return -1;
1572   }
1573 
1574   /* Nullify here because we call nghttp2_session_send() and they
1575      might refer to the old buffer. */
1576   stream->upload_mem = NULL;
1577   stream->upload_len = 0;
1578 
1579   /*
1580    * At this point 'stream' is just in the Curl_easy the connection
1581    * identifies as its owner at this time.
1582    */
1583 
1584   if(stream->bodystarted &&
1585      stream->nread_header_recvbuf < stream->header_recvbuf->size_used) {
1586     /* If there is body data pending for this stream to return, do that */
1587     size_t left =
1588       stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
1589     size_t ncopy = CURLMIN(len, left);
1590     memcpy(mem, stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
1591            ncopy);
1592     stream->nread_header_recvbuf += ncopy;
1593 
1594     H2BUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf\n",
1595                  (int)ncopy));
1596     return ncopy;
1597   }
1598 
1599   H2BUGF(infof(data, "http2_recv: easy %p (stream %u)\n",
1600                data, stream->stream_id));
1601 
1602   if((data->state.drain) && stream->memlen) {
1603     H2BUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n",
1604                  stream->memlen, stream->stream_id,
1605                  stream->mem, mem));
1606     if(mem != stream->mem) {
1607       /* if we didn't get the same buffer this time, we must move the data to
1608          the beginning */
1609       memmove(mem, stream->mem, stream->memlen);
1610       stream->len = len - stream->memlen;
1611       stream->mem = mem;
1612     }
1613     if(httpc->pause_stream_id == stream->stream_id && !stream->pausedata) {
1614       /* We have paused nghttp2, but we have no pause data (see
1615          on_data_chunk_recv). */
1616       httpc->pause_stream_id = 0;
1617       if(h2_process_pending_input(conn, httpc, &result) != 0) {
1618         *err = result;
1619         return -1;
1620       }
1621     }
1622   }
1623   else if(stream->pausedata) {
1624     DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
1625     nread = CURLMIN(len, stream->pauselen);
1626     memcpy(mem, stream->pausedata, nread);
1627 
1628     stream->pausedata += nread;
1629     stream->pauselen -= nread;
1630 
1631     infof(data, "%zd data bytes written\n", nread);
1632     if(stream->pauselen == 0) {
1633       H2BUGF(infof(data, "Unpaused by stream %u\n", stream->stream_id));
1634       DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
1635       httpc->pause_stream_id = 0;
1636 
1637       stream->pausedata = NULL;
1638       stream->pauselen = 0;
1639 
1640       /* When NGHTTP2_ERR_PAUSE is returned from
1641          data_source_read_callback, we might not process DATA frame
1642          fully.  Calling nghttp2_session_mem_recv() again will
1643          continue to process DATA frame, but if there is no incoming
1644          frames, then we have to call it again with 0-length data.
1645          Without this, on_stream_close callback will not be called,
1646          and stream could be hanged. */
1647       if(h2_process_pending_input(conn, httpc, &result) != 0) {
1648         *err = result;
1649         return -1;
1650       }
1651     }
1652     H2BUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u\n",
1653                  nread, stream->stream_id));
1654     return nread;
1655   }
1656   else if(httpc->pause_stream_id) {
1657     /* If a stream paused nghttp2_session_mem_recv previously, and has
1658        not processed all data, it still refers to the buffer in
1659        nghttp2_session.  If we call nghttp2_session_mem_recv(), we may
1660        overwrite that buffer.  To avoid that situation, just return
1661        here with CURLE_AGAIN.  This could be busy loop since data in
1662        socket is not read.  But it seems that usually streams are
1663        notified with its drain property, and socket is read again
1664        quickly. */
1665     H2BUGF(infof(data, "stream %x is paused, pause id: %x\n",
1666                  stream->stream_id, httpc->pause_stream_id));
1667     *err = CURLE_AGAIN;
1668     return -1;
1669   }
1670   else {
1671     char *inbuf;
1672     /* remember where to store incoming data for this stream and how big the
1673        buffer is */
1674     stream->mem = mem;
1675     stream->len = len;
1676     stream->memlen = 0;
1677 
1678     if(httpc->inbuflen == 0) {
1679       nread = ((Curl_recv *)httpc->recv_underlying)(
1680           conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
1681 
1682       if(nread == -1) {
1683         if(result != CURLE_AGAIN)
1684           failf(data, "Failed receiving HTTP2 data");
1685         else if(stream->closed)
1686           /* received when the stream was already closed! */
1687           return http2_handle_stream_close(conn, data, stream, err);
1688 
1689         *err = result;
1690         return -1;
1691       }
1692 
1693       if(nread == 0) {
1694         H2BUGF(infof(data, "end of stream\n"));
1695         *err = CURLE_OK;
1696         return 0;
1697       }
1698 
1699       H2BUGF(infof(data, "nread=%zd\n", nread));
1700 
1701       httpc->inbuflen = nread;
1702       inbuf = httpc->inbuf;
1703     }
1704     else {
1705       nread = httpc->inbuflen - httpc->nread_inbuf;
1706       inbuf = httpc->inbuf + httpc->nread_inbuf;
1707 
1708       H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd\n",
1709                    nread));
1710     }
1711     rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
1712 
1713     if(nghttp2_is_fatal((int)rv)) {
1714       failf(data, "nghttp2_session_mem_recv() returned %zd:%s\n",
1715             rv, nghttp2_strerror((int)rv));
1716       *err = CURLE_RECV_ERROR;
1717       return -1;
1718     }
1719     H2BUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", rv));
1720     if(nread == rv) {
1721       H2BUGF(infof(data, "All data in connection buffer processed\n"));
1722       httpc->inbuflen = 0;
1723       httpc->nread_inbuf = 0;
1724     }
1725     else {
1726       httpc->nread_inbuf += rv;
1727       H2BUGF(infof(data, "%zu bytes left in connection buffer\n",
1728                    httpc->inbuflen - httpc->nread_inbuf));
1729     }
1730     /* Always send pending frames in nghttp2 session, because
1731        nghttp2_session_mem_recv() may queue new frame */
1732     rv = h2_session_send(data, httpc->h2);
1733     if(rv != 0) {
1734       *err = CURLE_SEND_ERROR;
1735       return -1;
1736     }
1737 
1738     if(should_close_session(httpc)) {
1739       H2BUGF(infof(data, "http2_recv: nothing to do in this session\n"));
1740       *err = CURLE_HTTP2;
1741       return -1;
1742     }
1743   }
1744   if(stream->memlen) {
1745     ssize_t retlen = stream->memlen;
1746     H2BUGF(infof(data, "http2_recv: returns %zd for stream %u\n",
1747                  retlen, stream->stream_id));
1748     stream->memlen = 0;
1749 
1750     if(httpc->pause_stream_id == stream->stream_id) {
1751       /* data for this stream is returned now, but this stream caused a pause
1752          already so we need it called again asap */
1753       H2BUGF(infof(data, "Data returned for PAUSED stream %u\n",
1754                    stream->stream_id));
1755     }
1756     else if(!stream->closed) {
1757       drained_transfer(data, httpc);
1758     }
1759 
1760     return retlen;
1761   }
1762   /* If stream is closed, return 0 to signal the http routine to close
1763      the connection */
1764   if(stream->closed) {
1765     return http2_handle_stream_close(conn, data, stream, err);
1766   }
1767   *err = CURLE_AGAIN;
1768   H2BUGF(infof(data, "http2_recv returns AGAIN for stream %u\n",
1769                stream->stream_id));
1770   return -1;
1771 }
1772 
1773 /* Index where :authority header field will appear in request header
1774    field list. */
1775 #define AUTHORITY_DST_IDX 3
1776 
1777 #define HEADER_OVERFLOW(x) \
1778   (x.namelen > (uint16_t)-1 || x.valuelen > (uint16_t)-1 - x.namelen)
1779 
1780 /*
1781  * Check header memory for the token "trailers".
1782  * Parse the tokens as separated by comma and surrounded by whitespace.
1783  * Returns TRUE if found or FALSE if not.
1784  */
contains_trailers(const char * p,size_t len)1785 static bool contains_trailers(const char *p, size_t len)
1786 {
1787   const char *end = p + len;
1788   for(;;) {
1789     for(; p != end && (*p == ' ' || *p == '\t'); ++p)
1790       ;
1791     if(p == end || (size_t)(end - p) < sizeof("trailers") - 1)
1792       return FALSE;
1793     if(strncasecompare("trailers", p, sizeof("trailers") - 1)) {
1794       p += sizeof("trailers") - 1;
1795       for(; p != end && (*p == ' ' || *p == '\t'); ++p)
1796         ;
1797       if(p == end || *p == ',')
1798         return TRUE;
1799     }
1800     /* skip to next token */
1801     for(; p != end && *p != ','; ++p)
1802       ;
1803     if(p == end)
1804       return FALSE;
1805     ++p;
1806   }
1807 }
1808 
1809 typedef enum {
1810   /* Send header to server */
1811   HEADERINST_FORWARD,
1812   /* Don't send header to server */
1813   HEADERINST_IGNORE,
1814   /* Discard header, and replace it with "te: trailers" */
1815   HEADERINST_TE_TRAILERS
1816 } header_instruction;
1817 
1818 /* Decides how to treat given header field. */
inspect_header(const char * name,size_t namelen,const char * value,size_t valuelen)1819 static header_instruction inspect_header(const char *name, size_t namelen,
1820                                          const char *value, size_t valuelen) {
1821   switch(namelen) {
1822   case 2:
1823     if(!strncasecompare("te", name, namelen))
1824       return HEADERINST_FORWARD;
1825 
1826     return contains_trailers(value, valuelen) ?
1827            HEADERINST_TE_TRAILERS : HEADERINST_IGNORE;
1828   case 7:
1829     return strncasecompare("upgrade", name, namelen) ?
1830            HEADERINST_IGNORE : HEADERINST_FORWARD;
1831   case 10:
1832     return (strncasecompare("connection", name, namelen) ||
1833             strncasecompare("keep-alive", name, namelen)) ?
1834            HEADERINST_IGNORE : HEADERINST_FORWARD;
1835   case 16:
1836     return strncasecompare("proxy-connection", name, namelen) ?
1837            HEADERINST_IGNORE : HEADERINST_FORWARD;
1838   case 17:
1839     return strncasecompare("transfer-encoding", name, namelen) ?
1840            HEADERINST_IGNORE : HEADERINST_FORWARD;
1841   default:
1842     return HEADERINST_FORWARD;
1843   }
1844 }
1845 
http2_send(struct connectdata * conn,int sockindex,const void * mem,size_t len,CURLcode * err)1846 static ssize_t http2_send(struct connectdata *conn, int sockindex,
1847                           const void *mem, size_t len, CURLcode *err)
1848 {
1849   /*
1850    * BIG TODO: Currently, we send request in this function, but this
1851    * function is also used to send request body. It would be nice to
1852    * add dedicated function for request.
1853    */
1854   int rv;
1855   struct http_conn *httpc = &conn->proto.httpc;
1856   struct HTTP *stream = conn->data->req.protop;
1857   nghttp2_nv *nva = NULL;
1858   size_t nheader;
1859   size_t i;
1860   size_t authority_idx;
1861   char *hdbuf = (char *)mem;
1862   char *end, *line_end;
1863   nghttp2_data_provider data_prd;
1864   int32_t stream_id;
1865   nghttp2_session *h2 = httpc->h2;
1866   nghttp2_priority_spec pri_spec;
1867 
1868   (void)sockindex;
1869 
1870   H2BUGF(infof(conn->data, "http2_send len=%zu\n", len));
1871 
1872   if(stream->stream_id != -1) {
1873     if(stream->close_handled) {
1874       infof(conn->data, "stream %d closed\n", stream->stream_id);
1875       *err = CURLE_HTTP2_STREAM;
1876       return -1;
1877     }
1878     else if(stream->closed) {
1879       return http2_handle_stream_close(conn, conn->data, stream, err);
1880     }
1881     /* If stream_id != -1, we have dispatched request HEADERS, and now
1882        are going to send or sending request body in DATA frame */
1883     stream->upload_mem = mem;
1884     stream->upload_len = len;
1885     nghttp2_session_resume_data(h2, stream->stream_id);
1886     rv = h2_session_send(conn->data, h2);
1887     if(nghttp2_is_fatal(rv)) {
1888       *err = CURLE_SEND_ERROR;
1889       return -1;
1890     }
1891     len -= stream->upload_len;
1892 
1893     /* Nullify here because we call nghttp2_session_send() and they
1894        might refer to the old buffer. */
1895     stream->upload_mem = NULL;
1896     stream->upload_len = 0;
1897 
1898     if(should_close_session(httpc)) {
1899       H2BUGF(infof(conn->data, "http2_send: nothing to do in this session\n"));
1900       *err = CURLE_HTTP2;
1901       return -1;
1902     }
1903 
1904     if(stream->upload_left) {
1905       /* we are sure that we have more data to send here.  Calling the
1906          following API will make nghttp2_session_want_write() return
1907          nonzero if remote window allows it, which then libcurl checks
1908          socket is writable or not.  See http2_perform_getsock(). */
1909       nghttp2_session_resume_data(h2, stream->stream_id);
1910     }
1911 
1912     H2BUGF(infof(conn->data, "http2_send returns %zu for stream %u\n", len,
1913                  stream->stream_id));
1914     return len;
1915   }
1916 
1917   /* Calculate number of headers contained in [mem, mem + len) */
1918   /* Here, we assume the curl http code generate *correct* HTTP header
1919      field block */
1920   nheader = 0;
1921   for(i = 1; i < len; ++i) {
1922     if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') {
1923       ++nheader;
1924       ++i;
1925     }
1926   }
1927   if(nheader < 2)
1928     goto fail;
1929 
1930   /* We counted additional 2 \r\n in the first and last line. We need 3
1931      new headers: :method, :path and :scheme. Therefore we need one
1932      more space. */
1933   nheader += 1;
1934   nva = malloc(sizeof(nghttp2_nv) * nheader);
1935   if(nva == NULL) {
1936     *err = CURLE_OUT_OF_MEMORY;
1937     return -1;
1938   }
1939 
1940   /* Extract :method, :path from request line
1941      We do line endings with CRLF so checking for CR is enough */
1942   line_end = memchr(hdbuf, '\r', len);
1943   if(!line_end)
1944     goto fail;
1945 
1946   /* Method does not contain spaces */
1947   end = memchr(hdbuf, ' ', line_end - hdbuf);
1948   if(!end || end == hdbuf)
1949     goto fail;
1950   nva[0].name = (unsigned char *)":method";
1951   nva[0].namelen = strlen((char *)nva[0].name);
1952   nva[0].value = (unsigned char *)hdbuf;
1953   nva[0].valuelen = (size_t)(end - hdbuf);
1954   nva[0].flags = NGHTTP2_NV_FLAG_NONE;
1955   if(HEADER_OVERFLOW(nva[0])) {
1956     failf(conn->data, "Failed sending HTTP request: Header overflow");
1957     goto fail;
1958   }
1959 
1960   hdbuf = end + 1;
1961 
1962   /* Path may contain spaces so scan backwards */
1963   end = NULL;
1964   for(i = (size_t)(line_end - hdbuf); i; --i) {
1965     if(hdbuf[i - 1] == ' ') {
1966       end = &hdbuf[i - 1];
1967       break;
1968     }
1969   }
1970   if(!end || end == hdbuf)
1971     goto fail;
1972   nva[1].name = (unsigned char *)":path";
1973   nva[1].namelen = strlen((char *)nva[1].name);
1974   nva[1].value = (unsigned char *)hdbuf;
1975   nva[1].valuelen = (size_t)(end - hdbuf);
1976   nva[1].flags = NGHTTP2_NV_FLAG_NONE;
1977   if(HEADER_OVERFLOW(nva[1])) {
1978     failf(conn->data, "Failed sending HTTP request: Header overflow");
1979     goto fail;
1980   }
1981 
1982   nva[2].name = (unsigned char *)":scheme";
1983   nva[2].namelen = strlen((char *)nva[2].name);
1984   if(conn->handler->flags & PROTOPT_SSL)
1985     nva[2].value = (unsigned char *)"https";
1986   else
1987     nva[2].value = (unsigned char *)"http";
1988   nva[2].valuelen = strlen((char *)nva[2].value);
1989   nva[2].flags = NGHTTP2_NV_FLAG_NONE;
1990   if(HEADER_OVERFLOW(nva[2])) {
1991     failf(conn->data, "Failed sending HTTP request: Header overflow");
1992     goto fail;
1993   }
1994 
1995   authority_idx = 0;
1996   i = 3;
1997   while(i < nheader) {
1998     size_t hlen;
1999 
2000     hdbuf = line_end + 2;
2001 
2002     /* check for next CR, but only within the piece of data left in the given
2003        buffer */
2004     line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem));
2005     if(!line_end || (line_end == hdbuf))
2006       goto fail;
2007 
2008     /* header continuation lines are not supported */
2009     if(*hdbuf == ' ' || *hdbuf == '\t')
2010       goto fail;
2011 
2012     for(end = hdbuf; end < line_end && *end != ':'; ++end)
2013       ;
2014     if(end == hdbuf || end == line_end)
2015       goto fail;
2016     hlen = end - hdbuf;
2017 
2018     if(hlen == 4 && strncasecompare("host", hdbuf, 4)) {
2019       authority_idx = i;
2020       nva[i].name = (unsigned char *)":authority";
2021       nva[i].namelen = strlen((char *)nva[i].name);
2022     }
2023     else {
2024       nva[i].name = (unsigned char *)hdbuf;
2025       nva[i].namelen = (size_t)(end - hdbuf);
2026     }
2027     hdbuf = end + 1;
2028     while(*hdbuf == ' ' || *hdbuf == '\t')
2029       ++hdbuf;
2030     end = line_end;
2031 
2032     switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf,
2033                           end - hdbuf)) {
2034     case HEADERINST_IGNORE:
2035       /* skip header fields prohibited by HTTP/2 specification. */
2036       --nheader;
2037       continue;
2038     case HEADERINST_TE_TRAILERS:
2039       nva[i].value = (uint8_t*)"trailers";
2040       nva[i].valuelen = sizeof("trailers") - 1;
2041       break;
2042     default:
2043       nva[i].value = (unsigned char *)hdbuf;
2044       nva[i].valuelen = (size_t)(end - hdbuf);
2045     }
2046 
2047     nva[i].flags = NGHTTP2_NV_FLAG_NONE;
2048     if(HEADER_OVERFLOW(nva[i])) {
2049       failf(conn->data, "Failed sending HTTP request: Header overflow");
2050       goto fail;
2051     }
2052     ++i;
2053   }
2054 
2055   /* :authority must come before non-pseudo header fields */
2056   if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) {
2057     nghttp2_nv authority = nva[authority_idx];
2058     for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
2059       nva[i] = nva[i - 1];
2060     }
2061     nva[i] = authority;
2062   }
2063 
2064   /* Warn stream may be rejected if cumulative length of headers is too large.
2065      It appears nghttp2 will not send a header frame larger than 64KB. */
2066 #define MAX_ACC 60000  /* <64KB to account for some overhead */
2067   {
2068     size_t acc = 0;
2069 
2070     for(i = 0; i < nheader; ++i) {
2071       acc += nva[i].namelen + nva[i].valuelen;
2072 
2073       H2BUGF(infof(conn->data, "h2 header: %.*s:%.*s\n",
2074                    nva[i].namelen, nva[i].name,
2075                    nva[i].valuelen, nva[i].value));
2076     }
2077 
2078     if(acc > MAX_ACC) {
2079       infof(conn->data, "http2_send: Warning: The cumulative length of all "
2080             "headers exceeds %zu bytes and that could cause the "
2081             "stream to be rejected.\n", MAX_ACC);
2082     }
2083   }
2084 
2085   h2_pri_spec(conn->data, &pri_spec);
2086 
2087   switch(conn->data->set.httpreq) {
2088   case HTTPREQ_POST:
2089   case HTTPREQ_POST_FORM:
2090   case HTTPREQ_POST_MIME:
2091   case HTTPREQ_PUT:
2092     if(conn->data->state.infilesize != -1)
2093       stream->upload_left = conn->data->state.infilesize;
2094     else
2095       /* data sending without specifying the data amount up front */
2096       stream->upload_left = -1; /* unknown, but not zero */
2097 
2098     data_prd.read_callback = data_source_read_callback;
2099     data_prd.source.ptr = NULL;
2100     stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
2101                                        &data_prd, conn->data);
2102     break;
2103   default:
2104     stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
2105                                        NULL, conn->data);
2106   }
2107 
2108   Curl_safefree(nva);
2109 
2110   if(stream_id < 0) {
2111     H2BUGF(infof(conn->data, "http2_send() send error\n"));
2112     *err = CURLE_SEND_ERROR;
2113     return -1;
2114   }
2115 
2116   infof(conn->data, "Using Stream ID: %x (easy handle %p)\n",
2117         stream_id, (void *)conn->data);
2118   stream->stream_id = stream_id;
2119 
2120   /* this does not call h2_session_send() since there can not have been any
2121    * priority upodate since the nghttp2_submit_request() call above */
2122   rv = nghttp2_session_send(h2);
2123 
2124   if(rv != 0) {
2125     *err = CURLE_SEND_ERROR;
2126     return -1;
2127   }
2128 
2129   if(should_close_session(httpc)) {
2130     H2BUGF(infof(conn->data, "http2_send: nothing to do in this session\n"));
2131     *err = CURLE_HTTP2;
2132     return -1;
2133   }
2134 
2135   if(stream->stream_id != -1) {
2136     /* If whole HEADERS frame was sent off to the underlying socket,
2137        the nghttp2 library calls data_source_read_callback. But only
2138        it found that no data available, so it deferred the DATA
2139        transmission. Which means that nghttp2_session_want_write()
2140        returns 0 on http2_perform_getsock(), which results that no
2141        writable socket check is performed. To workaround this, we
2142        issue nghttp2_session_resume_data() here to bring back DATA
2143        transmission from deferred state. */
2144     nghttp2_session_resume_data(h2, stream->stream_id);
2145   }
2146 
2147   return len;
2148 
2149 fail:
2150   free(nva);
2151   *err = CURLE_SEND_ERROR;
2152   return -1;
2153 }
2154 
Curl_http2_setup(struct connectdata * conn)2155 CURLcode Curl_http2_setup(struct connectdata *conn)
2156 {
2157   CURLcode result;
2158   struct http_conn *httpc = &conn->proto.httpc;
2159   struct HTTP *stream = conn->data->req.protop;
2160 
2161   stream->stream_id = -1;
2162 
2163   if(!stream->header_recvbuf) {
2164     stream->header_recvbuf = Curl_add_buffer_init();
2165     if(!stream->header_recvbuf)
2166       return CURLE_OUT_OF_MEMORY;
2167   }
2168 
2169   if((conn->handler == &Curl_handler_http2_ssl) ||
2170      (conn->handler == &Curl_handler_http2))
2171     return CURLE_OK; /* already done */
2172 
2173   if(conn->handler->flags & PROTOPT_SSL)
2174     conn->handler = &Curl_handler_http2_ssl;
2175   else
2176     conn->handler = &Curl_handler_http2;
2177 
2178   result = http2_init(conn);
2179   if(result) {
2180     Curl_add_buffer_free(&stream->header_recvbuf);
2181     return result;
2182   }
2183 
2184   infof(conn->data, "Using HTTP2, server supports multi-use\n");
2185   stream->upload_left = 0;
2186   stream->upload_mem = NULL;
2187   stream->upload_len = 0;
2188 
2189   httpc->inbuflen = 0;
2190   httpc->nread_inbuf = 0;
2191 
2192   httpc->pause_stream_id = 0;
2193   httpc->drain_total = 0;
2194 
2195   conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2196   conn->httpversion = 20;
2197   conn->bundle->multiuse = BUNDLE_MULTIPLEX;
2198 
2199   infof(conn->data, "Connection state changed (HTTP/2 confirmed)\n");
2200   multi_connchanged(conn->data->multi);
2201 
2202   return CURLE_OK;
2203 }
2204 
Curl_http2_switched(struct connectdata * conn,const char * mem,size_t nread)2205 CURLcode Curl_http2_switched(struct connectdata *conn,
2206                              const char *mem, size_t nread)
2207 {
2208   CURLcode result;
2209   struct http_conn *httpc = &conn->proto.httpc;
2210   int rv;
2211   ssize_t nproc;
2212   struct Curl_easy *data = conn->data;
2213   struct HTTP *stream = conn->data->req.protop;
2214 
2215   result = Curl_http2_setup(conn);
2216   if(result)
2217     return result;
2218 
2219   httpc->recv_underlying = conn->recv[FIRSTSOCKET];
2220   httpc->send_underlying = conn->send[FIRSTSOCKET];
2221   conn->recv[FIRSTSOCKET] = http2_recv;
2222   conn->send[FIRSTSOCKET] = http2_send;
2223 
2224   if(conn->data->req.upgr101 == UPGR101_RECEIVED) {
2225     /* stream 1 is opened implicitly on upgrade */
2226     stream->stream_id = 1;
2227     /* queue SETTINGS frame (again) */
2228     rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings,
2229                                  httpc->binlen, NULL);
2230     if(rv != 0) {
2231       failf(data, "nghttp2_session_upgrade() failed: %s(%d)",
2232             nghttp2_strerror(rv), rv);
2233       return CURLE_HTTP2;
2234     }
2235 
2236     rv = nghttp2_session_set_stream_user_data(httpc->h2,
2237                                               stream->stream_id,
2238                                               data);
2239     if(rv) {
2240       infof(data, "http/2: failed to set user_data for stream %d!\n",
2241             stream->stream_id);
2242       DEBUGASSERT(0);
2243     }
2244   }
2245   else {
2246     populate_settings(conn, httpc);
2247 
2248     /* stream ID is unknown at this point */
2249     stream->stream_id = -1;
2250     rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE,
2251                                  httpc->local_settings,
2252                                  httpc->local_settings_num);
2253     if(rv != 0) {
2254       failf(data, "nghttp2_submit_settings() failed: %s(%d)",
2255             nghttp2_strerror(rv), rv);
2256       return CURLE_HTTP2;
2257     }
2258   }
2259 
2260 #ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
2261   rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, 0,
2262                                              HTTP2_HUGE_WINDOW_SIZE);
2263   if(rv != 0) {
2264     failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
2265           nghttp2_strerror(rv), rv);
2266     return CURLE_HTTP2;
2267   }
2268 #endif
2269 
2270   /* we are going to copy mem to httpc->inbuf.  This is required since
2271      mem is part of buffer pointed by stream->mem, and callbacks
2272      called by nghttp2_session_mem_recv() will write stream specific
2273      data into stream->mem, overwriting data already there. */
2274   if(H2_BUFSIZE < nread) {
2275     failf(data, "connection buffer size is too small to store data following "
2276                 "HTTP Upgrade response header: buflen=%zu, datalen=%zu",
2277           H2_BUFSIZE, nread);
2278     return CURLE_HTTP2;
2279   }
2280 
2281   infof(conn->data, "Copying HTTP/2 data in stream buffer to connection buffer"
2282                     " after upgrade: len=%zu\n",
2283         nread);
2284 
2285   if(nread)
2286     memcpy(httpc->inbuf, mem, nread);
2287   httpc->inbuflen = nread;
2288 
2289   nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf,
2290                                    httpc->inbuflen);
2291 
2292   if(nghttp2_is_fatal((int)nproc)) {
2293     failf(data, "nghttp2_session_mem_recv() failed: %s(%d)",
2294           nghttp2_strerror((int)nproc), (int)nproc);
2295     return CURLE_HTTP2;
2296   }
2297 
2298   H2BUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", nproc));
2299 
2300   if((ssize_t)nread == nproc) {
2301     httpc->inbuflen = 0;
2302     httpc->nread_inbuf = 0;
2303   }
2304   else {
2305     httpc->nread_inbuf += nproc;
2306   }
2307 
2308   /* Try to send some frames since we may read SETTINGS already. */
2309   rv = h2_session_send(data, httpc->h2);
2310 
2311   if(rv != 0) {
2312     failf(data, "nghttp2_session_send() failed: %s(%d)",
2313           nghttp2_strerror(rv), rv);
2314     return CURLE_HTTP2;
2315   }
2316 
2317   if(should_close_session(httpc)) {
2318     H2BUGF(infof(data,
2319                  "nghttp2_session_send(): nothing to do in this session\n"));
2320     return CURLE_HTTP2;
2321   }
2322 
2323   return CURLE_OK;
2324 }
2325 
Curl_http2_add_child(struct Curl_easy * parent,struct Curl_easy * child,bool exclusive)2326 CURLcode Curl_http2_add_child(struct Curl_easy *parent,
2327                               struct Curl_easy *child,
2328                               bool exclusive)
2329 {
2330   if(parent) {
2331     struct Curl_http2_dep **tail;
2332     struct Curl_http2_dep *dep = calloc(1, sizeof(struct Curl_http2_dep));
2333     if(!dep)
2334       return CURLE_OUT_OF_MEMORY;
2335     dep->data = child;
2336 
2337     if(parent->set.stream_dependents && exclusive) {
2338       struct Curl_http2_dep *node = parent->set.stream_dependents;
2339       while(node) {
2340         node->data->set.stream_depends_on = child;
2341         node = node->next;
2342       }
2343 
2344       tail = &child->set.stream_dependents;
2345       while(*tail)
2346         tail = &(*tail)->next;
2347 
2348       DEBUGASSERT(!*tail);
2349       *tail = parent->set.stream_dependents;
2350       parent->set.stream_dependents = 0;
2351     }
2352 
2353     tail = &parent->set.stream_dependents;
2354     while(*tail) {
2355       (*tail)->data->set.stream_depends_e = FALSE;
2356       tail = &(*tail)->next;
2357     }
2358 
2359     DEBUGASSERT(!*tail);
2360     *tail = dep;
2361   }
2362 
2363   child->set.stream_depends_on = parent;
2364   child->set.stream_depends_e = exclusive;
2365   return CURLE_OK;
2366 }
2367 
Curl_http2_remove_child(struct Curl_easy * parent,struct Curl_easy * child)2368 void Curl_http2_remove_child(struct Curl_easy *parent, struct Curl_easy *child)
2369 {
2370   struct Curl_http2_dep *last = 0;
2371   struct Curl_http2_dep *data = parent->set.stream_dependents;
2372   DEBUGASSERT(child->set.stream_depends_on == parent);
2373 
2374   while(data && data->data != child) {
2375     last = data;
2376     data = data->next;
2377   }
2378 
2379   DEBUGASSERT(data);
2380 
2381   if(data) {
2382     if(last) {
2383       last->next = data->next;
2384     }
2385     else {
2386       parent->set.stream_dependents = data->next;
2387     }
2388     free(data);
2389   }
2390 
2391   child->set.stream_depends_on = 0;
2392   child->set.stream_depends_e = FALSE;
2393 }
2394 
Curl_http2_cleanup_dependencies(struct Curl_easy * data)2395 void Curl_http2_cleanup_dependencies(struct Curl_easy *data)
2396 {
2397   while(data->set.stream_dependents) {
2398     struct Curl_easy *tmp = data->set.stream_dependents->data;
2399     Curl_http2_remove_child(data, tmp);
2400     if(data->set.stream_depends_on)
2401       Curl_http2_add_child(data->set.stream_depends_on, tmp, FALSE);
2402   }
2403 
2404   if(data->set.stream_depends_on)
2405     Curl_http2_remove_child(data->set.stream_depends_on, data);
2406 }
2407 
2408 /* Only call this function for a transfer that already got a HTTP/2
2409    CURLE_HTTP2_STREAM error! */
Curl_h2_http_1_1_error(struct connectdata * conn)2410 bool Curl_h2_http_1_1_error(struct connectdata *conn)
2411 {
2412   struct http_conn *httpc = &conn->proto.httpc;
2413   return (httpc->error_code == NGHTTP2_HTTP_1_1_REQUIRED);
2414 }
2415 
2416 #else /* !USE_NGHTTP2 */
2417 
2418 /* Satisfy external references even if http2 is not compiled in. */
2419 
2420 #define CURL_DISABLE_TYPECHECK
2421 #include <curl/curl.h>
2422 
curl_pushheader_bynum(struct curl_pushheaders * h,size_t num)2423 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
2424 {
2425   (void) h;
2426   (void) num;
2427   return NULL;
2428 }
2429 
curl_pushheader_byname(struct curl_pushheaders * h,const char * header)2430 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
2431 {
2432   (void) h;
2433   (void) header;
2434   return NULL;
2435 }
2436 
2437 #endif /* USE_NGHTTP2 */
2438