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