http2: actually init nghttp2 and send HTTP2-Settings properly
[platform/upstream/curl.git] / lib / http2.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2013, 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
36 /* include memdebug.h last */
37 #include "memdebug.h"
38
39 /*
40  * Store nghttp2 version info in this buffer, Prefix with a space.  Return
41  * total length written.
42  */
43 int Curl_http2_ver(char *p, size_t len)
44 {
45   nghttp2_info *h2 = nghttp2_version(0);
46   return snprintf(p, len, " nghttp2/%s", h2->version_str);
47 }
48
49 /*
50  * The implementation of nghttp2_send_callback type. Here we write |data| with
51  * size |length| to the network and return the number of bytes actually
52  * written. See the documentation of nghttp2_send_callback for the details.
53  */
54 static ssize_t send_callback(nghttp2_session *h2,
55                              const uint8_t *data, size_t length, int flags,
56                              void *userp)
57 {
58   struct connectdata *conn = (struct connectdata *)userp;
59   ssize_t written;
60   CURLcode rc =
61     Curl_write(conn, conn->sock[0], data, length, &written);
62   (void)h2;
63   (void)flags;
64
65   if(rc) {
66     failf(conn->data, "Failed sending HTTP2 data");
67     return NGHTTP2_ERR_CALLBACK_FAILURE;
68   }
69   else if(!written)
70     return NGHTTP2_ERR_WOULDBLOCK;
71
72   return written;
73 }
74
75 /*
76  * The implementation of nghttp2_recv_callback type. Here we read data from
77  * the network and write them in |buf|. The capacity of |buf| is |length|
78  * bytes. Returns the number of bytes stored in |buf|. See the documentation
79  * of nghttp2_recv_callback for the details.
80  */
81 static ssize_t recv_callback(nghttp2_session *h2,
82                              uint8_t *buf, size_t length, int flags,
83                              void *userp)
84 {
85   struct connectdata *conn = (struct connectdata *)userp;
86   ssize_t nread;
87   CURLcode rc = Curl_read(conn, conn->sock[0], (char *)buf, length, &nread);
88   (void)h2;
89   (void)flags;
90
91   if(rc) {
92     failf(conn->data, "Failed recving HTTP2 data");
93     return NGHTTP2_ERR_CALLBACK_FAILURE;
94   }
95   if(!nread)
96     return NGHTTP2_ERR_WOULDBLOCK;
97
98   return nread;
99 }
100
101 /*
102  * This is all callbacks nghttp2 calls
103  */
104 static const nghttp2_session_callbacks callbacks = {
105   send_callback,
106   recv_callback,
107   NULL,
108   NULL,
109   NULL,
110   NULL,
111   NULL,
112   NULL,
113   NULL,
114   NULL,
115   NULL,
116   NULL,
117   NULL,
118   NULL
119 };
120
121 /*
122  * The HTTP2 settings we send in the Upgrade request
123  */
124 static nghttp2_settings_entry settings[] = {
125   { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 },
126   { NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, NGHTTP2_INITIAL_WINDOW_SIZE },
127 };
128
129 /*
130  * Append headers to ask for a HTTP1.1 to HTTP2 upgrade.
131  */
132 CURLcode Curl_http2_request(Curl_send_buffer *req,
133                             struct connectdata *conn)
134 {
135   uint8_t binsettings[80];
136   CURLcode result;
137   ssize_t binlen;
138   char *base64;
139   size_t blen;
140
141   if(!conn->proto.httpc.h2) {
142     /* The nghttp2 session is not yet setup, do it */
143     int rc = nghttp2_session_client_new(&conn->proto.httpc.h2,
144                                         &callbacks, &conn);
145     if(rc) {
146       failf(conn->data, "Couldn't initialize nghttp2!");
147       return CURLE_OUT_OF_MEMORY; /* most likely at least */
148     }
149   }
150
151   /* As long as we have a fixed set of settings, we don't have to dynamically
152    * figure out the base64 strings since it'll always be the same. However,
153    * the settings will likely not be fixed every time in the future.
154    */
155
156   /* this returns number of bytes it wrote */
157   binlen = nghttp2_pack_settings_payload(binsettings, settings,
158                                          sizeof(settings)/sizeof(settings[0]));
159   if(!binlen) {
160     failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
161     return CURLE_FAILED_INIT;
162   }
163
164   result = Curl_base64_encode(conn->data, (const char *)binsettings, binlen,
165                               &base64, &blen);
166   if(result)
167     return result;
168
169   result = Curl_add_bufferf(req,
170                             "Connection: Upgrade, HTTP2-Settings\r\n"
171                             "Upgrade: HTTP/2.0\r\n"
172                             "HTTP2-Settings: %s\r\n", base64);
173   free(base64);
174
175   return result;
176 }
177
178 #endif