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
47static 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
71static 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
79static 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 */
97void 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 */
123const 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
143const 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 */
167int 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 */
178static 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
208static 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
333static 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
346static 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
402static 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}
412static 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}
423static 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}
435static 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
473static 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. */
485static 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 */
511static 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
585static 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 */
641static 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
648static void freestreamentry(void *freethis)
649{
650  (void)freethis;
651}
652
653/*
654 * Initialize nghttp2 for a Curl connection
655 */
656CURLcode 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 */
725CURLcode 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 */
734CURLcode 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
776static 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 */
799static 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 */
992static 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
1201CURLcode 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
1245CURLcode 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