1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2015, 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 http://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 "curl_printf.h"
27 #include <nghttp2/nghttp2.h>
28 #include "urldata.h"
29 #include "http2.h"
30 #include "http.h"
31 #include "sendf.h"
32 #include "curl_base64.h"
33 #include "rawstr.h"
34 #include "multiif.h"
35 #include "conncache.h"
36 
37 /* The last #include files should be: */
38 #include "curl_memory.h"
39 #include "memdebug.h"
40 
41 #define MIN(x,y) ((x)<(y)?(x):(y))
42 
43 #if (NGHTTP2_VERSION_NUM < 0x000600)
44 #error too old nghttp2 version, upgrade!
45 #endif
46 
http2_perform_getsock(const struct connectdata * conn,curl_socket_t * sock,int numsocks)47 static int http2_perform_getsock(const struct connectdata *conn,
48                                  curl_socket_t *sock, /* points to
49                                                          numsocks
50                                                          number of
51                                                          sockets */
52                                  int numsocks)
53 {
54   const struct http_conn *c = &conn->proto.httpc;
55   int bitmap = GETSOCK_BLANK;
56   (void)numsocks;
57 
58   /* TODO We should check underlying socket state if it is SSL socket
59      because of renegotiation. */
60   sock[0] = conn->sock[FIRSTSOCKET];
61 
62   if(nghttp2_session_want_read(c->h2))
63     bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
64 
65   if(nghttp2_session_want_write(c->h2))
66     bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
67 
68   return bitmap;
69 }
70 
http2_getsock(struct connectdata * conn,curl_socket_t * sock,int numsocks)71 static int http2_getsock(struct connectdata *conn,
72                          curl_socket_t *sock, /* points to numsocks
73                                                  number of sockets */
74                          int numsocks)
75 {
76   return http2_perform_getsock(conn, sock, numsocks);
77 }
78 
http2_disconnect(struct connectdata * conn,bool dead_connection)79 static CURLcode http2_disconnect(struct connectdata *conn,
80                                  bool dead_connection)
81 {
82   struct http_conn *c = &conn->proto.httpc;
83   (void)dead_connection;
84 
85   DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT starts now\n"));
86 
87   nghttp2_session_del(c->h2);
88   Curl_safefree(c->inbuf);
89   Curl_hash_destroy(&c->streamsh);
90 
91   DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT done\n"));
92 
93   return CURLE_OK;
94 }
95 
96 /* called from Curl_http_setup_conn */
Curl_http2_setup_conn(struct connectdata * conn)97 void Curl_http2_setup_conn(struct connectdata *conn)
98 {
99   struct HTTP *http = conn->data->req.protop;
100 
101   conn->proto.httpc.settings.max_concurrent_streams =
102     DEFAULT_MAX_CONCURRENT_STREAMS;
103 
104   http->nread_header_recvbuf = 0;
105   http->bodystarted = FALSE;
106   http->status_code = -1;
107   http->pausedata = NULL;
108   http->pauselen = 0;
109   http->error_code = NGHTTP2_NO_ERROR;
110   http->closed = FALSE;
111 
112   /* where to store incoming data for this stream and how big the buffer is */
113   http->mem = conn->data->state.buffer;
114   http->len = BUFSIZE;
115   http->memlen = 0;
116 }
117 
118 /*
119  * HTTP2 handler interface. This isn't added to the general list of protocols
120  * but will be used at run-time when the protocol is dynamically switched from
121  * HTTP to HTTP2.
122  */
123 const struct Curl_handler Curl_handler_http2 = {
124   "HTTP2",                              /* scheme */
125   ZERO_NULL,                            /* setup_connection */
126   Curl_http,                            /* do_it */
127   Curl_http_done,                       /* done */
128   ZERO_NULL,                            /* do_more */
129   ZERO_NULL,                            /* connect_it */
130   ZERO_NULL,                            /* connecting */
131   ZERO_NULL,                            /* doing */
132   http2_getsock,                        /* proto_getsock */
133   http2_getsock,                        /* doing_getsock */
134   ZERO_NULL,                            /* domore_getsock */
135   http2_perform_getsock,                /* perform_getsock */
136   http2_disconnect,                     /* disconnect */
137   ZERO_NULL,                            /* readwrite */
138   PORT_HTTP,                            /* defport */
139   CURLPROTO_HTTP,                       /* protocol */
140   PROTOPT_NONE                          /* flags */
141 };
142 
143 const struct Curl_handler Curl_handler_http2_ssl = {
144   "HTTP2",                              /* scheme */
145   ZERO_NULL,                            /* setup_connection */
146   Curl_http,                            /* do_it */
147   Curl_http_done,                       /* done */
148   ZERO_NULL,                            /* do_more */
149   ZERO_NULL,                            /* connect_it */
150   ZERO_NULL,                            /* connecting */
151   ZERO_NULL,                            /* doing */
152   http2_getsock,                        /* proto_getsock */
153   http2_getsock,                        /* doing_getsock */
154   ZERO_NULL,                            /* domore_getsock */
155   http2_perform_getsock,                /* perform_getsock */
156   http2_disconnect,                     /* disconnect */
157   ZERO_NULL,                            /* readwrite */
158   PORT_HTTP,                            /* defport */
159   CURLPROTO_HTTPS,                      /* protocol */
160   PROTOPT_SSL                           /* flags */
161 };
162 
163 /*
164  * Store nghttp2 version info in this buffer, Prefix with a space.  Return
165  * total length written.
166  */
Curl_http2_ver(char * p,size_t len)167 int Curl_http2_ver(char *p, size_t len)
168 {
169   nghttp2_info *h2 = nghttp2_version(0);
170   return snprintf(p, len, " nghttp2/%s", h2->version_str);
171 }
172 
173 /*
174  * The implementation of nghttp2_send_callback type. Here we write |data| with
175  * size |length| to the network and return the number of bytes actually
176  * written. See the documentation of nghttp2_send_callback for the details.
177  */
send_callback(nghttp2_session * h2,const uint8_t * data,size_t length,int flags,void * userp)178 static ssize_t send_callback(nghttp2_session *h2,
179                              const uint8_t *data, size_t length, int flags,
180                              void *userp)
181 {
182   struct connectdata *conn = (struct connectdata *)userp;
183   struct http_conn *c = &conn->proto.httpc;
184   ssize_t written;
185   CURLcode result = CURLE_OK;
186 
187   (void)h2;
188   (void)flags;
189 
190   written = ((Curl_send*)c->send_underlying)(conn, FIRSTSOCKET,
191                                              data, length, &result);
192 
193   if(result == CURLE_AGAIN) {
194     return NGHTTP2_ERR_WOULDBLOCK;
195   }
196 
197   if(written == -1) {
198     failf(conn->data, "Failed sending HTTP2 data");
199     return NGHTTP2_ERR_CALLBACK_FAILURE;
200   }
201 
202   if(!written)
203     return NGHTTP2_ERR_WOULDBLOCK;
204 
205   return written;
206 }
207 
on_frame_recv(nghttp2_session * session,const nghttp2_frame * frame,void * userp)208 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
209                          void *userp)
210 {
211   struct connectdata *conn = (struct connectdata *)userp;
212   struct http_conn *httpc = &conn->proto.httpc;
213   struct SessionHandle *data_s = NULL;
214   struct HTTP *stream = NULL;
215   int rv;
216   size_t left, ncopy;
217   int32_t stream_id = frame->hd.stream_id;
218 
219   (void)session;
220   (void)frame;
221   DEBUGF(infof(conn->data, "on_frame_recv() header %x stream %x\n",
222                frame->hd.type, stream_id));
223 
224   if(stream_id) {
225     /* get the stream from the hash based on Stream ID, stream ID zero is for
226        connection-oriented stuff */
227     data_s = Curl_hash_pick(&httpc->streamsh, &stream_id,
228                             sizeof(stream_id));
229     if(!data_s) {
230       /* Receiving a Stream ID not in the hash should not happen, this is an
231          internal error more than anything else! */
232       failf(conn->data, "Received frame on Stream ID: %x not in stream hash!",
233             stream_id);
234       return NGHTTP2_ERR_CALLBACK_FAILURE;
235     }
236     stream = data_s->req.protop;
237   }
238   else
239     /* we do nothing on stream zero */
240     return 0;
241 
242   switch(frame->hd.type) {
243   case NGHTTP2_DATA:
244     /* If body started on this stream, then receiving DATA is illegal. */
245     if(!stream->bodystarted) {
246       rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
247                                      stream_id, NGHTTP2_PROTOCOL_ERROR);
248 
249       if(nghttp2_is_fatal(rv)) {
250         return NGHTTP2_ERR_CALLBACK_FAILURE;
251       }
252     }
253     break;
254   case NGHTTP2_HEADERS:
255     if(frame->headers.cat == NGHTTP2_HCAT_REQUEST)
256       break;
257 
258     if(stream->bodystarted) {
259       /* Only valid HEADERS after body started is trailer HEADERS.  We
260          ignores trailer HEADERS for now.  nghttp2 guarantees that it
261          has END_STREAM flag set. */
262       break;
263     }
264 
265     /* nghttp2 guarantees that :status is received, and we store it to
266        stream->status_code */
267     DEBUGASSERT(stream->status_code != -1);
268 
269     /* Only final status code signals the end of header */
270     if(stream->status_code / 100 != 1) {
271       stream->bodystarted = TRUE;
272       stream->status_code = -1;
273     }
274 
275     Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
276 
277     left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
278     ncopy = MIN(stream->len, left);
279 
280     memcpy(&stream->mem[stream->memlen],
281            stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
282            ncopy);
283     stream->nread_header_recvbuf += ncopy;
284 
285     DEBUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n",
286                  ncopy, stream_id, stream->mem));
287 
288     stream->len -= ncopy;
289     stream->memlen += ncopy;
290 
291     data_s->state.drain++;
292     Curl_expire(data_s, 1);
293     break;
294   case NGHTTP2_PUSH_PROMISE:
295     DEBUGF(infof(data_s, "Got PUSH_PROMISE, RST_STREAM it!\n"));
296     rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
297                                    frame->push_promise.promised_stream_id,
298                                    NGHTTP2_CANCEL);
299     if(nghttp2_is_fatal(rv)) {
300       return rv;
301     }
302     break;
303   case NGHTTP2_SETTINGS:
304   {
305     uint32_t max_conn = httpc->settings.max_concurrent_streams;
306     DEBUGF(infof(conn->data, "Got SETTINGS for stream %u!\n", stream_id));
307     httpc->settings.max_concurrent_streams =
308       nghttp2_session_get_remote_settings(
309         session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
310     httpc->settings.enable_push =
311       nghttp2_session_get_remote_settings(
312         session, NGHTTP2_SETTINGS_ENABLE_PUSH);
313     DEBUGF(infof(conn->data, "MAX_CONCURRENT_STREAMS == %d\n",
314                  httpc->settings.max_concurrent_streams));
315     DEBUGF(infof(conn->data, "ENABLE_PUSH == %s\n",
316                  httpc->settings.enable_push?"TRUE":"false"));
317     if(max_conn != httpc->settings.max_concurrent_streams) {
318       /* only signal change if the value actually changed */
319       infof(conn->data,
320             "Connection state changed (MAX_CONCURRENT_STREAMS updated)!\n");
321       Curl_multi_connchanged(conn->data->multi);
322     }
323   }
324   break;
325   default:
326     DEBUGF(infof(conn->data, "Got frame type %x for stream %u!\n",
327                  frame->hd.type, stream_id));
328     break;
329   }
330   return 0;
331 }
332 
on_invalid_frame_recv(nghttp2_session * session,const nghttp2_frame * frame,int lib_error_code,void * userp)333 static int on_invalid_frame_recv(nghttp2_session *session,
334                                  const nghttp2_frame *frame,
335                                  int lib_error_code, void *userp)
336 {
337   struct connectdata *conn = (struct connectdata *)userp;
338   (void)session;
339   (void)frame;
340   DEBUGF(infof(conn->data,
341                "on_invalid_frame_recv() was called, error=%d:%s\n",
342                lib_error_code, nghttp2_strerror(lib_error_code)));
343   return 0;
344 }
345 
on_data_chunk_recv(nghttp2_session * session,uint8_t flags,int32_t stream_id,const uint8_t * data,size_t len,void * userp)346 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
347                               int32_t stream_id,
348                               const uint8_t *data, size_t len, void *userp)
349 {
350   struct connectdata *conn = (struct connectdata *)userp;
351   struct HTTP *stream;
352   struct SessionHandle *data_s;
353   size_t nread;
354   (void)session;
355   (void)flags;
356   (void)data;
357   DEBUGF(infof(conn->data, "on_data_chunk_recv() "
358                "len = %u, stream %u\n", len, stream_id));
359 
360   DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
361 
362   /* get the stream from the hash based on Stream ID */
363   data_s = Curl_hash_pick(&conn->proto.httpc.streamsh, &stream_id,
364                           sizeof(stream_id));
365   if(!data_s) {
366     /* Receiving a Stream ID not in the hash should not happen, this is an
367        internal error more than anything else! */
368     failf(conn->data, "Received frame on Stream ID: %x not in stream hash!",
369           stream_id);
370     return NGHTTP2_ERR_CALLBACK_FAILURE;
371   }
372   stream = data_s->req.protop;
373 
374   nread = MIN(stream->len, len);
375   memcpy(&stream->mem[stream->memlen], data, nread);
376 
377   stream->len -= nread;
378   stream->memlen += nread;
379 
380   data_s->state.drain++;
381   Curl_expire(data_s, 1); /* TODO: fix so that this can be set to 0 for
382                              immediately? */
383 
384   DEBUGF(infof(data_s, "%zu data received for stream %u "
385                "(%zu left in buffer %p, total %zu)\n",
386                nread, stream_id,
387                stream->len, stream->mem,
388                stream->memlen));
389 
390   if(nread < len) {
391     stream->pausedata = data + nread;
392     stream->pauselen = len - nread;
393     DEBUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
394                  ", stream %u\n",
395                  len - nread, stream_id));
396     conn->proto.httpc.pause_stream_id = stream_id;
397     return NGHTTP2_ERR_PAUSE;
398   }
399   return 0;
400 }
401 
before_frame_send(nghttp2_session * session,const nghttp2_frame * frame,void * userp)402 static int before_frame_send(nghttp2_session *session,
403                              const nghttp2_frame *frame,
404                              void *userp)
405 {
406   struct connectdata *conn = (struct connectdata *)userp;
407   (void)session;
408   (void)frame;
409   DEBUGF(infof(conn->data, "before_frame_send() was called\n"));
410   return 0;
411 }
on_frame_send(nghttp2_session * session,const nghttp2_frame * frame,void * userp)412 static int on_frame_send(nghttp2_session *session,
413                          const nghttp2_frame *frame,
414                          void *userp)
415 {
416   struct connectdata *conn = (struct connectdata *)userp;
417   (void)session;
418   (void)frame;
419   DEBUGF(infof(conn->data, "on_frame_send() was called, length = %zd\n",
420                frame->hd.length));
421   return 0;
422 }
on_frame_not_send(nghttp2_session * session,const nghttp2_frame * frame,int lib_error_code,void * userp)423 static int on_frame_not_send(nghttp2_session *session,
424                              const nghttp2_frame *frame,
425                              int lib_error_code, void *userp)
426 {
427   struct connectdata *conn = (struct connectdata *)userp;
428   (void)session;
429   (void)frame;
430   DEBUGF(infof(conn->data,
431                "on_frame_not_send() was called, lib_error_code = %d\n",
432                lib_error_code));
433   return 0;
434 }
on_stream_close(nghttp2_session * session,int32_t stream_id,uint32_t error_code,void * userp)435 static int on_stream_close(nghttp2_session *session, int32_t stream_id,
436                            uint32_t error_code, void *userp)
437 {
438   struct connectdata *conn = (struct connectdata *)userp;
439   struct SessionHandle *data_s;
440   struct HTTP *stream;
441   (void)session;
442   (void)stream_id;
443   DEBUGF(infof(conn->data, "on_stream_close(), error_code = %d, stream %u\n",
444                error_code, stream_id));
445 
446   if(stream_id) {
447     /* get the stream from the hash based on Stream ID, stream ID zero is for
448        connection-oriented stuff */
449     data_s = Curl_hash_pick(&conn->proto.httpc.streamsh, &stream_id,
450                             sizeof(stream_id));
451     if(!data_s) {
452       /* We could get stream ID not in the hash.  For example, if we
453          decided to reject stream (e.g., PUSH_PROMISE).  We call infof
454          as a debugging purpose for now. */
455       infof(conn->data,
456             "Received frame on Stream ID: %x not in stream hash!\n",
457             stream_id);
458       return 0;
459     }
460     stream = data_s->req.protop;
461 
462     stream->error_code = error_code;
463     stream->closed = TRUE;
464 
465     /* remove the entry from the hash as the stream is now gone */
466     Curl_hash_delete(&conn->proto.httpc.streamsh,
467                      &stream_id, sizeof(stream_id));
468     DEBUGF(infof(conn->data, "Removed stream %u hash!\n", stream_id));
469   }
470   return 0;
471 }
472 
on_begin_headers(nghttp2_session * session,const nghttp2_frame * frame,void * userp)473 static int on_begin_headers(nghttp2_session *session,
474                             const nghttp2_frame *frame, void *userp)
475 {
476   struct connectdata *conn = (struct connectdata *)userp;
477   (void)session;
478   (void)frame;
479   DEBUGF(infof(conn->data, "on_begin_headers() was called\n"));
480   return 0;
481 }
482 
483 /* Decode HTTP status code.  Returns -1 if no valid status code was
484    decoded. */
decode_status_code(const uint8_t * value,size_t len)485 static int decode_status_code(const uint8_t *value, size_t len)
486 {
487   int i;
488   int res;
489 
490   if(len != 3) {
491     return -1;
492   }
493 
494   res = 0;
495 
496   for(i = 0; i < 3; ++i) {
497     char c = value[i];
498 
499     if(c < '0' || c > '9') {
500       return -1;
501     }
502 
503     res *= 10;
504     res += c - '0';
505   }
506 
507   return res;
508 }
509 
510 /* 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)511 static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
512                      const uint8_t *name, size_t namelen,
513                      const uint8_t *value, size_t valuelen,
514                      uint8_t flags,
515                      void *userp)
516 {
517   struct connectdata *conn = (struct connectdata *)userp;
518   struct HTTP *stream;
519   struct SessionHandle *data_s;
520   int32_t stream_id = frame->hd.stream_id;
521 
522   (void)session;
523   (void)frame;
524   (void)flags;
525 
526   /* Ignore PUSH_PROMISE for now */
527   if(frame->hd.type != NGHTTP2_HEADERS) {
528     return 0;
529   }
530 
531   DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
532 
533   /* get the stream from the hash based on Stream ID */
534   data_s = Curl_hash_pick(&conn->proto.httpc.streamsh, &stream_id,
535                           sizeof(stream_id));
536   if(!data_s) {
537     /* Receiving a Stream ID not in the hash should not happen, this is an
538        internal error more than anything else! */
539     failf(conn->data, "Received frame on Stream ID: %x not in stream hash!",
540           stream_id);
541     return NGHTTP2_ERR_CALLBACK_FAILURE;
542   }
543   stream = data_s->req.protop;
544 
545   if(stream->bodystarted)
546     /* Ignore trailer or HEADERS not mapped to HTTP semantics.  The
547        consequence is handled in on_frame_recv(). */
548     return 0;
549 
550   if(namelen == sizeof(":status") - 1 &&
551      memcmp(":status", name, namelen) == 0) {
552     /* nghttp2 guarantees :status is received first and only once, and
553        value is 3 digits status code, and decode_status_code always
554        succeeds. */
555     stream->status_code = decode_status_code(value, valuelen);
556     DEBUGASSERT(stream->status_code != -1);
557 
558     Curl_add_buffer(stream->header_recvbuf, "HTTP/2.0 ", 9);
559     Curl_add_buffer(stream->header_recvbuf, value, valuelen);
560     Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
561     data_s->state.drain++;
562     Curl_expire(data_s, 1);
563 
564     DEBUGF(infof(data_s, "h2 status: HTTP/2 %03d\n",
565                  stream->status_code));
566     return 0;
567   }
568 
569   /* nghttp2 guarantees that namelen > 0, and :status was already
570      received, and this is not pseudo-header field . */
571   /* convert to a HTTP1-style header */
572   Curl_add_buffer(stream->header_recvbuf, name, namelen);
573   Curl_add_buffer(stream->header_recvbuf, ":", 1);
574   Curl_add_buffer(stream->header_recvbuf, value, valuelen);
575   Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
576   data_s->state.drain++;
577   Curl_expire(data_s, 1);
578 
579   DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
580                value));
581 
582   return 0; /* 0 is successful */
583 }
584 
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)585 static ssize_t data_source_read_callback(nghttp2_session *session,
586                                          int32_t stream_id,
587                                          uint8_t *buf, size_t length,
588                                          uint32_t *data_flags,
589                                          nghttp2_data_source *source,
590                                          void *userp)
591 {
592   struct connectdata *conn = (struct connectdata *)userp;
593   struct http_conn *c = &conn->proto.httpc;
594   struct SessionHandle *data_s;
595   struct HTTP *stream = NULL;
596   size_t nread;
597   (void)session;
598   (void)stream_id;
599   (void)source;
600 
601   if(stream_id) {
602     /* get the stream from the hash based on Stream ID, stream ID zero is for
603        connection-oriented stuff */
604     data_s = Curl_hash_pick(&c->streamsh, &stream_id, sizeof(stream_id));
605     if(!data_s) {
606       /* Receiving a Stream ID not in the hash should not happen, this is an
607          internal error more than anything else! */
608       failf(conn->data, "Asked for data to stream %u not in hash!", stream_id);
609       return NGHTTP2_ERR_CALLBACK_FAILURE;
610     }
611     stream = data_s->req.protop;
612   }
613   else {
614     failf(conn->data, "nghttp2 confusion");
615     return NGHTTP2_ERR_INVALID_ARGUMENT;
616   }
617 
618   nread = MIN(stream->upload_len, length);
619   if(nread > 0) {
620     memcpy(buf, stream->upload_mem, nread);
621     stream->upload_mem += nread;
622     stream->upload_len -= nread;
623     stream->upload_left -= nread;
624   }
625 
626   if(stream->upload_left == 0)
627     *data_flags = 1;
628   else if(nread == 0)
629     return NGHTTP2_ERR_DEFERRED;
630 
631   DEBUGF(infof(data_s, "data_source_read_callback: "
632                "returns %zu bytes stream %u\n",
633                nread, stream_id));
634 
635   return nread;
636 }
637 
638 /*
639  * The HTTP2 settings we send in the Upgrade request
640  */
641 static nghttp2_settings_entry settings[] = {
642   { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 },
643   { NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, NGHTTP2_INITIAL_WINDOW_SIZE },
644 };
645 
646 #define H2_BUFSIZE 32768
647 
freestreamentry(void * freethis)648 static void freestreamentry(void *freethis)
649 {
650   (void)freethis;
651 }
652 
653 /*
654  * Initialize nghttp2 for a Curl connection
655  */
Curl_http2_init(struct connectdata * conn)656 CURLcode Curl_http2_init(struct connectdata *conn)
657 {
658   if(!conn->proto.httpc.h2) {
659     int rc;
660     nghttp2_session_callbacks *callbacks;
661 
662     conn->proto.httpc.inbuf = malloc(H2_BUFSIZE);
663     if(conn->proto.httpc.inbuf == NULL)
664       return CURLE_OUT_OF_MEMORY;
665 
666     rc = nghttp2_session_callbacks_new(&callbacks);
667 
668     if(rc) {
669       failf(conn->data, "Couldn't initialize nghttp2 callbacks!");
670       return CURLE_OUT_OF_MEMORY; /* most likely at least */
671     }
672 
673     /* nghttp2_send_callback */
674     nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
675     /* nghttp2_on_frame_recv_callback */
676     nghttp2_session_callbacks_set_on_frame_recv_callback
677       (callbacks, on_frame_recv);
678     /* nghttp2_on_invalid_frame_recv_callback */
679     nghttp2_session_callbacks_set_on_invalid_frame_recv_callback
680       (callbacks, on_invalid_frame_recv);
681     /* nghttp2_on_data_chunk_recv_callback */
682     nghttp2_session_callbacks_set_on_data_chunk_recv_callback
683       (callbacks, on_data_chunk_recv);
684     /* nghttp2_before_frame_send_callback */
685     nghttp2_session_callbacks_set_before_frame_send_callback
686       (callbacks, before_frame_send);
687     /* nghttp2_on_frame_send_callback */
688     nghttp2_session_callbacks_set_on_frame_send_callback
689       (callbacks, on_frame_send);
690     /* nghttp2_on_frame_not_send_callback */
691     nghttp2_session_callbacks_set_on_frame_not_send_callback
692       (callbacks, on_frame_not_send);
693     /* nghttp2_on_stream_close_callback */
694     nghttp2_session_callbacks_set_on_stream_close_callback
695       (callbacks, on_stream_close);
696     /* nghttp2_on_begin_headers_callback */
697     nghttp2_session_callbacks_set_on_begin_headers_callback
698       (callbacks, on_begin_headers);
699     /* nghttp2_on_header_callback */
700     nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
701 
702     /* The nghttp2 session is not yet setup, do it */
703     rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
704 
705     nghttp2_session_callbacks_del(callbacks);
706 
707     if(rc) {
708       failf(conn->data, "Couldn't initialize nghttp2!");
709       return CURLE_OUT_OF_MEMORY; /* most likely at least */
710     }
711 
712     rc = Curl_hash_init(&conn->proto.httpc.streamsh, 7, Curl_hash_str,
713                         Curl_str_key_compare, freestreamentry);
714     if(rc) {
715       failf(conn->data, "Couldn't init stream hash!");
716       return CURLE_OUT_OF_MEMORY; /* most likely at least */
717     }
718   }
719   return CURLE_OK;
720 }
721 
722 /*
723  * Send a request using http2
724  */
Curl_http2_send_request(struct connectdata * conn)725 CURLcode Curl_http2_send_request(struct connectdata *conn)
726 {
727   (void)conn;
728   return CURLE_OK;
729 }
730 
731 /*
732  * Append headers to ask for a HTTP1.1 to HTTP2 upgrade.
733  */
Curl_http2_request_upgrade(Curl_send_buffer * req,struct connectdata * conn)734 CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req,
735                                     struct connectdata *conn)
736 {
737   CURLcode result;
738   ssize_t binlen;
739   char *base64;
740   size_t blen;
741   struct SingleRequest *k = &conn->data->req;
742   uint8_t *binsettings = conn->proto.httpc.binsettings;
743 
744   /* As long as we have a fixed set of settings, we don't have to dynamically
745    * figure out the base64 strings since it'll always be the same. However,
746    * the settings will likely not be fixed every time in the future.
747    */
748 
749   /* this returns number of bytes it wrote */
750   binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
751                                          settings,
752                                          sizeof(settings)/sizeof(settings[0]));
753   if(!binlen) {
754     failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
755     return CURLE_FAILED_INIT;
756   }
757   conn->proto.httpc.binlen = binlen;
758 
759   result = Curl_base64url_encode(conn->data, (const char *)binsettings, binlen,
760                                  &base64, &blen);
761   if(result)
762     return result;
763 
764   result = Curl_add_bufferf(req,
765                             "Connection: Upgrade, HTTP2-Settings\r\n"
766                             "Upgrade: %s\r\n"
767                             "HTTP2-Settings: %s\r\n",
768                             NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
769   free(base64);
770 
771   k->upgr101 = UPGR101_REQUESTED;
772 
773   return result;
774 }
775 
http2_handle_stream_close(struct http_conn * httpc,struct SessionHandle * data,struct HTTP * stream,CURLcode * err)776 static ssize_t http2_handle_stream_close(struct http_conn *httpc,
777                                          struct SessionHandle *data,
778                                          struct HTTP *stream, CURLcode *err) {
779   if(httpc->pause_stream_id == stream->stream_id) {
780     httpc->pause_stream_id = 0;
781   }
782   /* Reset to FALSE to prevent infinite loop in readwrite_data
783    function. */
784   stream->closed = FALSE;
785   if(stream->error_code != NGHTTP2_NO_ERROR) {
786     failf(data, "HTTP/2 stream %u was not closed cleanly: error_code = %d",
787           stream->stream_id, stream->error_code);
788     *err = CURLE_HTTP2;
789     return -1;
790   }
791   DEBUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close\n"));
792   return 0;
793 }
794 
795 /*
796  * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
797  * a regular CURLcode value.
798  */
http2_recv(struct connectdata * conn,int sockindex,char * mem,size_t len,CURLcode * err)799 static ssize_t http2_recv(struct connectdata *conn, int sockindex,
800                           char *mem, size_t len, CURLcode *err)
801 {
802   CURLcode result = CURLE_OK;
803   ssize_t rv;
804   ssize_t nread;
805   struct http_conn *httpc = &conn->proto.httpc;
806   struct SessionHandle *data = conn->data;
807   struct HTTP *stream = data->req.protop;
808 
809   (void)sockindex; /* we always do HTTP2 on sockindex 0 */
810 
811   /* If stream is closed, return 0 to signal the http routine to close
812      the connection.  We need to handle stream closure here,
813      otherwise, we may be going to read from underlying connection,
814      and gets EAGAIN, and we will get stuck there. */
815   if(stream->memlen == 0 && stream->closed) {
816     return http2_handle_stream_close(httpc, data, stream, err);
817   }
818 
819   /* Nullify here because we call nghttp2_session_send() and they
820      might refer to the old buffer. */
821   stream->upload_mem = NULL;
822   stream->upload_len = 0;
823 
824   /*
825    * At this point 'stream' is just in the SessionHandle the connection
826    * identifies as its owner at this time.
827    */
828 
829   if(stream->bodystarted &&
830      stream->nread_header_recvbuf < stream->header_recvbuf->size_used) {
831     /* If there is body data pending for this stream to return, do that */
832     size_t left =
833       stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
834     size_t ncopy = MIN(len, left);
835     memcpy(mem, stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
836            ncopy);
837     stream->nread_header_recvbuf += ncopy;
838 
839     infof(data, "http2_recv: Got %d bytes from header_recvbuf\n",
840           (int)ncopy);
841     return ncopy;
842   }
843 
844   infof(data, "http2_recv: %d bytes buffer at %p (stream %u)\n",
845         len, mem, stream->stream_id);
846 
847   if((data->state.drain) && stream->memlen) {
848     DEBUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n",
849                  stream->memlen, stream->stream_id,
850                  stream->mem, mem));
851     if(mem != stream->mem) {
852       /* if we didn't get the same buffer this time, we must move the data to
853          the beginning */
854       memmove(mem, stream->mem, stream->memlen);
855       stream->len = len - stream->memlen;
856       stream->mem = mem;
857     }
858   }
859   else if(stream->pausedata) {
860     nread = MIN(len, stream->pauselen);
861     memcpy(mem, stream->pausedata, nread);
862 
863     stream->pausedata += nread;
864     stream->pauselen -= nread;
865 
866     infof(data, "%zu data bytes written\n", nread);
867     if(stream->pauselen == 0) {
868       DEBUGF(infof(data, "Unpaused by stream %u\n", stream->stream_id));
869       assert(httpc->pause_stream_id == stream->stream_id);
870       httpc->pause_stream_id = 0;
871 
872       stream->pausedata = NULL;
873       stream->pauselen = 0;
874     }
875     infof(data, "http2_recv: returns unpaused %zd bytes on stream %u\n",
876           nread, stream->stream_id);
877     return nread;
878   }
879   else if(httpc->pause_stream_id) {
880     /* If a stream paused nghttp2_session_mem_recv previously, and has
881        not processed all data, it still refers to the buffer in
882        nghttp2_session.  If we call nghttp2_session_mem_recv(), we may
883        overwrite that buffer.  To avoid that situation, just return
884        here with CURLE_AGAIN.  This could be busy loop since data in
885        socket is not read.  But it seems that usually streams are
886        notified with its drain property, and socket is read again
887        quickly. */
888     *err = CURLE_AGAIN;
889     return -1;
890   }
891   else {
892     char *inbuf;
893     /* remember where to store incoming data for this stream and how big the
894        buffer is */
895     stream->mem = mem;
896     stream->len = len;
897     stream->memlen = 0;
898 
899     if(httpc->inbuflen == 0) {
900       nread = ((Curl_recv *)httpc->recv_underlying)(
901           conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
902 
903       if(result == CURLE_AGAIN) {
904         *err = result;
905         return -1;
906       }
907 
908       if(nread == -1) {
909         failf(data, "Failed receiving HTTP2 data");
910         *err = result;
911         return 0;
912       }
913 
914       if(nread == 0) {
915         failf(data, "Unexpected EOF");
916         *err = CURLE_RECV_ERROR;
917         return -1;
918       }
919 
920       DEBUGF(infof(data, "nread=%zd\n", nread));
921 
922       httpc->inbuflen = nread;
923       inbuf = httpc->inbuf;
924     }
925     else {
926       nread = httpc->inbuflen - httpc->nread_inbuf;
927       inbuf = httpc->inbuf + httpc->nread_inbuf;
928 
929       DEBUGF(infof(data, "Use data left in connection buffer, nread=%zd\n",
930                    nread));
931     }
932     rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
933 
934     if(nghttp2_is_fatal((int)rv)) {
935       failf(data, "nghttp2_session_mem_recv() returned %d:%s\n",
936             rv, nghttp2_strerror((int)rv));
937       *err = CURLE_RECV_ERROR;
938       return 0;
939     }
940     DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", rv));
941     if(nread == rv) {
942       DEBUGF(infof(data, "All data in connection buffer processed\n"));
943       httpc->inbuflen = 0;
944       httpc->nread_inbuf = 0;
945     }
946     else {
947       httpc->nread_inbuf += rv;
948       DEBUGF(infof(data, "%zu bytes left in connection buffer\n",
949                    httpc->inbuflen - httpc->nread_inbuf));
950     }
951     /* Always send pending frames in nghttp2 session, because
952        nghttp2_session_mem_recv() may queue new frame */
953     rv = nghttp2_session_send(httpc->h2);
954     if(rv != 0) {
955       *err = CURLE_SEND_ERROR;
956       return 0;
957     }
958   }
959   if(stream->memlen) {
960     ssize_t retlen = stream->memlen;
961     infof(data, "http2_recv: returns %zd for stream %u\n",
962           retlen, stream->stream_id);
963     stream->memlen = 0;
964 
965     if(httpc->pause_stream_id == stream->stream_id) {
966       /* data for this stream is returned now, but this stream caused a pause
967          already so we need it called again asap */
968       DEBUGF(infof(data, "Data returned for PAUSED stream %u\n",
969                    stream->stream_id));
970     }
971     else
972       data->state.drain = 0; /* this stream is hereby drained */
973 
974     return retlen;
975   }
976   /* If stream is closed, return 0 to signal the http routine to close
977      the connection */
978   if(stream->closed) {
979     return http2_handle_stream_close(httpc, data, stream, err);
980   }
981   *err = CURLE_AGAIN;
982   DEBUGF(infof(data, "http2_recv returns AGAIN for stream %u\n",
983                stream->stream_id));
984   return -1;
985 }
986 
987 /* Index where :authority header field will appear in request header
988    field list. */
989 #define AUTHORITY_DST_IDX 3
990 
991 /* return number of received (decrypted) bytes */
http2_send(struct connectdata * conn,int sockindex,const void * mem,size_t len,CURLcode * err)992 static ssize_t http2_send(struct connectdata *conn, int sockindex,
993                           const void *mem, size_t len, CURLcode *err)
994 {
995   /*
996    * BIG TODO: Currently, we send request in this function, but this
997    * function is also used to send request body. It would be nice to
998    * add dedicated function for request.
999    */
1000   int rv;
1001   struct http_conn *httpc = &conn->proto.httpc;
1002   struct HTTP *stream = conn->data->req.protop;
1003   nghttp2_nv *nva;
1004   size_t nheader;
1005   size_t i;
1006   size_t authority_idx;
1007   char *hdbuf = (char*)mem;
1008   char *end;
1009   nghttp2_data_provider data_prd;
1010   int32_t stream_id;
1011   nghttp2_session *h2 = httpc->h2;
1012 
1013   (void)sockindex;
1014 
1015   DEBUGF(infof(conn->data, "http2_send len=%zu\n", len));
1016 
1017   if(stream->stream_id != -1) {
1018     /* If stream_id != -1, we have dispatched request HEADERS, and now
1019        are going to send or sending request body in DATA frame */
1020     stream->upload_mem = mem;
1021     stream->upload_len = len;
1022     nghttp2_session_resume_data(h2, stream->stream_id);
1023     rv = nghttp2_session_send(h2);
1024     if(nghttp2_is_fatal(rv)) {
1025       *err = CURLE_SEND_ERROR;
1026       return -1;
1027     }
1028     len -= stream->upload_len;
1029 
1030     /* Nullify here because we call nghttp2_session_send() and they
1031        might refer to the old buffer. */
1032     stream->upload_mem = NULL;
1033     stream->upload_len = 0;
1034 
1035     if(stream->upload_left) {
1036       /* we are sure that we have more data to send here.  Calling the
1037          following API will make nghttp2_session_want_write() return
1038          nonzero if remote window allows it, which then libcurl checks
1039          socket is writable or not.  See http2_perform_getsock(). */
1040       nghttp2_session_resume_data(h2, stream->stream_id);
1041     }
1042 
1043     DEBUGF(infof(conn->data, "http2_send returns %zu for stream %u\n", len,
1044                  stream->stream_id));
1045     return len;
1046   }
1047 
1048   /* Calculate number of headers contained in [mem, mem + len) */
1049   /* Here, we assume the curl http code generate *correct* HTTP header
1050      field block */
1051   nheader = 0;
1052   for(i = 0; i < len; ++i) {
1053     if(hdbuf[i] == 0x0a) {
1054       ++nheader;
1055     }
1056   }
1057   /* We counted additional 2 \n in the first and last line. We need 3
1058      new headers: :method, :path and :scheme. Therefore we need one
1059      more space. */
1060   nheader += 1;
1061   nva = malloc(sizeof(nghttp2_nv) * nheader);
1062   if(nva == NULL) {
1063     *err = CURLE_OUT_OF_MEMORY;
1064     return -1;
1065   }
1066   /* Extract :method, :path from request line */
1067   end = strchr(hdbuf, ' ');
1068   nva[0].name = (unsigned char *)":method";
1069   nva[0].namelen = (uint16_t)strlen((char *)nva[0].name);
1070   nva[0].value = (unsigned char *)hdbuf;
1071   nva[0].valuelen = (uint16_t)(end - hdbuf);
1072   nva[0].flags = NGHTTP2_NV_FLAG_NONE;
1073 
1074   hdbuf = end + 1;
1075 
1076   end = strchr(hdbuf, ' ');
1077   nva[1].name = (unsigned char *)":path";
1078   nva[1].namelen = (uint16_t)strlen((char *)nva[1].name);
1079   nva[1].value = (unsigned char *)hdbuf;
1080   nva[1].valuelen = (uint16_t)(end - hdbuf);
1081   nva[1].flags = NGHTTP2_NV_FLAG_NONE;
1082 
1083   nva[2].name = (unsigned char *)":scheme";
1084   nva[2].namelen = (uint16_t)strlen((char *)nva[2].name);
1085   if(conn->handler->flags & PROTOPT_SSL)
1086     nva[2].value = (unsigned char *)"https";
1087   else
1088     nva[2].value = (unsigned char *)"http";
1089   nva[2].valuelen = (uint16_t)strlen((char *)nva[2].value);
1090   nva[2].flags = NGHTTP2_NV_FLAG_NONE;
1091 
1092   hdbuf = strchr(hdbuf, 0x0a);
1093   ++hdbuf;
1094 
1095   authority_idx = 0;
1096 
1097   for(i = 3; i < nheader; ++i) {
1098     end = strchr(hdbuf, ':');
1099     assert(end);
1100     if(end - hdbuf == 4 && Curl_raw_nequal("host", hdbuf, 4)) {
1101       authority_idx = i;
1102       nva[i].name = (unsigned char *)":authority";
1103       nva[i].namelen = (uint16_t)strlen((char *)nva[i].name);
1104     }
1105     else {
1106       nva[i].name = (unsigned char *)hdbuf;
1107       nva[i].namelen = (uint16_t)(end - hdbuf);
1108     }
1109     hdbuf = end + 1;
1110     for(; *hdbuf == ' '; ++hdbuf);
1111     end = strchr(hdbuf, 0x0d);
1112     assert(end);
1113     nva[i].value = (unsigned char *)hdbuf;
1114     nva[i].valuelen = (uint16_t)(end - hdbuf);
1115     nva[i].flags = NGHTTP2_NV_FLAG_NONE;
1116 
1117     hdbuf = end + 2;
1118     /* Inspect Content-Length header field and retrieve the request
1119        entity length so that we can set END_STREAM to the last DATA
1120        frame. */
1121     if(nva[i].namelen == 14 &&
1122        Curl_raw_nequal("content-length", (char*)nva[i].name, 14)) {
1123       size_t j;
1124       stream->upload_left = 0;
1125       for(j = 0; j < nva[i].valuelen; ++j) {
1126         stream->upload_left *= 10;
1127         stream->upload_left += nva[i].value[j] - '0';
1128       }
1129       DEBUGF(infof(conn->data,
1130                    "request content-length=%"
1131                    CURL_FORMAT_CURL_OFF_T
1132                    "\n", stream->upload_left));
1133     }
1134   }
1135 
1136   /* :authority must come before non-pseudo header fields */
1137   if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) {
1138     nghttp2_nv authority = nva[authority_idx];
1139     for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
1140       nva[i] = nva[i - 1];
1141     }
1142     nva[i] = authority;
1143   }
1144 
1145   switch(conn->data->set.httpreq) {
1146   case HTTPREQ_POST:
1147   case HTTPREQ_POST_FORM:
1148   case HTTPREQ_PUT:
1149     data_prd.read_callback = data_source_read_callback;
1150     data_prd.source.ptr = NULL;
1151     stream_id = nghttp2_submit_request(h2, NULL, nva, nheader,
1152                                        &data_prd, NULL);
1153     break;
1154   default:
1155     stream_id = nghttp2_submit_request(h2, NULL, nva, nheader,
1156                                        NULL, NULL);
1157   }
1158 
1159   free(nva);
1160 
1161   if(stream_id < 0) {
1162     DEBUGF(infof(conn->data, "http2_send() send error\n"));
1163     *err = CURLE_SEND_ERROR;
1164     return -1;
1165   }
1166 
1167   infof(conn->data, "Using Stream ID: %x (easy handle %p)\n",
1168         stream_id, conn->data);
1169   stream->stream_id = stream_id;
1170 
1171   /* put the SessionHandle in the hash with the stream_id as key */
1172   if(!Curl_hash_add(&httpc->streamsh, &stream->stream_id, sizeof(stream_id),
1173                     conn->data)) {
1174     failf(conn->data, "Couldn't add stream to hash!");
1175     *err = CURLE_OUT_OF_MEMORY;
1176     return -1;
1177   }
1178 
1179   rv = nghttp2_session_send(h2);
1180 
1181   if(rv != 0) {
1182     *err = CURLE_SEND_ERROR;
1183     return -1;
1184   }
1185 
1186   if(stream->stream_id != -1) {
1187     /* If whole HEADERS frame was sent off to the underlying socket,
1188        the nghttp2 library calls data_source_read_callback. But only
1189        it found that no data available, so it deferred the DATA
1190        transmission. Which means that nghttp2_session_want_write()
1191        returns 0 on http2_perform_getsock(), which results that no
1192        writable socket check is performed. To workaround this, we
1193        issue nghttp2_session_resume_data() here to bring back DATA
1194        transmission from deferred state. */
1195     nghttp2_session_resume_data(h2, stream->stream_id);
1196   }
1197 
1198   return len;
1199 }
1200 
Curl_http2_setup(struct connectdata * conn)1201 CURLcode Curl_http2_setup(struct connectdata *conn)
1202 {
1203   CURLcode result;
1204   struct http_conn *httpc = &conn->proto.httpc;
1205   struct HTTP *stream = conn->data->req.protop;
1206 
1207   stream->stream_id = -1;
1208 
1209   if(!stream->header_recvbuf)
1210     stream->header_recvbuf = Curl_add_buffer_init();
1211 
1212   if((conn->handler == &Curl_handler_http2_ssl) ||
1213      (conn->handler == &Curl_handler_http2))
1214     return CURLE_OK; /* already done */
1215 
1216   if(conn->handler->flags & PROTOPT_SSL)
1217     conn->handler = &Curl_handler_http2_ssl;
1218   else
1219     conn->handler = &Curl_handler_http2;
1220 
1221   result = Curl_http2_init(conn);
1222   if(result)
1223     return result;
1224 
1225   infof(conn->data, "Using HTTP2, server supports multi-use\n");
1226   stream->upload_left = 0;
1227   stream->upload_mem = NULL;
1228   stream->upload_len = 0;
1229 
1230   httpc->inbuflen = 0;
1231   httpc->nread_inbuf = 0;
1232 
1233   httpc->pause_stream_id = 0;
1234 
1235   conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
1236   conn->httpversion = 20;
1237   conn->bundle->multiuse = BUNDLE_MULTIPLEX;
1238 
1239   infof(conn->data, "Connection state changed (HTTP/2 confirmed)\n");
1240   Curl_multi_connchanged(conn->data->multi);
1241 
1242   return CURLE_OK;
1243 }
1244 
Curl_http2_switched(struct connectdata * conn,const char * mem,size_t nread)1245 CURLcode Curl_http2_switched(struct connectdata *conn,
1246                              const char *mem, size_t nread)
1247 {
1248   CURLcode result;
1249   struct http_conn *httpc = &conn->proto.httpc;
1250   int rv;
1251   ssize_t nproc;
1252   struct SessionHandle *data = conn->data;
1253   struct HTTP *stream = conn->data->req.protop;
1254 
1255   result = Curl_http2_setup(conn);
1256   if(result)
1257     return result;
1258 
1259   httpc->recv_underlying = (recving)conn->recv[FIRSTSOCKET];
1260   httpc->send_underlying = (sending)conn->send[FIRSTSOCKET];
1261   conn->recv[FIRSTSOCKET] = http2_recv;
1262   conn->send[FIRSTSOCKET] = http2_send;
1263 
1264   if(conn->data->req.upgr101 == UPGR101_RECEIVED) {
1265     /* stream 1 is opened implicitly on upgrade */
1266     stream->stream_id = 1;
1267     /* queue SETTINGS frame (again) */
1268     rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings,
1269                                  httpc->binlen, NULL);
1270     if(rv != 0) {
1271       failf(data, "nghttp2_session_upgrade() failed: %s(%d)",
1272             nghttp2_strerror(rv), rv);
1273       return CURLE_HTTP2;
1274     }
1275 
1276     /* put the SessionHandle in the hash with the stream->stream_id as key */
1277     if(!Curl_hash_add(&httpc->streamsh, &stream->stream_id,
1278                       sizeof(stream->stream_id), conn->data)) {
1279       failf(conn->data, "Couldn't add stream to hash!");
1280       return CURLE_OUT_OF_MEMORY;
1281     }
1282   }
1283   else {
1284     /* stream ID is unknown at this point */
1285     stream->stream_id = -1;
1286     rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE, NULL, 0);
1287     if(rv != 0) {
1288       failf(data, "nghttp2_submit_settings() failed: %s(%d)",
1289             nghttp2_strerror(rv), rv);
1290       return CURLE_HTTP2;
1291     }
1292   }
1293 
1294   /* we are going to copy mem to httpc->inbuf.  This is required since
1295      mem is part of buffer pointed by stream->mem, and callbacks
1296      called by nghttp2_session_mem_recv() will write stream specific
1297      data into stream->mem, overwriting data already there. */
1298   if(H2_BUFSIZE < nread) {
1299     failf(data, "connection buffer size is too small to store data following "
1300                 "HTTP Upgrade response header: buflen=%zu, datalen=%zu",
1301           H2_BUFSIZE, nread);
1302     return CURLE_HTTP2;
1303   }
1304 
1305   infof(conn->data, "Copying HTTP/2 data in stream buffer to connection buffer"
1306                     " after upgrade: len=%zu\n",
1307         nread);
1308 
1309   memcpy(httpc->inbuf, mem, nread);
1310   httpc->inbuflen = nread;
1311 
1312   nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf,
1313                                    httpc->inbuflen);
1314 
1315   if(nghttp2_is_fatal((int)nproc)) {
1316     failf(data, "nghttp2_session_mem_recv() failed: %s(%d)",
1317           nghttp2_strerror((int)nproc), (int)nproc);
1318     return CURLE_HTTP2;
1319   }
1320 
1321   DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", nproc));
1322 
1323   if((ssize_t)nread == nproc) {
1324     httpc->inbuflen = 0;
1325     httpc->nread_inbuf = 0;
1326   }
1327   else {
1328     httpc->nread_inbuf += nproc;
1329   }
1330 
1331   /* Try to send some frames since we may read SETTINGS already. */
1332   rv = nghttp2_session_send(httpc->h2);
1333 
1334   if(rv != 0) {
1335     failf(data, "nghttp2_session_send() failed: %s(%d)",
1336           nghttp2_strerror(rv), rv);
1337     return CURLE_HTTP2;
1338   }
1339 
1340   return CURLE_OK;
1341 }
1342 
1343 #endif
1344