http2: store response header in temporary buffer
[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   struct http_conn *c = &conn->proto.httpc;
121   (void)session;
122   (void)frame;
123   infof(conn->data, "on_frame_recv() was called with header %x\n",
124         frame->hd.type);
125   if(frame->hd.type == NGHTTP2_HEADERS &&
126      frame->headers.cat == NGHTTP2_HCAT_RESPONSE) {
127     c->bodystarted = TRUE;
128     Curl_add_buffer(c->header_recvbuf, "\r\n", 2);
129     c->nread_header_recvbuf = c->len < c->header_recvbuf->size_used ?
130       c->len : c->header_recvbuf->size_used;
131
132     memcpy(c->mem, c->header_recvbuf->buffer, c->nread_header_recvbuf);
133
134     c->mem += c->nread_header_recvbuf;
135     c->len -= c->nread_header_recvbuf;
136   }
137   if((frame->hd.type == NGHTTP2_HEADERS || frame->hd.type == NGHTTP2_DATA) &&
138      frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
139     infof(conn->data, "stream_id=%d closed\n", frame->hd.stream_id);
140   }
141   return 0;
142 }
143
144 static int on_invalid_frame_recv(nghttp2_session *session,
145                                  const nghttp2_frame *frame,
146                                  nghttp2_error_code error_code, void *userp)
147 {
148   struct connectdata *conn = (struct connectdata *)userp;
149   (void)session;
150   (void)frame;
151   infof(conn->data, "on_invalid_frame_recv() was called, error_code = %d\n",
152         error_code);
153   return 0;
154 }
155
156 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
157                               int32_t stream_id,
158                               const uint8_t *data, size_t len, void *userp)
159 {
160   struct connectdata *conn = (struct connectdata *)userp;
161   struct http_conn *c = &conn->proto.httpc;
162   (void)session;
163   (void)flags;
164   (void)data;
165   infof(conn->data, "on_data_chunk_recv() "
166         "len = %u, stream = %x\n", len, stream_id);
167
168   if(len <= c->len) {
169     memcpy(c->mem, data, len);
170     c->mem += len;
171     c->len -= len;
172   }
173   else {
174     infof(conn->data, "EEEEEEK: %d > %d\n", len, c->len);
175     /* return NGHTTP2_ERR_PAUSE; */
176   }
177
178   return 0;
179 }
180
181 static int before_frame_send(nghttp2_session *session,
182                              const nghttp2_frame *frame,
183                              void *userp)
184 {
185   struct connectdata *conn = (struct connectdata *)userp;
186   (void)session;
187   (void)frame;
188   infof(conn->data, "before_frame_send() was called\n");
189   return 0;
190 }
191 static int on_frame_send(nghttp2_session *session,
192                          const nghttp2_frame *frame,
193                          void *userp)
194 {
195   struct connectdata *conn = (struct connectdata *)userp;
196   (void)session;
197   (void)frame;
198   infof(conn->data, "on_frame_send() was called\n");
199   return 0;
200 }
201 static int on_frame_not_send(nghttp2_session *session,
202                              const nghttp2_frame *frame,
203                              int lib_error_code, void *userp)
204 {
205   struct connectdata *conn = (struct connectdata *)userp;
206   (void)session;
207   (void)frame;
208   infof(conn->data, "on_frame_not_send() was called, lib_error_code = %d\n",
209         lib_error_code);
210   return 0;
211 }
212 static int on_stream_close(nghttp2_session *session, int32_t stream_id,
213                            nghttp2_error_code error_code, void *userp)
214 {
215   struct connectdata *conn = (struct connectdata *)userp;
216   struct http_conn *c = &conn->proto.httpc;
217   (void)session;
218   (void)stream_id;
219   infof(conn->data, "on_stream_close() was called, error_code = %d\n",
220         error_code);
221
222   c->closed = TRUE;
223
224   return 0;
225 }
226
227 static int on_unknown_frame_recv(nghttp2_session *session,
228                                  const uint8_t *head, size_t headlen,
229                                  const uint8_t *payload, size_t payloadlen,
230                                  void *userp)
231 {
232   struct connectdata *conn = (struct connectdata *)userp;
233   (void)session;
234   (void)head;
235   (void)headlen;
236   (void)payload;
237   (void)payloadlen;
238   infof(conn->data, "on_unknown_frame_recv() was called\n");
239   return 0;
240 }
241 static int on_begin_headers(nghttp2_session *session,
242                             const nghttp2_frame *frame, void *userp)
243 {
244   struct connectdata *conn = (struct connectdata *)userp;
245   (void)session;
246   (void)frame;
247   infof(conn->data, "on_begin_headers() was called\n");
248   return 0;
249 }
250
251 static const char STATUS[] = ":status";
252
253 /* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
254 static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
255                       const uint8_t *name, size_t namelen,
256                       const uint8_t *value, size_t valuelen,
257                       void *userp)
258 {
259   struct connectdata *conn = (struct connectdata *)userp;
260   struct http_conn *c = &conn->proto.httpc;
261   (void)session;
262   (void)frame;
263
264   if(namelen == sizeof(":status") - 1 &&
265      memcmp(STATUS, name, namelen) == 0) {
266     snprintf(c->header_recvbuf->buffer, 13, "HTTP/2.0 %s", value);
267     c->header_recvbuf->buffer[12] = '\r';
268     return 0;
269   }
270   else {
271     /* convert to a HTTP1-style header */
272     infof(conn->data, "got header\n");
273     Curl_add_buffer(c->header_recvbuf, name, namelen);
274     Curl_add_buffer(c->header_recvbuf, ":", 1);
275     Curl_add_buffer(c->header_recvbuf, value, valuelen);
276     Curl_add_buffer(c->header_recvbuf, "\r\n", 2);
277   }
278
279   return 0; /* 0 is successful */
280 }
281
282 /*
283  * This is all callbacks nghttp2 calls
284  */
285 static const nghttp2_session_callbacks callbacks = {
286   send_callback,         /* nghttp2_send_callback */
287   NULL,                  /* nghttp2_recv_callback */
288   on_frame_recv,         /* nghttp2_on_frame_recv_callback */
289   on_invalid_frame_recv, /* nghttp2_on_invalid_frame_recv_callback */
290   on_data_chunk_recv,    /* nghttp2_on_data_chunk_recv_callback */
291   before_frame_send,     /* nghttp2_before_frame_send_callback */
292   on_frame_send,         /* nghttp2_on_frame_send_callback */
293   on_frame_not_send,     /* nghttp2_on_frame_not_send_callback */
294   on_stream_close,       /* nghttp2_on_stream_close_callback */
295   on_unknown_frame_recv, /* nghttp2_on_unknown_frame_recv_callback */
296   on_begin_headers,      /* nghttp2_on_begin_headers_callback */
297   on_header              /* nghttp2_on_header_callback */
298 };
299
300 /*
301  * The HTTP2 settings we send in the Upgrade request
302  */
303 static nghttp2_settings_entry settings[] = {
304   { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 },
305   { NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, NGHTTP2_INITIAL_WINDOW_SIZE },
306 };
307
308 /*
309  * Initialize nghttp2 for a Curl connection
310  */
311 CURLcode Curl_http2_init(struct connectdata *conn) {
312   if(!conn->proto.httpc.h2) {
313     /* The nghttp2 session is not yet setup, do it */
314     int rc = nghttp2_session_client_new(&conn->proto.httpc.h2,
315                                         &callbacks, conn);
316     if(rc) {
317       failf(conn->data, "Couldn't initialize nghttp2!");
318       return CURLE_OUT_OF_MEMORY; /* most likely at least */
319     }
320   }
321   return CURLE_OK;
322 }
323
324 /*
325  * Send a request using http2
326  */
327 CURLcode Curl_http2_send_request(struct connectdata *conn)
328 {
329   (void)conn;
330   return CURLE_OK;
331 }
332
333 /*
334  * Append headers to ask for a HTTP1.1 to HTTP2 upgrade.
335  */
336 CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req,
337                                     struct connectdata *conn)
338 {
339   CURLcode result;
340   ssize_t binlen;
341   char *base64;
342   size_t blen;
343   struct SingleRequest *k = &conn->data->req;
344   uint8_t *binsettings = conn->proto.httpc.binsettings;
345
346   Curl_http2_init(conn);
347
348   /* As long as we have a fixed set of settings, we don't have to dynamically
349    * figure out the base64 strings since it'll always be the same. However,
350    * the settings will likely not be fixed every time in the future.
351    */
352
353   /* this returns number of bytes it wrote */
354   binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
355                                          settings,
356                                          sizeof(settings)/sizeof(settings[0]));
357   if(!binlen) {
358     failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
359     return CURLE_FAILED_INIT;
360   }
361   conn->proto.httpc.binlen = binlen;
362
363   result = Curl_base64_encode(conn->data, (const char *)binsettings, binlen,
364                               &base64, &blen);
365   if(result)
366     return result;
367
368   result = Curl_add_bufferf(req,
369                             "Connection: Upgrade, HTTP2-Settings\r\n"
370                             "Upgrade: %s\r\n"
371                             "HTTP2-Settings: %s\r\n",
372                             NGHTTP2_PROTO_VERSION_ID, base64);
373   free(base64);
374
375   k->upgr101 = UPGR101_REQUESTED;
376
377   return result;
378 }
379
380 #define H2_BUFSIZE 4096
381
382 /*
383  * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
384  * a regular CURLcode value.
385  */
386 static ssize_t http2_recv(struct connectdata *conn, int sockindex,
387                           char *mem, size_t len, CURLcode *err)
388 {
389   CURLcode rc;
390   ssize_t rv;
391   ssize_t nread;
392   char inbuf[H2_BUFSIZE];
393   struct http_conn *httpc = &conn->proto.httpc;
394
395   (void)sockindex; /* we always do HTTP2 on sockindex 0 */
396
397   if(httpc->bodystarted &&
398      httpc->nread_header_recvbuf < httpc->header_recvbuf->size_used) {
399     size_t left =
400       httpc->header_recvbuf->size_used - httpc->nread_header_recvbuf;
401     size_t ncopy = len < left ? len : left;
402     memcpy(mem, httpc->header_recvbuf->buffer + httpc->nread_header_recvbuf,
403            ncopy);
404     httpc->nread_header_recvbuf += ncopy;
405     return ncopy;
406   }
407
408   conn->proto.httpc.mem = mem;
409   conn->proto.httpc.len = len;
410
411   infof(conn->data, "http2_recv: %d bytes buffer\n",
412         conn->proto.httpc.len);
413
414   rc = 0;
415   nread = ((Curl_recv*)httpc->recv_underlying)(conn, FIRSTSOCKET,
416                                                inbuf, H2_BUFSIZE, &rc);
417
418   if(rc == CURLE_AGAIN) {
419     *err = rc;
420     return -1;
421   }
422
423   if(nread == -1) {
424     failf(conn->data, "Failed receiving HTTP2 data");
425     *err = rc;
426     return 0;
427   }
428
429   infof(conn->data, "nread=%zd\n", nread);
430   rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
431
432   if(nghttp2_is_fatal((int)rv)) {
433     failf(conn->data, "nghttp2_session_mem_recv() returned %d:%s\n",
434           rv, nghttp2_strerror((int)rv));
435     *err = CURLE_RECV_ERROR;
436     return 0;
437   }
438   infof(conn->data, "nghttp2_session_mem_recv() returns %zd\n", rv);
439   /* Always send pending frames in nghttp2 session, because
440      nghttp2_session_mem_recv() may queue new frame */
441   rv = nghttp2_session_send(httpc->h2);
442   if(rv != 0) {
443     *err = CURLE_SEND_ERROR;
444     return 0;
445   }
446   if(len != httpc->len) {
447     return len - conn->proto.httpc.len;
448   }
449   /* If stream is closed, return 0 to signal the http routine to close
450      the connection */
451   if(httpc->closed) {
452     return 0;
453   }
454   *err = CURLE_AGAIN;
455   return -1;
456 }
457
458 #define MAKE_NV(k, v)                                           \
459   { (uint8_t*)k, (uint8_t*)v, sizeof(k) - 1, sizeof(v) - 1 }
460
461 #define MAKE_NV2(k, v, vlen)                            \
462   { (uint8_t*)k, (uint8_t*)v, sizeof(k) - 1, vlen }
463
464 /* return number of received (decrypted) bytes */
465 static ssize_t http2_send(struct connectdata *conn, int sockindex,
466                           const void *mem, size_t len, CURLcode *err)
467 {
468   /*
469    * BIG TODO: Currently, we send request in this function, but this
470    * function is also used to send request body. It would be nice to
471    * add dedicated function for request.
472    */
473   int rv;
474   struct http_conn *httpc = &conn->proto.httpc;
475   nghttp2_nv *nva;
476   size_t nheader;
477   size_t i;
478   char *hdbuf = (char*)mem;
479   char *end;
480   (void)sockindex;
481
482   infof(conn->data, "http2_send len=%zu\n", len);
483
484   /* Calculate number of headers contained in [mem, mem + len) */
485   /* Here, we assume the curl http code generate *correct* HTTP header
486      field block */
487   nheader = 0;
488   for(i = 0; i < len; ++i) {
489     if(hdbuf[i] == 0x0a) {
490       ++nheader;
491     }
492   }
493   /* We counted additional 2 \n in the first and last line. We need 3
494      new headers: :method, :path and :scheme. Therefore we need one
495      more space. */
496   nheader += 1;
497   nva = malloc(sizeof(nghttp2_nv) * nheader);
498   if(nva == NULL) {
499     *err = CURLE_OUT_OF_MEMORY;
500     return -1;
501   }
502   /* Extract :method, :path from request line */
503   end = strchr(hdbuf, ' ');
504   nva[0].name = (unsigned char *)":method";
505   nva[0].namelen = (uint16_t)strlen((char *)nva[0].name);
506   nva[0].value = (unsigned char *)hdbuf;
507   nva[0].valuelen = (uint16_t)(end - hdbuf);
508
509   hdbuf = end + 1;
510
511   end = strchr(hdbuf, ' ');
512   nva[1].name = (unsigned char *)":path";
513   nva[1].namelen = (uint16_t)strlen((char *)nva[1].name);
514   nva[1].value = (unsigned char *)hdbuf;
515   nva[1].valuelen = (uint16_t)(end - hdbuf);
516
517   nva[2].name = (unsigned char *)":scheme";
518   nva[2].namelen = (uint16_t)strlen((char *)nva[2].name);
519   if(conn->handler->flags & PROTOPT_SSL)
520     nva[2].value = (unsigned char *)"https";
521   else
522     nva[2].value = (unsigned char *)"http";
523   nva[2].valuelen = (uint16_t)strlen((char *)nva[2].value);
524
525   hdbuf = strchr(hdbuf, 0x0a);
526   ++hdbuf;
527
528   for(i = 3; i < nheader; ++i) {
529     end = strchr(hdbuf, ':');
530     assert(end);
531     if(end - hdbuf == 4 && Curl_raw_nequal("host", hdbuf, 4)) {
532       nva[i].name = (unsigned char *)":authority";
533       nva[i].namelen = (uint16_t)strlen((char *)nva[i].name);
534     }
535     else {
536       nva[i].name = (unsigned char *)hdbuf;
537       nva[i].namelen = (uint16_t)(end - hdbuf);
538     }
539     hdbuf = end + 1;
540     for(; *hdbuf == ' '; ++hdbuf);
541     end = strchr(hdbuf, 0x0d);
542     assert(end);
543     nva[i].value = (unsigned char *)hdbuf;
544     nva[i].valuelen = (uint16_t)(end - hdbuf);
545
546     hdbuf = end + 2;
547   }
548
549   rv = nghttp2_submit_request(httpc->h2, 0, nva, nheader, NULL, NULL);
550
551   free(nva);
552
553   if(rv != 0) {
554     *err = CURLE_SEND_ERROR;
555     return -1;
556   }
557
558   rv = nghttp2_session_send(httpc->h2);
559
560   if(rv != 0) {
561     *err = CURLE_SEND_ERROR;
562     return -1;
563   }
564
565   /* TODO: Still whole HEADERS frame may have not been sent because of
566      EAGAIN. But I don't know how to setup to call
567      nghttp2_session_send() when socket becomes writable. */
568
569   return len;
570 }
571
572 int Curl_http2_switched(struct connectdata *conn)
573 {
574   int rv;
575   CURLcode rc;
576   struct http_conn *httpc = &conn->proto.httpc;
577   /* we are switched! */
578   /* Don't know this is needed here at this moment. Original
579      handler->flags is still useful. */
580   /* conn->handler = &Curl_handler_http2; */
581   httpc->recv_underlying = (recving)conn->recv[FIRSTSOCKET];
582   httpc->send_underlying = (sending)conn->send[FIRSTSOCKET];
583   conn->recv[FIRSTSOCKET] = http2_recv;
584   conn->send[FIRSTSOCKET] = http2_send;
585   infof(conn->data, "We have switched to HTTP2\n");
586   httpc->bodystarted = FALSE;
587   httpc->closed = FALSE;
588   httpc->header_recvbuf = Curl_add_buffer_init();
589   httpc->nread_header_recvbuf = 0;
590
591   /* Put place holder for status line */
592   Curl_add_buffer(httpc->header_recvbuf, "HTTP/2.0 200\r\n", 14);
593
594   /* TODO: May get CURLE_AGAIN */
595   rv = (int) ((Curl_send*)httpc->send_underlying)
596     (conn, FIRSTSOCKET,
597      NGHTTP2_CLIENT_CONNECTION_HEADER,
598      NGHTTP2_CLIENT_CONNECTION_HEADER_LEN,
599      &rc);
600   assert(rv == 24);
601   if(conn->data->req.upgr101 == UPGR101_RECEIVED) {
602     /* queue SETTINGS frame (again) */
603     rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings,
604                                  httpc->binlen, NULL);
605     if(rv != 0) {
606       failf(conn->data, "nghttp2_session_upgrade() failed: %s(%d)",
607             nghttp2_strerror(rv), rv);
608       return -1;
609     }
610   }
611   else {
612     rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE, NULL, 0);
613     if(rv != 0) {
614       failf(conn->data, "nghttp2_submit_settings() failed: %s(%d)",
615             nghttp2_strerror(rv), rv);
616       return -1;
617     }
618   }
619   return 0;
620 }
621
622 #endif