http2: convert HEADER frames to HTTP1-like headers
[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
37 /* include memdebug.h last */
38 #include "memdebug.h"
39
40 #if (NGHTTP2_VERSION_NUM < 0x000300)
41 #error too old nghttp2 version, upgrade!
42 #endif
43
44 /*
45  * HTTP2 handler interface. This isn't added to the general list of protocols
46  * but will be used at run-time when the protocol is dynamically switched from
47  * HTTP to HTTP2.
48  */
49 const struct Curl_handler Curl_handler_http2 = {
50   "HTTP2",                              /* scheme */
51   ZERO_NULL,                            /* setup_connection */
52   ZERO_NULL,                            /* do_it */
53   ZERO_NULL     ,                       /* done */
54   ZERO_NULL,                            /* do_more */
55   ZERO_NULL,                            /* connect_it */
56   ZERO_NULL,                            /* connecting */
57   ZERO_NULL,                            /* doing */
58   ZERO_NULL,                            /* proto_getsock */
59   ZERO_NULL,                            /* doing_getsock */
60   ZERO_NULL,                            /* domore_getsock */
61   ZERO_NULL,                            /* perform_getsock */
62   ZERO_NULL,                            /* disconnect */
63   ZERO_NULL,                            /* readwrite */
64   PORT_HTTP,                            /* defport */
65   CURLPROTO_HTTP,                       /* protocol */
66   PROTOPT_NONE                          /* flags */
67 };
68
69
70 /*
71  * Store nghttp2 version info in this buffer, Prefix with a space.  Return
72  * total length written.
73  */
74 int Curl_http2_ver(char *p, size_t len)
75 {
76   nghttp2_info *h2 = nghttp2_version(0);
77   return snprintf(p, len, " nghttp2/%s", h2->version_str);
78 }
79
80 /*
81  * The implementation of nghttp2_send_callback type. Here we write |data| with
82  * size |length| to the network and return the number of bytes actually
83  * written. See the documentation of nghttp2_send_callback for the details.
84  */
85 static ssize_t send_callback(nghttp2_session *h2,
86                              const uint8_t *data, size_t length, int flags,
87                              void *userp)
88 {
89   struct connectdata *conn = (struct connectdata *)userp;
90   ssize_t written;
91   CURLcode rc =
92     Curl_write(conn, conn->sock[FIRSTSOCKET], data, length, &written);
93   (void)h2;
94   (void)flags;
95
96   if(rc) {
97     failf(conn->data, "Failed sending HTTP2 data");
98     return NGHTTP2_ERR_CALLBACK_FAILURE;
99   }
100   else if(!written)
101     return NGHTTP2_ERR_WOULDBLOCK;
102
103   return written;
104 }
105
106 /*
107  * The implementation of nghttp2_recv_callback type. Here we read data from
108  * the network and write them in |buf|. The capacity of |buf| is |length|
109  * bytes. Returns the number of bytes stored in |buf|. See the documentation
110  * of nghttp2_recv_callback for the details.
111  */
112 static ssize_t recv_callback(nghttp2_session *h2,
113                              uint8_t *buf, size_t length, int flags,
114                              void *userp)
115 {
116   struct connectdata *conn = (struct connectdata *)userp;
117   ssize_t nread;
118   CURLcode rc;
119
120   infof(conn->data, "recv_callback() was called with length %d\n", length);
121
122   rc = Curl_read_plain(conn->sock[FIRSTSOCKET], (char *)buf, length,
123                        &nread);
124   (void)h2;
125   (void)flags;
126
127   if(CURLE_AGAIN == rc) {
128     infof(conn->data, "recv_callback() returns NGHTTP2_ERR_WOULDBLOCK\n");
129     return NGHTTP2_ERR_WOULDBLOCK;
130   }
131   else if(rc) {
132     failf(conn->data, "Failed receiving HTTP2 data: %d", rc);
133     return NGHTTP2_ERR_CALLBACK_FAILURE;
134   }
135   else
136     infof(conn->data, "recv_callback() returns %d to nghttp2\n", nread);
137
138   return nread;
139 }
140
141 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
142                          void *userp)
143 {
144   struct connectdata *conn = (struct connectdata *)userp;
145   (void)session;
146   (void)frame;
147   infof(conn->data, "on_frame_recv() was called with header %x\n",
148         frame->hd.type);
149   return 0;
150 }
151
152 static int on_invalid_frame_recv(nghttp2_session *session,
153                                  const nghttp2_frame *frame,
154                                  nghttp2_error_code error_code, void *userp)
155 {
156   struct connectdata *conn = (struct connectdata *)userp;
157   (void)session;
158   (void)frame;
159   infof(conn->data, "on_invalid_frame_recv() was called, error_code = %d\n",
160         error_code);
161   return 0;
162 }
163
164 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
165                               int32_t stream_id,
166                               const uint8_t *data, size_t len, void *userp)
167 {
168   struct connectdata *conn = (struct connectdata *)userp;
169   (void)session;
170   (void)flags;
171   (void)stream_id;
172   (void)data;
173   infof(conn->data, "on_data_chunk_recv() was called with len = %u\n", len);
174   return 0;
175 }
176
177 static int before_frame_send(nghttp2_session *session,
178                              const nghttp2_frame *frame,
179                              void *userp)
180 {
181   struct connectdata *conn = (struct connectdata *)userp;
182   (void)session;
183   (void)frame;
184   infof(conn->data, "before_frame_send() was called\n");
185   return 0;
186 }
187 static int on_frame_send(nghttp2_session *session,
188                          const nghttp2_frame *frame,
189                          void *userp)
190 {
191   struct connectdata *conn = (struct connectdata *)userp;
192   (void)session;
193   (void)frame;
194   infof(conn->data, "on_frame_send() was called\n");
195   return 0;
196 }
197 static int on_frame_not_send(nghttp2_session *session,
198                              const nghttp2_frame *frame,
199                              int lib_error_code, void *userp)
200 {
201   struct connectdata *conn = (struct connectdata *)userp;
202   (void)session;
203   (void)frame;
204   infof(conn->data, "on_frame_not_send() was called, lib_error_code = %d\n",
205         lib_error_code);
206   return 0;
207 }
208 static int on_stream_close(nghttp2_session *session, int32_t stream_id,
209                            nghttp2_error_code error_code, void *userp)
210 {
211   struct connectdata *conn = (struct connectdata *)userp;
212   (void)session;
213   (void)stream_id;
214   infof(conn->data, "on_stream_close() was called, error_code = %d\n",
215         error_code);
216   return 0;
217 }
218
219 static int on_unknown_frame_recv(nghttp2_session *session,
220                                  const uint8_t *head, size_t headlen,
221                                  const uint8_t *payload, size_t payloadlen,
222                                  void *userp)
223 {
224   struct connectdata *conn = (struct connectdata *)userp;
225   (void)session;
226   (void)head;
227   (void)headlen;
228   (void)payload;
229   (void)payloadlen;
230   infof(conn->data, "on_unknown_frame_recv() was called\n");
231   return 0;
232 }
233 static int on_begin_headers(nghttp2_session *session,
234                             const nghttp2_frame *frame, void *userp)
235 {
236   struct connectdata *conn = (struct connectdata *)userp;
237   (void)session;
238   (void)frame;
239   infof(conn->data, "on_begin_headers() was called\n");
240   return 0;
241 }
242
243 /* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
244 static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
245                       const uint8_t *name, size_t namelen,
246                       const uint8_t *value, size_t valuelen,
247                       void *userp)
248 {
249   struct connectdata *conn = (struct connectdata *)userp;
250   struct http_conn *c = &conn->proto.httpc;
251   size_t hlen = namelen + valuelen + 3; /* colon + CRLF == 3 bytes */
252
253   (void)session;
254   (void)frame;
255
256   if(namelen && (name[0] == ':')) {
257     /* special case */
258     hlen = snprintf(c->mem, c->len, "HTTP/2.0 %s\r\n", value);
259   }
260   else if(hlen + 1 < c->len) { /* hlen + a zero byte */
261     /* convert to a HTTP1-style header */
262     memcpy(c->mem, name, namelen);
263     c->mem[namelen]=':';
264     memcpy(&c->mem[namelen+1], value, valuelen);
265     c->mem[namelen + valuelen + 1]='\r';
266     c->mem[namelen + valuelen + 2]='\n';
267     c->mem[namelen + valuelen + 3]=0; /* to display this easier */
268   }
269   infof(conn->data, "Got %s", c->mem);
270   c->mem += hlen;
271   c->len -= hlen;
272
273   return 0; /* 0 is successful */
274 }
275
276 /*
277  * This is all callbacks nghttp2 calls
278  */
279 static const nghttp2_session_callbacks callbacks = {
280   send_callback,         /* nghttp2_send_callback */
281   recv_callback,         /* nghttp2_recv_callback */
282   on_frame_recv,         /* nghttp2_on_frame_recv_callback */
283   on_invalid_frame_recv, /* nghttp2_on_invalid_frame_recv_callback */
284   on_data_chunk_recv,    /* nghttp2_on_data_chunk_recv_callback */
285   before_frame_send,     /* nghttp2_before_frame_send_callback */
286   on_frame_send,         /* nghttp2_on_frame_send_callback */
287   on_frame_not_send,     /* nghttp2_on_frame_not_send_callback */
288   on_stream_close,       /* nghttp2_on_stream_close_callback */
289   on_unknown_frame_recv, /* nghttp2_on_unknown_frame_recv_callback */
290   on_begin_headers,      /* nghttp2_on_begin_headers_callback */
291   on_header              /* nghttp2_on_header_callback */
292 };
293
294 /*
295  * The HTTP2 settings we send in the Upgrade request
296  */
297 static nghttp2_settings_entry settings[] = {
298   { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 },
299   { NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, NGHTTP2_INITIAL_WINDOW_SIZE },
300 };
301
302 /*
303  * Initialize nghttp2 for a Curl connection
304  */
305 CURLcode Curl_http2_init(struct connectdata *conn) {
306   if(!conn->proto.httpc.h2) {
307     /* The nghttp2 session is not yet setup, do it */
308     int rc = nghttp2_session_client_new(&conn->proto.httpc.h2,
309                                         &callbacks, conn);
310     if(rc) {
311       failf(conn->data, "Couldn't initialize nghttp2!");
312       return CURLE_OUT_OF_MEMORY; /* most likely at least */
313     }
314   }
315   return CURLE_OK;
316 }
317
318 /*
319  * Send a request using http2
320  */
321 CURLcode Curl_http2_send_request(struct connectdata *conn)
322 {
323   (void)conn;
324   return CURLE_OK;
325 }
326
327 /*
328  * Append headers to ask for a HTTP1.1 to HTTP2 upgrade.
329  */
330 CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req,
331                                     struct connectdata *conn)
332 {
333   CURLcode result;
334   ssize_t binlen;
335   char *base64;
336   size_t blen;
337   struct SingleRequest *k = &conn->data->req;
338   uint8_t *binsettings = conn->proto.httpc.binsettings;
339
340   Curl_http2_init(conn);
341
342   /* As long as we have a fixed set of settings, we don't have to dynamically
343    * figure out the base64 strings since it'll always be the same. However,
344    * the settings will likely not be fixed every time in the future.
345    */
346
347   /* this returns number of bytes it wrote */
348   binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
349                                          settings,
350                                          sizeof(settings)/sizeof(settings[0]));
351   if(!binlen) {
352     failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
353     return CURLE_FAILED_INIT;
354   }
355   conn->proto.httpc.binlen = binlen;
356
357   result = Curl_base64_encode(conn->data, (const char *)binsettings, binlen,
358                               &base64, &blen);
359   if(result)
360     return result;
361
362   result = Curl_add_bufferf(req,
363                             "Connection: Upgrade, HTTP2-Settings\r\n"
364                             "Upgrade: %s\r\n"
365                             "HTTP2-Settings: %s\r\n",
366                             NGHTTP2_PROTO_VERSION_ID, base64);
367   free(base64);
368
369   k->upgr101 = UPGR101_REQUESTED;
370
371   return result;
372 }
373
374 /*
375  * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
376  * a regular CURLcode value.
377  */
378 static ssize_t http2_recv(struct connectdata *conn, int sockindex,
379                           char *mem, size_t len, CURLcode *err)
380 {
381   int rc;
382   (void)sockindex; /* we always do HTTP2 on sockindex 0 */
383
384   conn->proto.httpc.mem = mem;
385   conn->proto.httpc.len = len;
386
387   rc = nghttp2_session_recv(conn->proto.httpc.h2);
388
389   if(rc < 0) {
390     failf(conn->data, "nghttp2_session_recv() returned %d\n",
391           rc);
392     *err = CURLE_RECV_ERROR;
393   }
394   return len - conn->proto.httpc.len;
395 }
396
397 /* return number of received (decrypted) bytes */
398 static ssize_t http2_send(struct connectdata *conn, int sockindex,
399                           const void *mem, size_t len, CURLcode *err)
400 {
401   /* TODO: proper implementation */
402   (void)conn;
403   (void)sockindex;
404   (void)mem;
405   (void)len;
406   (void)err;
407   return 0;
408 }
409
410 int Curl_http2_switched(struct connectdata *conn)
411 {
412   int rc;
413   struct http_conn *httpc = &conn->proto.httpc;
414   /* we are switched! */
415   conn->handler = &Curl_handler_http2;
416   conn->recv[FIRSTSOCKET] = http2_recv;
417   conn->send[FIRSTSOCKET] = http2_send;
418   infof(conn->data, "We have switched to HTTP2\n");
419
420   /* send the SETTINGS frame (again) */
421   rc = nghttp2_session_upgrade(httpc->h2, httpc->binsettings, httpc->binlen,
422                                conn);
423   return rc;
424 }
425
426 #endif