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