1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2015, 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 #include "curl_printf.h"
27 #include <nghttp2/nghttp2.h>
28 #include "urldata.h"
29 #include "http2.h"
30 #include "http.h"
31 #include "sendf.h"
32 #include "curl_base64.h"
33 #include "rawstr.h"
34 #include "multiif.h"
35 #include "conncache.h"
36
37 /* The last #include files should be: */
38 #include "curl_memory.h"
39 #include "memdebug.h"
40
41 #define MIN(x,y) ((x)<(y)?(x):(y))
42
43 #if (NGHTTP2_VERSION_NUM < 0x000600)
44 #error too old nghttp2 version, upgrade!
45 #endif
46
http2_perform_getsock(const struct connectdata * conn,curl_socket_t * sock,int numsocks)47 static int http2_perform_getsock(const struct connectdata *conn,
48 curl_socket_t *sock, /* points to
49 numsocks
50 number of
51 sockets */
52 int numsocks)
53 {
54 const struct http_conn *c = &conn->proto.httpc;
55 int bitmap = GETSOCK_BLANK;
56 (void)numsocks;
57
58 /* TODO We should check underlying socket state if it is SSL socket
59 because of renegotiation. */
60 sock[0] = conn->sock[FIRSTSOCKET];
61
62 if(nghttp2_session_want_read(c->h2))
63 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
64
65 if(nghttp2_session_want_write(c->h2))
66 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
67
68 return bitmap;
69 }
70
http2_getsock(struct connectdata * conn,curl_socket_t * sock,int numsocks)71 static int http2_getsock(struct connectdata *conn,
72 curl_socket_t *sock, /* points to numsocks
73 number of sockets */
74 int numsocks)
75 {
76 return http2_perform_getsock(conn, sock, numsocks);
77 }
78
http2_disconnect(struct connectdata * conn,bool dead_connection)79 static CURLcode http2_disconnect(struct connectdata *conn,
80 bool dead_connection)
81 {
82 struct http_conn *c = &conn->proto.httpc;
83 (void)dead_connection;
84
85 DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT starts now\n"));
86
87 nghttp2_session_del(c->h2);
88 Curl_safefree(c->inbuf);
89 Curl_hash_destroy(&c->streamsh);
90
91 DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT done\n"));
92
93 return CURLE_OK;
94 }
95
96 /* called from Curl_http_setup_conn */
Curl_http2_setup_conn(struct connectdata * conn)97 void Curl_http2_setup_conn(struct connectdata *conn)
98 {
99 struct HTTP *http = conn->data->req.protop;
100
101 conn->proto.httpc.settings.max_concurrent_streams =
102 DEFAULT_MAX_CONCURRENT_STREAMS;
103
104 http->nread_header_recvbuf = 0;
105 http->bodystarted = FALSE;
106 http->status_code = -1;
107 http->pausedata = NULL;
108 http->pauselen = 0;
109 http->error_code = NGHTTP2_NO_ERROR;
110 http->closed = FALSE;
111
112 /* where to store incoming data for this stream and how big the buffer is */
113 http->mem = conn->data->state.buffer;
114 http->len = BUFSIZE;
115 http->memlen = 0;
116 }
117
118 /*
119 * HTTP2 handler interface. This isn't added to the general list of protocols
120 * but will be used at run-time when the protocol is dynamically switched from
121 * HTTP to HTTP2.
122 */
123 const struct Curl_handler Curl_handler_http2 = {
124 "HTTP2", /* scheme */
125 ZERO_NULL, /* setup_connection */
126 Curl_http, /* do_it */
127 Curl_http_done, /* done */
128 ZERO_NULL, /* do_more */
129 ZERO_NULL, /* connect_it */
130 ZERO_NULL, /* connecting */
131 ZERO_NULL, /* doing */
132 http2_getsock, /* proto_getsock */
133 http2_getsock, /* doing_getsock */
134 ZERO_NULL, /* domore_getsock */
135 http2_perform_getsock, /* perform_getsock */
136 http2_disconnect, /* disconnect */
137 ZERO_NULL, /* readwrite */
138 PORT_HTTP, /* defport */
139 CURLPROTO_HTTP, /* protocol */
140 PROTOPT_NONE /* flags */
141 };
142
143 const struct Curl_handler Curl_handler_http2_ssl = {
144 "HTTP2", /* scheme */
145 ZERO_NULL, /* setup_connection */
146 Curl_http, /* do_it */
147 Curl_http_done, /* done */
148 ZERO_NULL, /* do_more */
149 ZERO_NULL, /* connect_it */
150 ZERO_NULL, /* connecting */
151 ZERO_NULL, /* doing */
152 http2_getsock, /* proto_getsock */
153 http2_getsock, /* doing_getsock */
154 ZERO_NULL, /* domore_getsock */
155 http2_perform_getsock, /* perform_getsock */
156 http2_disconnect, /* disconnect */
157 ZERO_NULL, /* readwrite */
158 PORT_HTTP, /* defport */
159 CURLPROTO_HTTPS, /* protocol */
160 PROTOPT_SSL /* flags */
161 };
162
163 /*
164 * Store nghttp2 version info in this buffer, Prefix with a space. Return
165 * total length written.
166 */
Curl_http2_ver(char * p,size_t len)167 int Curl_http2_ver(char *p, size_t len)
168 {
169 nghttp2_info *h2 = nghttp2_version(0);
170 return snprintf(p, len, " nghttp2/%s", h2->version_str);
171 }
172
173 /*
174 * The implementation of nghttp2_send_callback type. Here we write |data| with
175 * size |length| to the network and return the number of bytes actually
176 * written. See the documentation of nghttp2_send_callback for the details.
177 */
send_callback(nghttp2_session * h2,const uint8_t * data,size_t length,int flags,void * userp)178 static ssize_t send_callback(nghttp2_session *h2,
179 const uint8_t *data, size_t length, int flags,
180 void *userp)
181 {
182 struct connectdata *conn = (struct connectdata *)userp;
183 struct http_conn *c = &conn->proto.httpc;
184 ssize_t written;
185 CURLcode result = CURLE_OK;
186
187 (void)h2;
188 (void)flags;
189
190 written = ((Curl_send*)c->send_underlying)(conn, FIRSTSOCKET,
191 data, length, &result);
192
193 if(result == CURLE_AGAIN) {
194 return NGHTTP2_ERR_WOULDBLOCK;
195 }
196
197 if(written == -1) {
198 failf(conn->data, "Failed sending HTTP2 data");
199 return NGHTTP2_ERR_CALLBACK_FAILURE;
200 }
201
202 if(!written)
203 return NGHTTP2_ERR_WOULDBLOCK;
204
205 return written;
206 }
207
on_frame_recv(nghttp2_session * session,const nghttp2_frame * frame,void * userp)208 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
209 void *userp)
210 {
211 struct connectdata *conn = (struct connectdata *)userp;
212 struct http_conn *httpc = &conn->proto.httpc;
213 struct SessionHandle *data_s = NULL;
214 struct HTTP *stream = NULL;
215 int rv;
216 size_t left, ncopy;
217 int32_t stream_id = frame->hd.stream_id;
218
219 (void)session;
220 (void)frame;
221 DEBUGF(infof(conn->data, "on_frame_recv() header %x stream %x\n",
222 frame->hd.type, stream_id));
223
224 if(stream_id) {
225 /* get the stream from the hash based on Stream ID, stream ID zero is for
226 connection-oriented stuff */
227 data_s = Curl_hash_pick(&httpc->streamsh, &stream_id,
228 sizeof(stream_id));
229 if(!data_s) {
230 /* Receiving a Stream ID not in the hash should not happen, this is an
231 internal error more than anything else! */
232 failf(conn->data, "Received frame on Stream ID: %x not in stream hash!",
233 stream_id);
234 return NGHTTP2_ERR_CALLBACK_FAILURE;
235 }
236 stream = data_s->req.protop;
237 }
238 else
239 /* we do nothing on stream zero */
240 return 0;
241
242 switch(frame->hd.type) {
243 case NGHTTP2_DATA:
244 /* If body started on this stream, then receiving DATA is illegal. */
245 if(!stream->bodystarted) {
246 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
247 stream_id, NGHTTP2_PROTOCOL_ERROR);
248
249 if(nghttp2_is_fatal(rv)) {
250 return NGHTTP2_ERR_CALLBACK_FAILURE;
251 }
252 }
253 break;
254 case NGHTTP2_HEADERS:
255 if(frame->headers.cat == NGHTTP2_HCAT_REQUEST)
256 break;
257
258 if(stream->bodystarted) {
259 /* Only valid HEADERS after body started is trailer HEADERS. We
260 ignores trailer HEADERS for now. nghttp2 guarantees that it
261 has END_STREAM flag set. */
262 break;
263 }
264
265 /* nghttp2 guarantees that :status is received, and we store it to
266 stream->status_code */
267 DEBUGASSERT(stream->status_code != -1);
268
269 /* Only final status code signals the end of header */
270 if(stream->status_code / 100 != 1) {
271 stream->bodystarted = TRUE;
272 stream->status_code = -1;
273 }
274
275 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
276
277 left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
278 ncopy = MIN(stream->len, left);
279
280 memcpy(&stream->mem[stream->memlen],
281 stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
282 ncopy);
283 stream->nread_header_recvbuf += ncopy;
284
285 DEBUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n",
286 ncopy, stream_id, stream->mem));
287
288 stream->len -= ncopy;
289 stream->memlen += ncopy;
290
291 data_s->state.drain++;
292 Curl_expire(data_s, 1);
293 break;
294 case NGHTTP2_PUSH_PROMISE:
295 DEBUGF(infof(data_s, "Got PUSH_PROMISE, RST_STREAM it!\n"));
296 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
297 frame->push_promise.promised_stream_id,
298 NGHTTP2_CANCEL);
299 if(nghttp2_is_fatal(rv)) {
300 return rv;
301 }
302 break;
303 case NGHTTP2_SETTINGS:
304 {
305 uint32_t max_conn = httpc->settings.max_concurrent_streams;
306 DEBUGF(infof(conn->data, "Got SETTINGS for stream %u!\n", stream_id));
307 httpc->settings.max_concurrent_streams =
308 nghttp2_session_get_remote_settings(
309 session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
310 httpc->settings.enable_push =
311 nghttp2_session_get_remote_settings(
312 session, NGHTTP2_SETTINGS_ENABLE_PUSH);
313 DEBUGF(infof(conn->data, "MAX_CONCURRENT_STREAMS == %d\n",
314 httpc->settings.max_concurrent_streams));
315 DEBUGF(infof(conn->data, "ENABLE_PUSH == %s\n",
316 httpc->settings.enable_push?"TRUE":"false"));
317 if(max_conn != httpc->settings.max_concurrent_streams) {
318 /* only signal change if the value actually changed */
319 infof(conn->data,
320 "Connection state changed (MAX_CONCURRENT_STREAMS updated)!\n");
321 Curl_multi_connchanged(conn->data->multi);
322 }
323 }
324 break;
325 default:
326 DEBUGF(infof(conn->data, "Got frame type %x for stream %u!\n",
327 frame->hd.type, stream_id));
328 break;
329 }
330 return 0;
331 }
332
on_invalid_frame_recv(nghttp2_session * session,const nghttp2_frame * frame,int lib_error_code,void * userp)333 static int on_invalid_frame_recv(nghttp2_session *session,
334 const nghttp2_frame *frame,
335 int lib_error_code, void *userp)
336 {
337 struct connectdata *conn = (struct connectdata *)userp;
338 (void)session;
339 (void)frame;
340 DEBUGF(infof(conn->data,
341 "on_invalid_frame_recv() was called, error=%d:%s\n",
342 lib_error_code, nghttp2_strerror(lib_error_code)));
343 return 0;
344 }
345
on_data_chunk_recv(nghttp2_session * session,uint8_t flags,int32_t stream_id,const uint8_t * data,size_t len,void * userp)346 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
347 int32_t stream_id,
348 const uint8_t *data, size_t len, void *userp)
349 {
350 struct connectdata *conn = (struct connectdata *)userp;
351 struct HTTP *stream;
352 struct SessionHandle *data_s;
353 size_t nread;
354 (void)session;
355 (void)flags;
356 (void)data;
357 DEBUGF(infof(conn->data, "on_data_chunk_recv() "
358 "len = %u, stream %u\n", len, stream_id));
359
360 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
361
362 /* get the stream from the hash based on Stream ID */
363 data_s = Curl_hash_pick(&conn->proto.httpc.streamsh, &stream_id,
364 sizeof(stream_id));
365 if(!data_s) {
366 /* Receiving a Stream ID not in the hash should not happen, this is an
367 internal error more than anything else! */
368 failf(conn->data, "Received frame on Stream ID: %x not in stream hash!",
369 stream_id);
370 return NGHTTP2_ERR_CALLBACK_FAILURE;
371 }
372 stream = data_s->req.protop;
373
374 nread = MIN(stream->len, len);
375 memcpy(&stream->mem[stream->memlen], data, nread);
376
377 stream->len -= nread;
378 stream->memlen += nread;
379
380 data_s->state.drain++;
381 Curl_expire(data_s, 1); /* TODO: fix so that this can be set to 0 for
382 immediately? */
383
384 DEBUGF(infof(data_s, "%zu data received for stream %u "
385 "(%zu left in buffer %p, total %zu)\n",
386 nread, stream_id,
387 stream->len, stream->mem,
388 stream->memlen));
389
390 if(nread < len) {
391 stream->pausedata = data + nread;
392 stream->pauselen = len - nread;
393 DEBUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
394 ", stream %u\n",
395 len - nread, stream_id));
396 conn->proto.httpc.pause_stream_id = stream_id;
397 return NGHTTP2_ERR_PAUSE;
398 }
399 return 0;
400 }
401
before_frame_send(nghttp2_session * session,const nghttp2_frame * frame,void * userp)402 static int before_frame_send(nghttp2_session *session,
403 const nghttp2_frame *frame,
404 void *userp)
405 {
406 struct connectdata *conn = (struct connectdata *)userp;
407 (void)session;
408 (void)frame;
409 DEBUGF(infof(conn->data, "before_frame_send() was called\n"));
410 return 0;
411 }
on_frame_send(nghttp2_session * session,const nghttp2_frame * frame,void * userp)412 static int on_frame_send(nghttp2_session *session,
413 const nghttp2_frame *frame,
414 void *userp)
415 {
416 struct connectdata *conn = (struct connectdata *)userp;
417 (void)session;
418 (void)frame;
419 DEBUGF(infof(conn->data, "on_frame_send() was called, length = %zd\n",
420 frame->hd.length));
421 return 0;
422 }
on_frame_not_send(nghttp2_session * session,const nghttp2_frame * frame,int lib_error_code,void * userp)423 static int on_frame_not_send(nghttp2_session *session,
424 const nghttp2_frame *frame,
425 int lib_error_code, void *userp)
426 {
427 struct connectdata *conn = (struct connectdata *)userp;
428 (void)session;
429 (void)frame;
430 DEBUGF(infof(conn->data,
431 "on_frame_not_send() was called, lib_error_code = %d\n",
432 lib_error_code));
433 return 0;
434 }
on_stream_close(nghttp2_session * session,int32_t stream_id,uint32_t error_code,void * userp)435 static int on_stream_close(nghttp2_session *session, int32_t stream_id,
436 uint32_t error_code, void *userp)
437 {
438 struct connectdata *conn = (struct connectdata *)userp;
439 struct SessionHandle *data_s;
440 struct HTTP *stream;
441 (void)session;
442 (void)stream_id;
443 DEBUGF(infof(conn->data, "on_stream_close(), error_code = %d, stream %u\n",
444 error_code, stream_id));
445
446 if(stream_id) {
447 /* get the stream from the hash based on Stream ID, stream ID zero is for
448 connection-oriented stuff */
449 data_s = Curl_hash_pick(&conn->proto.httpc.streamsh, &stream_id,
450 sizeof(stream_id));
451 if(!data_s) {
452 /* We could get stream ID not in the hash. For example, if we
453 decided to reject stream (e.g., PUSH_PROMISE). We call infof
454 as a debugging purpose for now. */
455 infof(conn->data,
456 "Received frame on Stream ID: %x not in stream hash!\n",
457 stream_id);
458 return 0;
459 }
460 stream = data_s->req.protop;
461
462 stream->error_code = error_code;
463 stream->closed = TRUE;
464
465 /* remove the entry from the hash as the stream is now gone */
466 Curl_hash_delete(&conn->proto.httpc.streamsh,
467 &stream_id, sizeof(stream_id));
468 DEBUGF(infof(conn->data, "Removed stream %u hash!\n", stream_id));
469 }
470 return 0;
471 }
472
on_begin_headers(nghttp2_session * session,const nghttp2_frame * frame,void * userp)473 static int on_begin_headers(nghttp2_session *session,
474 const nghttp2_frame *frame, void *userp)
475 {
476 struct connectdata *conn = (struct connectdata *)userp;
477 (void)session;
478 (void)frame;
479 DEBUGF(infof(conn->data, "on_begin_headers() was called\n"));
480 return 0;
481 }
482
483 /* Decode HTTP status code. Returns -1 if no valid status code was
484 decoded. */
decode_status_code(const uint8_t * value,size_t len)485 static int decode_status_code(const uint8_t *value, size_t len)
486 {
487 int i;
488 int res;
489
490 if(len != 3) {
491 return -1;
492 }
493
494 res = 0;
495
496 for(i = 0; i < 3; ++i) {
497 char c = value[i];
498
499 if(c < '0' || c > '9') {
500 return -1;
501 }
502
503 res *= 10;
504 res += c - '0';
505 }
506
507 return res;
508 }
509
510 /* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
on_header(nghttp2_session * session,const nghttp2_frame * frame,const uint8_t * name,size_t namelen,const uint8_t * value,size_t valuelen,uint8_t flags,void * userp)511 static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
512 const uint8_t *name, size_t namelen,
513 const uint8_t *value, size_t valuelen,
514 uint8_t flags,
515 void *userp)
516 {
517 struct connectdata *conn = (struct connectdata *)userp;
518 struct HTTP *stream;
519 struct SessionHandle *data_s;
520 int32_t stream_id = frame->hd.stream_id;
521
522 (void)session;
523 (void)frame;
524 (void)flags;
525
526 /* Ignore PUSH_PROMISE for now */
527 if(frame->hd.type != NGHTTP2_HEADERS) {
528 return 0;
529 }
530
531 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
532
533 /* get the stream from the hash based on Stream ID */
534 data_s = Curl_hash_pick(&conn->proto.httpc.streamsh, &stream_id,
535 sizeof(stream_id));
536 if(!data_s) {
537 /* Receiving a Stream ID not in the hash should not happen, this is an
538 internal error more than anything else! */
539 failf(conn->data, "Received frame on Stream ID: %x not in stream hash!",
540 stream_id);
541 return NGHTTP2_ERR_CALLBACK_FAILURE;
542 }
543 stream = data_s->req.protop;
544
545 if(stream->bodystarted)
546 /* Ignore trailer or HEADERS not mapped to HTTP semantics. The
547 consequence is handled in on_frame_recv(). */
548 return 0;
549
550 if(namelen == sizeof(":status") - 1 &&
551 memcmp(":status", name, namelen) == 0) {
552 /* nghttp2 guarantees :status is received first and only once, and
553 value is 3 digits status code, and decode_status_code always
554 succeeds. */
555 stream->status_code = decode_status_code(value, valuelen);
556 DEBUGASSERT(stream->status_code != -1);
557
558 Curl_add_buffer(stream->header_recvbuf, "HTTP/2.0 ", 9);
559 Curl_add_buffer(stream->header_recvbuf, value, valuelen);
560 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
561 data_s->state.drain++;
562 Curl_expire(data_s, 1);
563
564 DEBUGF(infof(data_s, "h2 status: HTTP/2 %03d\n",
565 stream->status_code));
566 return 0;
567 }
568
569 /* nghttp2 guarantees that namelen > 0, and :status was already
570 received, and this is not pseudo-header field . */
571 /* convert to a HTTP1-style header */
572 Curl_add_buffer(stream->header_recvbuf, name, namelen);
573 Curl_add_buffer(stream->header_recvbuf, ":", 1);
574 Curl_add_buffer(stream->header_recvbuf, value, valuelen);
575 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
576 data_s->state.drain++;
577 Curl_expire(data_s, 1);
578
579 DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
580 value));
581
582 return 0; /* 0 is successful */
583 }
584
data_source_read_callback(nghttp2_session * session,int32_t stream_id,uint8_t * buf,size_t length,uint32_t * data_flags,nghttp2_data_source * source,void * userp)585 static ssize_t data_source_read_callback(nghttp2_session *session,
586 int32_t stream_id,
587 uint8_t *buf, size_t length,
588 uint32_t *data_flags,
589 nghttp2_data_source *source,
590 void *userp)
591 {
592 struct connectdata *conn = (struct connectdata *)userp;
593 struct http_conn *c = &conn->proto.httpc;
594 struct SessionHandle *data_s;
595 struct HTTP *stream = NULL;
596 size_t nread;
597 (void)session;
598 (void)stream_id;
599 (void)source;
600
601 if(stream_id) {
602 /* get the stream from the hash based on Stream ID, stream ID zero is for
603 connection-oriented stuff */
604 data_s = Curl_hash_pick(&c->streamsh, &stream_id, sizeof(stream_id));
605 if(!data_s) {
606 /* Receiving a Stream ID not in the hash should not happen, this is an
607 internal error more than anything else! */
608 failf(conn->data, "Asked for data to stream %u not in hash!", stream_id);
609 return NGHTTP2_ERR_CALLBACK_FAILURE;
610 }
611 stream = data_s->req.protop;
612 }
613 else {
614 failf(conn->data, "nghttp2 confusion");
615 return NGHTTP2_ERR_INVALID_ARGUMENT;
616 }
617
618 nread = MIN(stream->upload_len, length);
619 if(nread > 0) {
620 memcpy(buf, stream->upload_mem, nread);
621 stream->upload_mem += nread;
622 stream->upload_len -= nread;
623 stream->upload_left -= nread;
624 }
625
626 if(stream->upload_left == 0)
627 *data_flags = 1;
628 else if(nread == 0)
629 return NGHTTP2_ERR_DEFERRED;
630
631 DEBUGF(infof(data_s, "data_source_read_callback: "
632 "returns %zu bytes stream %u\n",
633 nread, stream_id));
634
635 return nread;
636 }
637
638 /*
639 * The HTTP2 settings we send in the Upgrade request
640 */
641 static nghttp2_settings_entry settings[] = {
642 { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 },
643 { NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, NGHTTP2_INITIAL_WINDOW_SIZE },
644 };
645
646 #define H2_BUFSIZE 32768
647
freestreamentry(void * freethis)648 static void freestreamentry(void *freethis)
649 {
650 (void)freethis;
651 }
652
653 /*
654 * Initialize nghttp2 for a Curl connection
655 */
Curl_http2_init(struct connectdata * conn)656 CURLcode Curl_http2_init(struct connectdata *conn)
657 {
658 if(!conn->proto.httpc.h2) {
659 int rc;
660 nghttp2_session_callbacks *callbacks;
661
662 conn->proto.httpc.inbuf = malloc(H2_BUFSIZE);
663 if(conn->proto.httpc.inbuf == NULL)
664 return CURLE_OUT_OF_MEMORY;
665
666 rc = nghttp2_session_callbacks_new(&callbacks);
667
668 if(rc) {
669 failf(conn->data, "Couldn't initialize nghttp2 callbacks!");
670 return CURLE_OUT_OF_MEMORY; /* most likely at least */
671 }
672
673 /* nghttp2_send_callback */
674 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
675 /* nghttp2_on_frame_recv_callback */
676 nghttp2_session_callbacks_set_on_frame_recv_callback
677 (callbacks, on_frame_recv);
678 /* nghttp2_on_invalid_frame_recv_callback */
679 nghttp2_session_callbacks_set_on_invalid_frame_recv_callback
680 (callbacks, on_invalid_frame_recv);
681 /* nghttp2_on_data_chunk_recv_callback */
682 nghttp2_session_callbacks_set_on_data_chunk_recv_callback
683 (callbacks, on_data_chunk_recv);
684 /* nghttp2_before_frame_send_callback */
685 nghttp2_session_callbacks_set_before_frame_send_callback
686 (callbacks, before_frame_send);
687 /* nghttp2_on_frame_send_callback */
688 nghttp2_session_callbacks_set_on_frame_send_callback
689 (callbacks, on_frame_send);
690 /* nghttp2_on_frame_not_send_callback */
691 nghttp2_session_callbacks_set_on_frame_not_send_callback
692 (callbacks, on_frame_not_send);
693 /* nghttp2_on_stream_close_callback */
694 nghttp2_session_callbacks_set_on_stream_close_callback
695 (callbacks, on_stream_close);
696 /* nghttp2_on_begin_headers_callback */
697 nghttp2_session_callbacks_set_on_begin_headers_callback
698 (callbacks, on_begin_headers);
699 /* nghttp2_on_header_callback */
700 nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
701
702 /* The nghttp2 session is not yet setup, do it */
703 rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
704
705 nghttp2_session_callbacks_del(callbacks);
706
707 if(rc) {
708 failf(conn->data, "Couldn't initialize nghttp2!");
709 return CURLE_OUT_OF_MEMORY; /* most likely at least */
710 }
711
712 rc = Curl_hash_init(&conn->proto.httpc.streamsh, 7, Curl_hash_str,
713 Curl_str_key_compare, freestreamentry);
714 if(rc) {
715 failf(conn->data, "Couldn't init stream hash!");
716 return CURLE_OUT_OF_MEMORY; /* most likely at least */
717 }
718 }
719 return CURLE_OK;
720 }
721
722 /*
723 * Send a request using http2
724 */
Curl_http2_send_request(struct connectdata * conn)725 CURLcode Curl_http2_send_request(struct connectdata *conn)
726 {
727 (void)conn;
728 return CURLE_OK;
729 }
730
731 /*
732 * Append headers to ask for a HTTP1.1 to HTTP2 upgrade.
733 */
Curl_http2_request_upgrade(Curl_send_buffer * req,struct connectdata * conn)734 CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req,
735 struct connectdata *conn)
736 {
737 CURLcode result;
738 ssize_t binlen;
739 char *base64;
740 size_t blen;
741 struct SingleRequest *k = &conn->data->req;
742 uint8_t *binsettings = conn->proto.httpc.binsettings;
743
744 /* As long as we have a fixed set of settings, we don't have to dynamically
745 * figure out the base64 strings since it'll always be the same. However,
746 * the settings will likely not be fixed every time in the future.
747 */
748
749 /* this returns number of bytes it wrote */
750 binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
751 settings,
752 sizeof(settings)/sizeof(settings[0]));
753 if(!binlen) {
754 failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
755 return CURLE_FAILED_INIT;
756 }
757 conn->proto.httpc.binlen = binlen;
758
759 result = Curl_base64url_encode(conn->data, (const char *)binsettings, binlen,
760 &base64, &blen);
761 if(result)
762 return result;
763
764 result = Curl_add_bufferf(req,
765 "Connection: Upgrade, HTTP2-Settings\r\n"
766 "Upgrade: %s\r\n"
767 "HTTP2-Settings: %s\r\n",
768 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
769 free(base64);
770
771 k->upgr101 = UPGR101_REQUESTED;
772
773 return result;
774 }
775
http2_handle_stream_close(struct http_conn * httpc,struct SessionHandle * data,struct HTTP * stream,CURLcode * err)776 static ssize_t http2_handle_stream_close(struct http_conn *httpc,
777 struct SessionHandle *data,
778 struct HTTP *stream, CURLcode *err) {
779 if(httpc->pause_stream_id == stream->stream_id) {
780 httpc->pause_stream_id = 0;
781 }
782 /* Reset to FALSE to prevent infinite loop in readwrite_data
783 function. */
784 stream->closed = FALSE;
785 if(stream->error_code != NGHTTP2_NO_ERROR) {
786 failf(data, "HTTP/2 stream %u was not closed cleanly: error_code = %d",
787 stream->stream_id, stream->error_code);
788 *err = CURLE_HTTP2;
789 return -1;
790 }
791 DEBUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close\n"));
792 return 0;
793 }
794
795 /*
796 * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
797 * a regular CURLcode value.
798 */
http2_recv(struct connectdata * conn,int sockindex,char * mem,size_t len,CURLcode * err)799 static ssize_t http2_recv(struct connectdata *conn, int sockindex,
800 char *mem, size_t len, CURLcode *err)
801 {
802 CURLcode result = CURLE_OK;
803 ssize_t rv;
804 ssize_t nread;
805 struct http_conn *httpc = &conn->proto.httpc;
806 struct SessionHandle *data = conn->data;
807 struct HTTP *stream = data->req.protop;
808
809 (void)sockindex; /* we always do HTTP2 on sockindex 0 */
810
811 /* If stream is closed, return 0 to signal the http routine to close
812 the connection. We need to handle stream closure here,
813 otherwise, we may be going to read from underlying connection,
814 and gets EAGAIN, and we will get stuck there. */
815 if(stream->memlen == 0 && stream->closed) {
816 return http2_handle_stream_close(httpc, data, stream, err);
817 }
818
819 /* Nullify here because we call nghttp2_session_send() and they
820 might refer to the old buffer. */
821 stream->upload_mem = NULL;
822 stream->upload_len = 0;
823
824 /*
825 * At this point 'stream' is just in the SessionHandle the connection
826 * identifies as its owner at this time.
827 */
828
829 if(stream->bodystarted &&
830 stream->nread_header_recvbuf < stream->header_recvbuf->size_used) {
831 /* If there is body data pending for this stream to return, do that */
832 size_t left =
833 stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
834 size_t ncopy = MIN(len, left);
835 memcpy(mem, stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
836 ncopy);
837 stream->nread_header_recvbuf += ncopy;
838
839 infof(data, "http2_recv: Got %d bytes from header_recvbuf\n",
840 (int)ncopy);
841 return ncopy;
842 }
843
844 infof(data, "http2_recv: %d bytes buffer at %p (stream %u)\n",
845 len, mem, stream->stream_id);
846
847 if((data->state.drain) && stream->memlen) {
848 DEBUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n",
849 stream->memlen, stream->stream_id,
850 stream->mem, mem));
851 if(mem != stream->mem) {
852 /* if we didn't get the same buffer this time, we must move the data to
853 the beginning */
854 memmove(mem, stream->mem, stream->memlen);
855 stream->len = len - stream->memlen;
856 stream->mem = mem;
857 }
858 }
859 else if(stream->pausedata) {
860 nread = MIN(len, stream->pauselen);
861 memcpy(mem, stream->pausedata, nread);
862
863 stream->pausedata += nread;
864 stream->pauselen -= nread;
865
866 infof(data, "%zu data bytes written\n", nread);
867 if(stream->pauselen == 0) {
868 DEBUGF(infof(data, "Unpaused by stream %u\n", stream->stream_id));
869 assert(httpc->pause_stream_id == stream->stream_id);
870 httpc->pause_stream_id = 0;
871
872 stream->pausedata = NULL;
873 stream->pauselen = 0;
874 }
875 infof(data, "http2_recv: returns unpaused %zd bytes on stream %u\n",
876 nread, stream->stream_id);
877 return nread;
878 }
879 else if(httpc->pause_stream_id) {
880 /* If a stream paused nghttp2_session_mem_recv previously, and has
881 not processed all data, it still refers to the buffer in
882 nghttp2_session. If we call nghttp2_session_mem_recv(), we may
883 overwrite that buffer. To avoid that situation, just return
884 here with CURLE_AGAIN. This could be busy loop since data in
885 socket is not read. But it seems that usually streams are
886 notified with its drain property, and socket is read again
887 quickly. */
888 *err = CURLE_AGAIN;
889 return -1;
890 }
891 else {
892 char *inbuf;
893 /* remember where to store incoming data for this stream and how big the
894 buffer is */
895 stream->mem = mem;
896 stream->len = len;
897 stream->memlen = 0;
898
899 if(httpc->inbuflen == 0) {
900 nread = ((Curl_recv *)httpc->recv_underlying)(
901 conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
902
903 if(result == CURLE_AGAIN) {
904 *err = result;
905 return -1;
906 }
907
908 if(nread == -1) {
909 failf(data, "Failed receiving HTTP2 data");
910 *err = result;
911 return 0;
912 }
913
914 if(nread == 0) {
915 failf(data, "Unexpected EOF");
916 *err = CURLE_RECV_ERROR;
917 return -1;
918 }
919
920 DEBUGF(infof(data, "nread=%zd\n", nread));
921
922 httpc->inbuflen = nread;
923 inbuf = httpc->inbuf;
924 }
925 else {
926 nread = httpc->inbuflen - httpc->nread_inbuf;
927 inbuf = httpc->inbuf + httpc->nread_inbuf;
928
929 DEBUGF(infof(data, "Use data left in connection buffer, nread=%zd\n",
930 nread));
931 }
932 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
933
934 if(nghttp2_is_fatal((int)rv)) {
935 failf(data, "nghttp2_session_mem_recv() returned %d:%s\n",
936 rv, nghttp2_strerror((int)rv));
937 *err = CURLE_RECV_ERROR;
938 return 0;
939 }
940 DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", rv));
941 if(nread == rv) {
942 DEBUGF(infof(data, "All data in connection buffer processed\n"));
943 httpc->inbuflen = 0;
944 httpc->nread_inbuf = 0;
945 }
946 else {
947 httpc->nread_inbuf += rv;
948 DEBUGF(infof(data, "%zu bytes left in connection buffer\n",
949 httpc->inbuflen - httpc->nread_inbuf));
950 }
951 /* Always send pending frames in nghttp2 session, because
952 nghttp2_session_mem_recv() may queue new frame */
953 rv = nghttp2_session_send(httpc->h2);
954 if(rv != 0) {
955 *err = CURLE_SEND_ERROR;
956 return 0;
957 }
958 }
959 if(stream->memlen) {
960 ssize_t retlen = stream->memlen;
961 infof(data, "http2_recv: returns %zd for stream %u\n",
962 retlen, stream->stream_id);
963 stream->memlen = 0;
964
965 if(httpc->pause_stream_id == stream->stream_id) {
966 /* data for this stream is returned now, but this stream caused a pause
967 already so we need it called again asap */
968 DEBUGF(infof(data, "Data returned for PAUSED stream %u\n",
969 stream->stream_id));
970 }
971 else
972 data->state.drain = 0; /* this stream is hereby drained */
973
974 return retlen;
975 }
976 /* If stream is closed, return 0 to signal the http routine to close
977 the connection */
978 if(stream->closed) {
979 return http2_handle_stream_close(httpc, data, stream, err);
980 }
981 *err = CURLE_AGAIN;
982 DEBUGF(infof(data, "http2_recv returns AGAIN for stream %u\n",
983 stream->stream_id));
984 return -1;
985 }
986
987 /* Index where :authority header field will appear in request header
988 field list. */
989 #define AUTHORITY_DST_IDX 3
990
991 /* return number of received (decrypted) bytes */
http2_send(struct connectdata * conn,int sockindex,const void * mem,size_t len,CURLcode * err)992 static ssize_t http2_send(struct connectdata *conn, int sockindex,
993 const void *mem, size_t len, CURLcode *err)
994 {
995 /*
996 * BIG TODO: Currently, we send request in this function, but this
997 * function is also used to send request body. It would be nice to
998 * add dedicated function for request.
999 */
1000 int rv;
1001 struct http_conn *httpc = &conn->proto.httpc;
1002 struct HTTP *stream = conn->data->req.protop;
1003 nghttp2_nv *nva;
1004 size_t nheader;
1005 size_t i;
1006 size_t authority_idx;
1007 char *hdbuf = (char*)mem;
1008 char *end;
1009 nghttp2_data_provider data_prd;
1010 int32_t stream_id;
1011 nghttp2_session *h2 = httpc->h2;
1012
1013 (void)sockindex;
1014
1015 DEBUGF(infof(conn->data, "http2_send len=%zu\n", len));
1016
1017 if(stream->stream_id != -1) {
1018 /* If stream_id != -1, we have dispatched request HEADERS, and now
1019 are going to send or sending request body in DATA frame */
1020 stream->upload_mem = mem;
1021 stream->upload_len = len;
1022 nghttp2_session_resume_data(h2, stream->stream_id);
1023 rv = nghttp2_session_send(h2);
1024 if(nghttp2_is_fatal(rv)) {
1025 *err = CURLE_SEND_ERROR;
1026 return -1;
1027 }
1028 len -= stream->upload_len;
1029
1030 /* Nullify here because we call nghttp2_session_send() and they
1031 might refer to the old buffer. */
1032 stream->upload_mem = NULL;
1033 stream->upload_len = 0;
1034
1035 if(stream->upload_left) {
1036 /* we are sure that we have more data to send here. Calling the
1037 following API will make nghttp2_session_want_write() return
1038 nonzero if remote window allows it, which then libcurl checks
1039 socket is writable or not. See http2_perform_getsock(). */
1040 nghttp2_session_resume_data(h2, stream->stream_id);
1041 }
1042
1043 DEBUGF(infof(conn->data, "http2_send returns %zu for stream %u\n", len,
1044 stream->stream_id));
1045 return len;
1046 }
1047
1048 /* Calculate number of headers contained in [mem, mem + len) */
1049 /* Here, we assume the curl http code generate *correct* HTTP header
1050 field block */
1051 nheader = 0;
1052 for(i = 0; i < len; ++i) {
1053 if(hdbuf[i] == 0x0a) {
1054 ++nheader;
1055 }
1056 }
1057 /* We counted additional 2 \n in the first and last line. We need 3
1058 new headers: :method, :path and :scheme. Therefore we need one
1059 more space. */
1060 nheader += 1;
1061 nva = malloc(sizeof(nghttp2_nv) * nheader);
1062 if(nva == NULL) {
1063 *err = CURLE_OUT_OF_MEMORY;
1064 return -1;
1065 }
1066 /* Extract :method, :path from request line */
1067 end = strchr(hdbuf, ' ');
1068 nva[0].name = (unsigned char *)":method";
1069 nva[0].namelen = (uint16_t)strlen((char *)nva[0].name);
1070 nva[0].value = (unsigned char *)hdbuf;
1071 nva[0].valuelen = (uint16_t)(end - hdbuf);
1072 nva[0].flags = NGHTTP2_NV_FLAG_NONE;
1073
1074 hdbuf = end + 1;
1075
1076 end = strchr(hdbuf, ' ');
1077 nva[1].name = (unsigned char *)":path";
1078 nva[1].namelen = (uint16_t)strlen((char *)nva[1].name);
1079 nva[1].value = (unsigned char *)hdbuf;
1080 nva[1].valuelen = (uint16_t)(end - hdbuf);
1081 nva[1].flags = NGHTTP2_NV_FLAG_NONE;
1082
1083 nva[2].name = (unsigned char *)":scheme";
1084 nva[2].namelen = (uint16_t)strlen((char *)nva[2].name);
1085 if(conn->handler->flags & PROTOPT_SSL)
1086 nva[2].value = (unsigned char *)"https";
1087 else
1088 nva[2].value = (unsigned char *)"http";
1089 nva[2].valuelen = (uint16_t)strlen((char *)nva[2].value);
1090 nva[2].flags = NGHTTP2_NV_FLAG_NONE;
1091
1092 hdbuf = strchr(hdbuf, 0x0a);
1093 ++hdbuf;
1094
1095 authority_idx = 0;
1096
1097 for(i = 3; i < nheader; ++i) {
1098 end = strchr(hdbuf, ':');
1099 assert(end);
1100 if(end - hdbuf == 4 && Curl_raw_nequal("host", hdbuf, 4)) {
1101 authority_idx = i;
1102 nva[i].name = (unsigned char *)":authority";
1103 nva[i].namelen = (uint16_t)strlen((char *)nva[i].name);
1104 }
1105 else {
1106 nva[i].name = (unsigned char *)hdbuf;
1107 nva[i].namelen = (uint16_t)(end - hdbuf);
1108 }
1109 hdbuf = end + 1;
1110 for(; *hdbuf == ' '; ++hdbuf);
1111 end = strchr(hdbuf, 0x0d);
1112 assert(end);
1113 nva[i].value = (unsigned char *)hdbuf;
1114 nva[i].valuelen = (uint16_t)(end - hdbuf);
1115 nva[i].flags = NGHTTP2_NV_FLAG_NONE;
1116
1117 hdbuf = end + 2;
1118 /* Inspect Content-Length header field and retrieve the request
1119 entity length so that we can set END_STREAM to the last DATA
1120 frame. */
1121 if(nva[i].namelen == 14 &&
1122 Curl_raw_nequal("content-length", (char*)nva[i].name, 14)) {
1123 size_t j;
1124 stream->upload_left = 0;
1125 for(j = 0; j < nva[i].valuelen; ++j) {
1126 stream->upload_left *= 10;
1127 stream->upload_left += nva[i].value[j] - '0';
1128 }
1129 DEBUGF(infof(conn->data,
1130 "request content-length=%"
1131 CURL_FORMAT_CURL_OFF_T
1132 "\n", stream->upload_left));
1133 }
1134 }
1135
1136 /* :authority must come before non-pseudo header fields */
1137 if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) {
1138 nghttp2_nv authority = nva[authority_idx];
1139 for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
1140 nva[i] = nva[i - 1];
1141 }
1142 nva[i] = authority;
1143 }
1144
1145 switch(conn->data->set.httpreq) {
1146 case HTTPREQ_POST:
1147 case HTTPREQ_POST_FORM:
1148 case HTTPREQ_PUT:
1149 data_prd.read_callback = data_source_read_callback;
1150 data_prd.source.ptr = NULL;
1151 stream_id = nghttp2_submit_request(h2, NULL, nva, nheader,
1152 &data_prd, NULL);
1153 break;
1154 default:
1155 stream_id = nghttp2_submit_request(h2, NULL, nva, nheader,
1156 NULL, NULL);
1157 }
1158
1159 free(nva);
1160
1161 if(stream_id < 0) {
1162 DEBUGF(infof(conn->data, "http2_send() send error\n"));
1163 *err = CURLE_SEND_ERROR;
1164 return -1;
1165 }
1166
1167 infof(conn->data, "Using Stream ID: %x (easy handle %p)\n",
1168 stream_id, conn->data);
1169 stream->stream_id = stream_id;
1170
1171 /* put the SessionHandle in the hash with the stream_id as key */
1172 if(!Curl_hash_add(&httpc->streamsh, &stream->stream_id, sizeof(stream_id),
1173 conn->data)) {
1174 failf(conn->data, "Couldn't add stream to hash!");
1175 *err = CURLE_OUT_OF_MEMORY;
1176 return -1;
1177 }
1178
1179 rv = nghttp2_session_send(h2);
1180
1181 if(rv != 0) {
1182 *err = CURLE_SEND_ERROR;
1183 return -1;
1184 }
1185
1186 if(stream->stream_id != -1) {
1187 /* If whole HEADERS frame was sent off to the underlying socket,
1188 the nghttp2 library calls data_source_read_callback. But only
1189 it found that no data available, so it deferred the DATA
1190 transmission. Which means that nghttp2_session_want_write()
1191 returns 0 on http2_perform_getsock(), which results that no
1192 writable socket check is performed. To workaround this, we
1193 issue nghttp2_session_resume_data() here to bring back DATA
1194 transmission from deferred state. */
1195 nghttp2_session_resume_data(h2, stream->stream_id);
1196 }
1197
1198 return len;
1199 }
1200
Curl_http2_setup(struct connectdata * conn)1201 CURLcode Curl_http2_setup(struct connectdata *conn)
1202 {
1203 CURLcode result;
1204 struct http_conn *httpc = &conn->proto.httpc;
1205 struct HTTP *stream = conn->data->req.protop;
1206
1207 stream->stream_id = -1;
1208
1209 if(!stream->header_recvbuf)
1210 stream->header_recvbuf = Curl_add_buffer_init();
1211
1212 if((conn->handler == &Curl_handler_http2_ssl) ||
1213 (conn->handler == &Curl_handler_http2))
1214 return CURLE_OK; /* already done */
1215
1216 if(conn->handler->flags & PROTOPT_SSL)
1217 conn->handler = &Curl_handler_http2_ssl;
1218 else
1219 conn->handler = &Curl_handler_http2;
1220
1221 result = Curl_http2_init(conn);
1222 if(result)
1223 return result;
1224
1225 infof(conn->data, "Using HTTP2, server supports multi-use\n");
1226 stream->upload_left = 0;
1227 stream->upload_mem = NULL;
1228 stream->upload_len = 0;
1229
1230 httpc->inbuflen = 0;
1231 httpc->nread_inbuf = 0;
1232
1233 httpc->pause_stream_id = 0;
1234
1235 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
1236 conn->httpversion = 20;
1237 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
1238
1239 infof(conn->data, "Connection state changed (HTTP/2 confirmed)\n");
1240 Curl_multi_connchanged(conn->data->multi);
1241
1242 return CURLE_OK;
1243 }
1244
Curl_http2_switched(struct connectdata * conn,const char * mem,size_t nread)1245 CURLcode Curl_http2_switched(struct connectdata *conn,
1246 const char *mem, size_t nread)
1247 {
1248 CURLcode result;
1249 struct http_conn *httpc = &conn->proto.httpc;
1250 int rv;
1251 ssize_t nproc;
1252 struct SessionHandle *data = conn->data;
1253 struct HTTP *stream = conn->data->req.protop;
1254
1255 result = Curl_http2_setup(conn);
1256 if(result)
1257 return result;
1258
1259 httpc->recv_underlying = (recving)conn->recv[FIRSTSOCKET];
1260 httpc->send_underlying = (sending)conn->send[FIRSTSOCKET];
1261 conn->recv[FIRSTSOCKET] = http2_recv;
1262 conn->send[FIRSTSOCKET] = http2_send;
1263
1264 if(conn->data->req.upgr101 == UPGR101_RECEIVED) {
1265 /* stream 1 is opened implicitly on upgrade */
1266 stream->stream_id = 1;
1267 /* queue SETTINGS frame (again) */
1268 rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings,
1269 httpc->binlen, NULL);
1270 if(rv != 0) {
1271 failf(data, "nghttp2_session_upgrade() failed: %s(%d)",
1272 nghttp2_strerror(rv), rv);
1273 return CURLE_HTTP2;
1274 }
1275
1276 /* put the SessionHandle in the hash with the stream->stream_id as key */
1277 if(!Curl_hash_add(&httpc->streamsh, &stream->stream_id,
1278 sizeof(stream->stream_id), conn->data)) {
1279 failf(conn->data, "Couldn't add stream to hash!");
1280 return CURLE_OUT_OF_MEMORY;
1281 }
1282 }
1283 else {
1284 /* stream ID is unknown at this point */
1285 stream->stream_id = -1;
1286 rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE, NULL, 0);
1287 if(rv != 0) {
1288 failf(data, "nghttp2_submit_settings() failed: %s(%d)",
1289 nghttp2_strerror(rv), rv);
1290 return CURLE_HTTP2;
1291 }
1292 }
1293
1294 /* we are going to copy mem to httpc->inbuf. This is required since
1295 mem is part of buffer pointed by stream->mem, and callbacks
1296 called by nghttp2_session_mem_recv() will write stream specific
1297 data into stream->mem, overwriting data already there. */
1298 if(H2_BUFSIZE < nread) {
1299 failf(data, "connection buffer size is too small to store data following "
1300 "HTTP Upgrade response header: buflen=%zu, datalen=%zu",
1301 H2_BUFSIZE, nread);
1302 return CURLE_HTTP2;
1303 }
1304
1305 infof(conn->data, "Copying HTTP/2 data in stream buffer to connection buffer"
1306 " after upgrade: len=%zu\n",
1307 nread);
1308
1309 memcpy(httpc->inbuf, mem, nread);
1310 httpc->inbuflen = nread;
1311
1312 nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf,
1313 httpc->inbuflen);
1314
1315 if(nghttp2_is_fatal((int)nproc)) {
1316 failf(data, "nghttp2_session_mem_recv() failed: %s(%d)",
1317 nghttp2_strerror((int)nproc), (int)nproc);
1318 return CURLE_HTTP2;
1319 }
1320
1321 DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", nproc));
1322
1323 if((ssize_t)nread == nproc) {
1324 httpc->inbuflen = 0;
1325 httpc->nread_inbuf = 0;
1326 }
1327 else {
1328 httpc->nread_inbuf += nproc;
1329 }
1330
1331 /* Try to send some frames since we may read SETTINGS already. */
1332 rv = nghttp2_session_send(httpc->h2);
1333
1334 if(rv != 0) {
1335 failf(data, "nghttp2_session_send() failed: %s(%d)",
1336 nghttp2_strerror(rv), rv);
1337 return CURLE_HTTP2;
1338 }
1339
1340 return CURLE_OK;
1341 }
1342
1343 #endif
1344