HTTP2: add layer between existing http and socket(TLS) layer
[platform/upstream/curl.git] / lib / http2.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2014, 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 #define _MPRINTF_REPLACE
27 #include <curl/mprintf.h>
28
29 #include <nghttp2/nghttp2.h>
30 #include "urldata.h"
31 #include "http2.h"
32 #include "http.h"
33 #include "sendf.h"
34 #include "curl_base64.h"
35 #include "curl_memory.h"
36 #include "rawstr.h"
37
38 /* include memdebug.h last */
39 #include "memdebug.h"
40
41 #if (NGHTTP2_VERSION_NUM < 0x000300)
42 #error too old nghttp2 version, upgrade!
43 #endif
44
45 /*
46  * HTTP2 handler interface. This isn't added to the general list of protocols
47  * but will be used at run-time when the protocol is dynamically switched from
48  * HTTP to HTTP2.
49  */
50 const struct Curl_handler Curl_handler_http2 = {
51   "HTTP2",                              /* scheme */
52   ZERO_NULL,                            /* setup_connection */
53   ZERO_NULL,                            /* do_it */
54   ZERO_NULL     ,                       /* done */
55   ZERO_NULL,                            /* do_more */
56   ZERO_NULL,                            /* connect_it */
57   ZERO_NULL,                            /* connecting */
58   ZERO_NULL,                            /* doing */
59   ZERO_NULL,                            /* proto_getsock */
60   ZERO_NULL,                            /* doing_getsock */
61   ZERO_NULL,                            /* domore_getsock */
62   ZERO_NULL,                            /* perform_getsock */
63   ZERO_NULL,                            /* disconnect */
64   ZERO_NULL,                            /* readwrite */
65   PORT_HTTP,                            /* defport */
66   CURLPROTO_HTTP,                       /* protocol */
67   PROTOPT_NONE                          /* flags */
68 };
69
70
71 /*
72  * Store nghttp2 version info in this buffer, Prefix with a space.  Return
73  * total length written.
74  */
75 int Curl_http2_ver(char *p, size_t len)
76 {
77   nghttp2_info *h2 = nghttp2_version(0);
78   return snprintf(p, len, " nghttp2/%s", h2->version_str);
79 }
80
81 /*
82  * The implementation of nghttp2_send_callback type. Here we write |data| with
83  * size |length| to the network and return the number of bytes actually
84  * written. See the documentation of nghttp2_send_callback for the details.
85  */
86 static ssize_t send_callback(nghttp2_session *h2,
87                              const uint8_t *data, size_t length, int flags,
88                              void *userp)
89 {
90   struct connectdata *conn = (struct connectdata *)userp;
91   struct http_conn *httpc = &conn->proto.httpc;
92   ssize_t written;
93   CURLcode rc;
94   (void)h2;
95   (void)flags;
96
97   rc = 0;
98   written = ((Curl_send*)httpc->send_underlying)(conn, FIRSTSOCKET,
99                                                  data, length, &rc);
100
101   if(rc == CURLE_AGAIN) {
102     return NGHTTP2_ERR_WOULDBLOCK;
103   }
104
105   if(written == -1) {
106     failf(conn->data, "Failed sending HTTP2 data");
107     return NGHTTP2_ERR_CALLBACK_FAILURE;
108   }
109
110   if(!written)
111     return NGHTTP2_ERR_WOULDBLOCK;
112
113   return written;
114 }
115
116 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
117                          void *userp)
118 {
119   struct connectdata *conn = (struct connectdata *)userp;
120   (void)session;
121   (void)frame;
122   infof(conn->data, "on_frame_recv() was called with header %x\n",
123         frame->hd.type);
124   if((frame->hd.type == NGHTTP2_HEADERS || frame->hd.type == NGHTTP2_DATA) &&
125      frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
126     infof(conn->data, "stream_id=%d closed\n", frame->hd.stream_id);
127   }
128   return 0;
129 }
130
131 static int on_invalid_frame_recv(nghttp2_session *session,
132                                  const nghttp2_frame *frame,
133                                  nghttp2_error_code error_code, void *userp)
134 {
135   struct connectdata *conn = (struct connectdata *)userp;
136   (void)session;
137   (void)frame;
138   infof(conn->data, "on_invalid_frame_recv() was called, error_code = %d\n",
139         error_code);
140   return 0;
141 }
142
143 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
144                               int32_t stream_id,
145                               const uint8_t *data, size_t len, void *userp)
146 {
147   struct connectdata *conn = (struct connectdata *)userp;
148   struct http_conn *c = &conn->proto.httpc;
149   (void)session;
150   (void)flags;
151   (void)data;
152   infof(conn->data, "on_data_chunk_recv() "
153         "len = %u, stream = %x\n", len, stream_id);
154
155   if(!c->bodystarted) {
156     memcpy(c->mem, "\r\n", 2); /* signal end of headers */
157     c->mem += 2;
158     c->len -= 2;
159     c->bodystarted = TRUE;
160   }
161
162   if(len <= c->len) {
163     memcpy(c->mem, data, len);
164     c->mem += len;
165     c->len -= len;
166   }
167   else {
168     infof(conn->data, "EEEEEEK: %d > %d\n", len, c->len);
169     /* return NGHTTP2_ERR_PAUSE; */
170   }
171
172   return 0;
173 }
174
175 static int before_frame_send(nghttp2_session *session,
176                              const nghttp2_frame *frame,
177                              void *userp)
178 {
179   struct connectdata *conn = (struct connectdata *)userp;
180   (void)session;
181   (void)frame;
182   infof(conn->data, "before_frame_send() was called\n");
183   return 0;
184 }
185 static int on_frame_send(nghttp2_session *session,
186                          const nghttp2_frame *frame,
187                          void *userp)
188 {
189   struct connectdata *conn = (struct connectdata *)userp;
190   (void)session;
191   (void)frame;
192   infof(conn->data, "on_frame_send() was called\n");
193   return 0;
194 }
195 static int on_frame_not_send(nghttp2_session *session,
196                              const nghttp2_frame *frame,
197                              int lib_error_code, void *userp)
198 {
199   struct connectdata *conn = (struct connectdata *)userp;
200   (void)session;
201   (void)frame;
202   infof(conn->data, "on_frame_not_send() was called, lib_error_code = %d\n",
203         lib_error_code);
204   return 0;
205 }
206 static int on_stream_close(nghttp2_session *session, int32_t stream_id,
207                            nghttp2_error_code error_code, void *userp)
208 {
209   struct connectdata *conn = (struct connectdata *)userp;
210   struct http_conn *c = &conn->proto.httpc;
211   (void)session;
212   (void)stream_id;
213   infof(conn->data, "on_stream_close() was called, error_code = %d\n",
214         error_code);
215
216   c->closed = TRUE;
217
218   return 0;
219 }
220
221 static int on_unknown_frame_recv(nghttp2_session *session,
222                                  const uint8_t *head, size_t headlen,
223                                  const uint8_t *payload, size_t payloadlen,
224                                  void *userp)
225 {
226   struct connectdata *conn = (struct connectdata *)userp;
227   (void)session;
228   (void)head;
229   (void)headlen;
230   (void)payload;
231   (void)payloadlen;
232   infof(conn->data, "on_unknown_frame_recv() was called\n");
233   return 0;
234 }
235 static int on_begin_headers(nghttp2_session *session,
236                             const nghttp2_frame *frame, void *userp)
237 {
238   struct connectdata *conn = (struct connectdata *)userp;
239   (void)session;
240   (void)frame;
241   infof(conn->data, "on_begin_headers() was called\n");
242   return 0;
243 }
244
245 /* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
246 static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
247                       const uint8_t *name, size_t namelen,
248                       const uint8_t *value, size_t valuelen,
249                       void *userp)
250 {
251   struct connectdata *conn = (struct connectdata *)userp;
252   struct http_conn *c = &conn->proto.httpc;
253   size_t hlen = namelen + valuelen + 3; /* colon + CRLF == 3 bytes */
254
255   (void)session;
256   (void)frame;
257
258   if(namelen && (name[0] == ':')) {
259     /* special case */
260     hlen = snprintf(c->mem, c->len, "HTTP/2.0 %s\r\n", value);
261   }
262   else if(hlen + 1 < c->len) { /* hlen + a zero byte */
263     /* convert to a HTTP1-style header */
264     memcpy(c->mem, name, namelen);
265     c->mem[namelen]=':';
266     memcpy(&c->mem[namelen+1], value, valuelen);
267     c->mem[namelen + valuelen + 1]='\r';
268     c->mem[namelen + valuelen + 2]='\n';
269     c->mem[namelen + valuelen + 3]=0; /* to display this easier */
270   }
271   c->mem += hlen;
272   c->len -= hlen;
273
274   return 0; /* 0 is successful */
275 }
276
277 /*
278  * This is all callbacks nghttp2 calls
279  */
280 static const nghttp2_session_callbacks callbacks = {
281   send_callback,         /* nghttp2_send_callback */
282   NULL,                  /* nghttp2_recv_callback */
283   on_frame_recv,         /* nghttp2_on_frame_recv_callback */
284   on_invalid_frame_recv, /* nghttp2_on_invalid_frame_recv_callback */
285   on_data_chunk_recv,    /* nghttp2_on_data_chunk_recv_callback */
286   before_frame_send,     /* nghttp2_before_frame_send_callback */
287   on_frame_send,         /* nghttp2_on_frame_send_callback */
288   on_frame_not_send,     /* nghttp2_on_frame_not_send_callback */
289   on_stream_close,       /* nghttp2_on_stream_close_callback */
290   on_unknown_frame_recv, /* nghttp2_on_unknown_frame_recv_callback */
291   on_begin_headers,      /* nghttp2_on_begin_headers_callback */
292   on_header              /* nghttp2_on_header_callback */
293 };
294
295 /*
296  * The HTTP2 settings we send in the Upgrade request
297  */
298 static nghttp2_settings_entry settings[] = {
299   { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 },
300   { NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, NGHTTP2_INITIAL_WINDOW_SIZE },
301 };
302
303 /*
304  * Initialize nghttp2 for a Curl connection
305  */
306 CURLcode Curl_http2_init(struct connectdata *conn) {
307   if(!conn->proto.httpc.h2) {
308     /* The nghttp2 session is not yet setup, do it */
309     int rc = nghttp2_session_client_new(&conn->proto.httpc.h2,
310                                         &callbacks, conn);
311     if(rc) {
312       failf(conn->data, "Couldn't initialize nghttp2!");
313       return CURLE_OUT_OF_MEMORY; /* most likely at least */
314     }
315   }
316   return CURLE_OK;
317 }
318
319 /*
320  * Send a request using http2
321  */
322 CURLcode Curl_http2_send_request(struct connectdata *conn)
323 {
324   (void)conn;
325   return CURLE_OK;
326 }
327
328 /*
329  * Append headers to ask for a HTTP1.1 to HTTP2 upgrade.
330  */
331 CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req,
332                                     struct connectdata *conn)
333 {
334   CURLcode result;
335   ssize_t binlen;
336   char *base64;
337   size_t blen;
338   struct SingleRequest *k = &conn->data->req;
339   uint8_t *binsettings = conn->proto.httpc.binsettings;
340
341   Curl_http2_init(conn);
342
343   /* As long as we have a fixed set of settings, we don't have to dynamically
344    * figure out the base64 strings since it'll always be the same. However,
345    * the settings will likely not be fixed every time in the future.
346    */
347
348   /* this returns number of bytes it wrote */
349   binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
350                                          settings,
351                                          sizeof(settings)/sizeof(settings[0]));
352   if(!binlen) {
353     failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
354     return CURLE_FAILED_INIT;
355   }
356   conn->proto.httpc.binlen = binlen;
357
358   result = Curl_base64_encode(conn->data, (const char *)binsettings, binlen,
359                               &base64, &blen);
360   if(result)
361     return result;
362
363   result = Curl_add_bufferf(req,
364                             "Connection: Upgrade, HTTP2-Settings\r\n"
365                             "Upgrade: %s\r\n"
366                             "HTTP2-Settings: %s\r\n",
367                             NGHTTP2_PROTO_VERSION_ID, base64);
368   free(base64);
369
370   k->upgr101 = UPGR101_REQUESTED;
371
372   return result;
373 }
374
375 #define H2_BUFSIZE 4096
376
377 /*
378  * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
379  * a regular CURLcode value.
380  */
381 static ssize_t http2_recv(struct connectdata *conn, int sockindex,
382                           char *mem, size_t len, CURLcode *err)
383 {
384   CURLcode rc;
385   ssize_t rv;
386   ssize_t nread;
387   char inbuf[H2_BUFSIZE];
388   struct http_conn *httpc = &conn->proto.httpc;
389
390   (void)sockindex; /* we always do HTTP2 on sockindex 0 */
391
392   conn->proto.httpc.mem = mem;
393   conn->proto.httpc.len = len;
394
395   infof(conn->data, "http2_recv: %d bytes buffer\n",
396         conn->proto.httpc.len);
397
398   rc = 0;
399   nread = ((Curl_recv*)httpc->recv_underlying)(conn, FIRSTSOCKET,
400                                                inbuf, H2_BUFSIZE, &rc);
401
402   if(rc == CURLE_AGAIN) {
403     *err = rc;
404     return -1;
405   }
406
407   if(nread == -1) {
408     failf(conn->data, "Failed receiving HTTP2 data");
409     *err = rc;
410     return 0;
411   }
412
413   infof(conn->data, "nread=%zd\n", nread);
414   rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
415
416   if(nghttp2_is_fatal((int)rv)) {
417     failf(conn->data, "nghttp2_session_mem_recv() returned %d:%s\n",
418           rv, nghttp2_strerror((int)rv));
419     *err = CURLE_RECV_ERROR;
420     return 0;
421   }
422   infof(conn->data, "nghttp2_session_mem_recv() returns %zd\n", rv);
423   /* Always send pending frames in nghttp2 session, because
424      nghttp2_session_mem_recv() may queue new frame */
425   rv = nghttp2_session_send(httpc->h2);
426   if(rv != 0) {
427     *err = CURLE_SEND_ERROR;
428     return 0;
429   }
430   if(len != httpc->len) {
431     return len - conn->proto.httpc.len;
432   }
433   /* If stream is closed, return 0 to signal the http routine to close
434      the connection */
435   if(httpc->closed) {
436     return 0;
437   }
438   *err = CURLE_AGAIN;
439   return -1;
440 }
441
442 #define MAKE_NV(k, v)                                           \
443   { (uint8_t*)k, (uint8_t*)v, sizeof(k) - 1, sizeof(v) - 1 }
444
445 #define MAKE_NV2(k, v, vlen)                            \
446   { (uint8_t*)k, (uint8_t*)v, sizeof(k) - 1, vlen }
447
448 /* return number of received (decrypted) bytes */
449 static ssize_t http2_send(struct connectdata *conn, int sockindex,
450                           const void *mem, size_t len, CURLcode *err)
451 {
452   /*
453    * BIG TODO: Currently, we send request in this function, but this
454    * function is also used to send request body. It would be nice to
455    * add dedicated function for request.
456    */
457   int rv;
458   struct http_conn *httpc = &conn->proto.httpc;
459   nghttp2_nv *nva;
460   size_t nheader;
461   size_t i;
462   char *hdbuf = (char*)mem;
463   char *end;
464   (void)sockindex;
465
466   infof(conn->data, "http2_send len=%zu\n", len);
467
468   /* Calculate number of headers contained in [mem, mem + len) */
469   /* Here, we assume the curl http code generate *correct* HTTP header
470      field block */
471   nheader = 0;
472   for(i = 0; i < len; ++i) {
473     if(hdbuf[i] == 0x0a) {
474       ++nheader;
475     }
476   }
477   /* We counted additional 2 \n in the first and last line. We need 3
478      new headers: :method, :path and :scheme. Therefore we need one
479      more space. */
480   nheader += 1;
481   nva = malloc(sizeof(nghttp2_nv) * nheader);
482   if(nva == NULL) {
483     *err = CURLE_OUT_OF_MEMORY;
484     return -1;
485   }
486   /* Extract :method, :path from request line */
487   end = strchr(hdbuf, ' ');
488   nva[0].name = (unsigned char *)":method";
489   nva[0].namelen = (uint16_t)strlen((char *)nva[0].name);
490   nva[0].value = (unsigned char *)hdbuf;
491   nva[0].valuelen = (uint16_t)(end - hdbuf);
492
493   hdbuf = end + 1;
494
495   end = strchr(hdbuf, ' ');
496   nva[1].name = (unsigned char *)":path";
497   nva[1].namelen = (uint16_t)strlen((char *)nva[1].name);
498   nva[1].value = (unsigned char *)hdbuf;
499   nva[1].valuelen = (uint16_t)(end - hdbuf);
500
501   nva[2].name = (unsigned char *)":scheme";
502   nva[2].namelen = (uint16_t)strlen((char *)nva[2].name);
503   if(conn->handler->flags & PROTOPT_SSL)
504     nva[2].value = (unsigned char *)"https";
505   else
506     nva[2].value = (unsigned char *)"http";
507   nva[2].valuelen = (uint16_t)strlen((char *)nva[2].value);
508
509   hdbuf = strchr(hdbuf, 0x0a);
510   ++hdbuf;
511
512   for(i = 3; i < nheader; ++i) {
513     end = strchr(hdbuf, ':');
514     assert(end);
515     if(end - hdbuf == 4 && Curl_raw_nequal("host", hdbuf, 4)) {
516       nva[i].name = (unsigned char *)":authority";
517       nva[i].namelen = (uint16_t)strlen((char *)nva[i].name);
518     }
519     else {
520       nva[i].name = (unsigned char *)hdbuf;
521       nva[i].namelen = (uint16_t)(end - hdbuf);
522     }
523     hdbuf = end + 1;
524     for(; *hdbuf == ' '; ++hdbuf);
525     end = strchr(hdbuf, 0x0d);
526     assert(end);
527     nva[i].value = (unsigned char *)hdbuf;
528     nva[i].valuelen = (uint16_t)(end - hdbuf);
529
530     hdbuf = end + 2;
531   }
532
533   rv = nghttp2_submit_request(httpc->h2, 0, nva, nheader, NULL, NULL);
534
535   free(nva);
536
537   if(rv != 0) {
538     *err = CURLE_SEND_ERROR;
539     return -1;
540   }
541
542   rv = nghttp2_session_send(httpc->h2);
543
544   if(rv != 0) {
545     *err = CURLE_SEND_ERROR;
546     return -1;
547   }
548
549   /* TODO: Still whole HEADERS frame may have not been sent because of
550      EAGAIN. But I don't know how to setup to call
551      nghttp2_session_send() when socket becomes writable. */
552
553   return len;
554 }
555
556 int Curl_http2_switched(struct connectdata *conn)
557 {
558   int rv;
559   CURLcode rc;
560   struct http_conn *httpc = &conn->proto.httpc;
561   /* we are switched! */
562   /* Don't know this is needed here at this moment. Original
563      handler->flags is still useful. */
564   /* conn->handler = &Curl_handler_http2; */
565   httpc->recv_underlying = (recving)conn->recv[FIRSTSOCKET];
566   httpc->send_underlying = (sending)conn->send[FIRSTSOCKET];
567   conn->recv[FIRSTSOCKET] = http2_recv;
568   conn->send[FIRSTSOCKET] = http2_send;
569   infof(conn->data, "We have switched to HTTP2\n");
570   httpc->bodystarted = FALSE;
571   httpc->closed = FALSE;
572
573   /* TODO: May get CURLE_AGAIN */
574   rv = (int) ((Curl_send*)httpc->send_underlying)
575     (conn, FIRSTSOCKET,
576      NGHTTP2_CLIENT_CONNECTION_HEADER,
577      NGHTTP2_CLIENT_CONNECTION_HEADER_LEN,
578      &rc);
579   assert(rv == 24);
580   if(conn->data->req.upgr101 == UPGR101_RECEIVED) {
581     /* queue SETTINGS frame (again) */
582     rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings,
583                                  httpc->binlen, NULL);
584     if(rv != 0) {
585       failf(conn->data, "nghttp2_session_upgrade() failed: %s(%d)",
586             nghttp2_strerror(rv), rv);
587       return -1;
588     }
589   }
590   else {
591     rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE, NULL, 0);
592     if(rv != 0) {
593       failf(conn->data, "nghttp2_submit_settings() failed: %s(%d)",
594             nghttp2_strerror(rv), rv);
595       return -1;
596     }
597   }
598   return 0;
599 }
600
601 #endif