source: UsbWattMeter/trunk/curl-7.47.1/lib/http2.c@ 164

Last change on this file since 164 was 164, checked in by coas-nagasima, 6 years ago

TOPPERS/ECNLサンプルアプリ「USB充電器電力計」を追加

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-csrc
File size: 52.9 KB
Line 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2016, 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 https://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#include "url.h"
37#include "connect.h"
38
39/* The last #include files should be: */
40#include "curl_memory.h"
41#include "memdebug.h"
42
43#define MIN(x,y) ((x)<(y)?(x):(y))
44
45#if (NGHTTP2_VERSION_NUM < 0x010000)
46#error too old nghttp2 version, upgrade!
47#endif
48
49/*
50 * Curl_http2_init_state() is called when the easy handle is created and
51 * allows for HTTP/2 specific init of state.
52 */
53void Curl_http2_init_state(struct UrlState *state)
54{
55 state->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
56}
57
58/*
59 * Curl_http2_init_userset() is called when the easy handle is created and
60 * allows for HTTP/2 specific user-set fields.
61 */
62void Curl_http2_init_userset(struct UserDefined *set)
63{
64 set->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
65}
66
67static int http2_perform_getsock(const struct connectdata *conn,
68 curl_socket_t *sock, /* points to
69 numsocks
70 number of
71 sockets */
72 int numsocks)
73{
74 const struct http_conn *c = &conn->proto.httpc;
75 int bitmap = GETSOCK_BLANK;
76 (void)numsocks;
77
78 /* TODO We should check underlying socket state if it is SSL socket
79 because of renegotiation. */
80 sock[0] = conn->sock[FIRSTSOCKET];
81
82 if(nghttp2_session_want_read(c->h2))
83 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
84
85 if(nghttp2_session_want_write(c->h2))
86 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
87
88 return bitmap;
89}
90
91static int http2_getsock(struct connectdata *conn,
92 curl_socket_t *sock, /* points to numsocks
93 number of sockets */
94 int numsocks)
95{
96 return http2_perform_getsock(conn, sock, numsocks);
97}
98
99static CURLcode http2_disconnect(struct connectdata *conn,
100 bool dead_connection)
101{
102 struct HTTP *http = conn->data->req.protop;
103 struct http_conn *c = &conn->proto.httpc;
104 (void)dead_connection;
105
106 DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT starts now\n"));
107
108 nghttp2_session_del(c->h2);
109 Curl_safefree(c->inbuf);
110
111 if(http) {
112 Curl_add_buffer_free(http->header_recvbuf);
113 http->header_recvbuf = NULL; /* clear the pointer */
114 Curl_add_buffer_free(http->trailer_recvbuf);
115 http->trailer_recvbuf = NULL; /* clear the pointer */
116 for(; http->push_headers_used > 0; --http->push_headers_used) {
117 free(http->push_headers[http->push_headers_used - 1]);
118 }
119 free(http->push_headers);
120 http->push_headers = NULL;
121 }
122
123 DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT done\n"));
124
125 return CURLE_OK;
126}
127
128/* called from Curl_http_setup_conn */
129void Curl_http2_setup_req(struct SessionHandle *data)
130{
131 struct HTTP *http = data->req.protop;
132
133 http->nread_header_recvbuf = 0;
134 http->bodystarted = FALSE;
135 http->status_code = -1;
136 http->pausedata = NULL;
137 http->pauselen = 0;
138 http->error_code = NGHTTP2_NO_ERROR;
139 http->closed = FALSE;
140 http->mem = data->state.buffer;
141 http->len = BUFSIZE;
142 http->memlen = 0;
143}
144
145/* called from Curl_http_setup_conn */
146void Curl_http2_setup_conn(struct connectdata *conn)
147{
148 conn->proto.httpc.settings.max_concurrent_streams =
149 DEFAULT_MAX_CONCURRENT_STREAMS;
150}
151
152/*
153 * HTTP2 handler interface. This isn't added to the general list of protocols
154 * but will be used at run-time when the protocol is dynamically switched from
155 * HTTP to HTTP2.
156 */
157const struct Curl_handler Curl_handler_http2 = {
158 "HTTP2", /* scheme */
159 ZERO_NULL, /* setup_connection */
160 Curl_http, /* do_it */
161 Curl_http_done, /* done */
162 ZERO_NULL, /* do_more */
163 ZERO_NULL, /* connect_it */
164 ZERO_NULL, /* connecting */
165 ZERO_NULL, /* doing */
166 http2_getsock, /* proto_getsock */
167 http2_getsock, /* doing_getsock */
168 ZERO_NULL, /* domore_getsock */
169 http2_perform_getsock, /* perform_getsock */
170 http2_disconnect, /* disconnect */
171 ZERO_NULL, /* readwrite */
172 PORT_HTTP, /* defport */
173 CURLPROTO_HTTP, /* protocol */
174 PROTOPT_NONE /* flags */
175};
176
177const struct Curl_handler Curl_handler_http2_ssl = {
178 "HTTP2", /* scheme */
179 ZERO_NULL, /* setup_connection */
180 Curl_http, /* do_it */
181 Curl_http_done, /* done */
182 ZERO_NULL, /* do_more */
183 ZERO_NULL, /* connect_it */
184 ZERO_NULL, /* connecting */
185 ZERO_NULL, /* doing */
186 http2_getsock, /* proto_getsock */
187 http2_getsock, /* doing_getsock */
188 ZERO_NULL, /* domore_getsock */
189 http2_perform_getsock, /* perform_getsock */
190 http2_disconnect, /* disconnect */
191 ZERO_NULL, /* readwrite */
192 PORT_HTTP, /* defport */
193 CURLPROTO_HTTPS, /* protocol */
194 PROTOPT_SSL /* flags */
195};
196
197/*
198 * Store nghttp2 version info in this buffer, Prefix with a space. Return
199 * total length written.
200 */
201int Curl_http2_ver(char *p, size_t len)
202{
203 nghttp2_info *h2 = nghttp2_version(0);
204 return snprintf(p, len, " nghttp2/%s", h2->version_str);
205}
206
207/*
208 * The implementation of nghttp2_send_callback type. Here we write |data| with
209 * size |length| to the network and return the number of bytes actually
210 * written. See the documentation of nghttp2_send_callback for the details.
211 */
212static ssize_t send_callback(nghttp2_session *h2,
213 const uint8_t *data, size_t length, int flags,
214 void *userp)
215{
216 struct connectdata *conn = (struct connectdata *)userp;
217 struct http_conn *c = &conn->proto.httpc;
218 ssize_t written;
219 CURLcode result = CURLE_OK;
220
221 (void)h2;
222 (void)flags;
223
224 written = ((Curl_send*)c->send_underlying)(conn, FIRSTSOCKET,
225 data, length, &result);
226
227 if(result == CURLE_AGAIN) {
228 return NGHTTP2_ERR_WOULDBLOCK;
229 }
230
231 if(written == -1) {
232 failf(conn->data, "Failed sending HTTP2 data");
233 return NGHTTP2_ERR_CALLBACK_FAILURE;
234 }
235
236 if(!written)
237 return NGHTTP2_ERR_WOULDBLOCK;
238
239 return written;
240}
241
242
243/* We pass a pointer to this struct in the push callback, but the contents of
244 the struct are hidden from the user. */
245struct curl_pushheaders {
246 struct SessionHandle *data;
247 const nghttp2_push_promise *frame;
248};
249
250/*
251 * push header access function. Only to be used from within the push callback
252 */
253char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
254{
255 /* Verify that we got a good easy handle in the push header struct, mostly to
256 detect rubbish input fast(er). */
257 if(!h || !GOOD_EASY_HANDLE(h->data))
258 return NULL;
259 else {
260 struct HTTP *stream = h->data->req.protop;
261 if(num < stream->push_headers_used)
262 return stream->push_headers[num];
263 }
264 return NULL;
265}
266
267/*
268 * push header access function. Only to be used from within the push callback
269 */
270char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
271{
272 /* Verify that we got a good easy handle in the push header struct,
273 mostly to detect rubbish input fast(er). Also empty header name
274 is just a rubbish too. We have to allow ":" at the beginning of
275 the header, but header == ":" must be rejected. If we have ':' in
276 the middle of header, it could be matched in middle of the value,
277 this is because we do prefix match.*/
278 if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] ||
279 Curl_raw_equal(header, ":") || strchr(header + 1, ':'))
280 return NULL;
281 else {
282 struct HTTP *stream = h->data->req.protop;
283 size_t len = strlen(header);
284 size_t i;
285 for(i=0; i<stream->push_headers_used; i++) {
286 if(!strncmp(header, stream->push_headers[i], len)) {
287 /* sub-match, make sure that it is followed by a colon */
288 if(stream->push_headers[i][len] != ':')
289 continue;
290 return &stream->push_headers[i][len+1];
291 }
292 }
293 }
294 return NULL;
295}
296
297static CURL *duphandle(struct SessionHandle *data)
298{
299 struct SessionHandle *second = curl_easy_duphandle(data);
300 if(second) {
301 /* setup the request struct */
302 struct HTTP *http = calloc(1, sizeof(struct HTTP));
303 if(!http) {
304 (void)Curl_close(second);
305 second = NULL;
306 }
307 else {
308 second->req.protop = http;
309 http->header_recvbuf = Curl_add_buffer_init();
310 if(!http->header_recvbuf) {
311 free(http);
312 (void)Curl_close(second);
313 second = NULL;
314 }
315 else {
316 Curl_http2_setup_req(second);
317 second->state.stream_weight = data->state.stream_weight;
318 }
319 }
320 }
321 return second;
322}
323
324
325static int push_promise(struct SessionHandle *data,
326 struct connectdata *conn,
327 const nghttp2_push_promise *frame)
328{
329 int rv;
330 DEBUGF(infof(data, "PUSH_PROMISE received, stream %u!\n",
331 frame->promised_stream_id));
332 if(data->multi->push_cb) {
333 struct HTTP *stream;
334 struct HTTP *newstream;
335 struct curl_pushheaders heads;
336 CURLMcode rc;
337 struct http_conn *httpc;
338 size_t i;
339 /* clone the parent */
340 struct SessionHandle *newhandle = duphandle(data);
341 if(!newhandle) {
342 infof(data, "failed to duplicate handle\n");
343 rv = 1; /* FAIL HARD */
344 goto fail;
345 }
346
347 heads.data = data;
348 heads.frame = frame;
349 /* ask the application */
350 DEBUGF(infof(data, "Got PUSH_PROMISE, ask application!\n"));
351
352 stream = data->req.protop;
353 if(!stream) {
354 failf(data, "Internal NULL stream!\n");
355 rv = 1;
356 goto fail;
357 }
358
359 rv = data->multi->push_cb(data, newhandle,
360 stream->push_headers_used, &heads,
361 data->multi->push_userp);
362
363 /* free the headers again */
364 for(i=0; i<stream->push_headers_used; i++)
365 free(stream->push_headers[i]);
366 free(stream->push_headers);
367 stream->push_headers = NULL;
368
369 if(rv) {
370 /* denied, kill off the new handle again */
371 (void)Curl_close(newhandle);
372 goto fail;
373 }
374
375 newstream = newhandle->req.protop;
376 newstream->stream_id = frame->promised_stream_id;
377 newhandle->req.maxdownload = -1;
378 newhandle->req.size = -1;
379
380 /* approved, add to the multi handle and immediately switch to PERFORM
381 state with the given connection !*/
382 rc = Curl_multi_add_perform(data->multi, newhandle, conn);
383 if(rc) {
384 infof(data, "failed to add handle to multi\n");
385 Curl_close(newhandle);
386 rv = 1;
387 goto fail;
388 }
389
390 httpc = &conn->proto.httpc;
391 nghttp2_session_set_stream_user_data(httpc->h2,
392 frame->promised_stream_id, newhandle);
393 }
394 else {
395 DEBUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n"));
396 rv = 1;
397 }
398 fail:
399 return rv;
400}
401
402static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
403 void *userp)
404{
405 struct connectdata *conn = (struct connectdata *)userp;
406 struct http_conn *httpc = &conn->proto.httpc;
407 struct SessionHandle *data_s = NULL;
408 struct HTTP *stream = NULL;
409 static int lastStream = -1;
410 int rv;
411 size_t left, ncopy;
412 int32_t stream_id = frame->hd.stream_id;
413
414 if(!stream_id) {
415 /* stream ID zero is for connection-oriented stuff */
416 if(frame->hd.type == NGHTTP2_SETTINGS) {
417 uint32_t max_conn = httpc->settings.max_concurrent_streams;
418 DEBUGF(infof(conn->data, "Got SETTINGS\n"));
419 httpc->settings.max_concurrent_streams =
420 nghttp2_session_get_remote_settings(
421 session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
422 httpc->settings.enable_push =
423 nghttp2_session_get_remote_settings(
424 session, NGHTTP2_SETTINGS_ENABLE_PUSH);
425 DEBUGF(infof(conn->data, "MAX_CONCURRENT_STREAMS == %d\n",
426 httpc->settings.max_concurrent_streams));
427 DEBUGF(infof(conn->data, "ENABLE_PUSH == %s\n",
428 httpc->settings.enable_push?"TRUE":"false"));
429 if(max_conn != httpc->settings.max_concurrent_streams) {
430 /* only signal change if the value actually changed */
431 infof(conn->data,
432 "Connection state changed (MAX_CONCURRENT_STREAMS updated)!\n");
433 Curl_multi_connchanged(conn->data->multi);
434 }
435 }
436 return 0;
437 }
438 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
439 if(lastStream != stream_id) {
440 lastStream = stream_id;
441 }
442 if(!data_s) {
443 DEBUGF(infof(conn->data,
444 "No SessionHandle associated with stream: %x\n",
445 stream_id));
446 return 0;
447 }
448
449 stream = data_s->req.protop;
450 if(!stream)
451 return NGHTTP2_ERR_CALLBACK_FAILURE;
452
453 DEBUGF(infof(data_s, "on_frame_recv() header %x stream %x\n",
454 frame->hd.type, stream_id));
455
456 switch(frame->hd.type) {
457 case NGHTTP2_DATA:
458 /* If body started on this stream, then receiving DATA is illegal. */
459 if(!stream->bodystarted) {
460 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
461 stream_id, NGHTTP2_PROTOCOL_ERROR);
462
463 if(nghttp2_is_fatal(rv)) {
464 return NGHTTP2_ERR_CALLBACK_FAILURE;
465 }
466 }
467 break;
468 case NGHTTP2_HEADERS:
469 if(stream->bodystarted) {
470 /* Only valid HEADERS after body started is trailer HEADERS. We
471 buffer them in on_header callback. */
472 break;
473 }
474
475 /* nghttp2 guarantees that :status is received, and we store it to
476 stream->status_code */
477 DEBUGASSERT(stream->status_code != -1);
478
479 /* Only final status code signals the end of header */
480 if(stream->status_code / 100 != 1) {
481 stream->bodystarted = TRUE;
482 stream->status_code = -1;
483 }
484
485 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
486
487 left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
488 ncopy = MIN(stream->len, left);
489
490 memcpy(&stream->mem[stream->memlen],
491 stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
492 ncopy);
493 stream->nread_header_recvbuf += ncopy;
494
495 DEBUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n",
496 ncopy, stream_id, stream->mem));
497
498 stream->len -= ncopy;
499 stream->memlen += ncopy;
500
501 data_s->state.drain++;
502 {
503 /* get the pointer from userp again since it was re-assigned above */
504 struct connectdata *conn_s = (struct connectdata *)userp;
505
506 /* if we receive data for another handle, wake that up */
507 if(conn_s->data != data_s)
508 Curl_expire(data_s, 1);
509 }
510 break;
511 case NGHTTP2_PUSH_PROMISE:
512 rv = push_promise(data_s, conn, &frame->push_promise);
513 if(rv) { /* deny! */
514 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
515 frame->push_promise.promised_stream_id,
516 NGHTTP2_CANCEL);
517 if(nghttp2_is_fatal(rv)) {
518 return rv;
519 }
520 }
521 break;
522 default:
523 DEBUGF(infof(conn->data, "Got frame type %x for stream %u!\n",
524 frame->hd.type, stream_id));
525 break;
526 }
527 return 0;
528}
529
530static int on_invalid_frame_recv(nghttp2_session *session,
531 const nghttp2_frame *frame,
532 int lib_error_code, void *userp)
533{
534 struct SessionHandle *data_s = NULL;
535 (void)userp;
536
537 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
538 if(data_s) {
539 DEBUGF(infof(data_s,
540 "on_invalid_frame_recv() was called, error=%d:%s\n",
541 lib_error_code, nghttp2_strerror(lib_error_code)));
542 }
543 return 0;
544}
545
546static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
547 int32_t stream_id,
548 const uint8_t *data, size_t len, void *userp)
549{
550 struct HTTP *stream;
551 struct SessionHandle *data_s;
552 size_t nread;
553 struct connectdata *conn = (struct connectdata *)userp;
554 (void)session;
555 (void)flags;
556 (void)data;
557
558 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
559
560 /* get the stream from the hash based on Stream ID */
561 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
562 if(!data_s)
563 /* Receiving a Stream ID not in the hash should not happen, this is an
564 internal error more than anything else! */
565 return NGHTTP2_ERR_CALLBACK_FAILURE;
566
567 stream = data_s->req.protop;
568 if(!stream)
569 return NGHTTP2_ERR_CALLBACK_FAILURE;
570
571 nread = MIN(stream->len, len);
572 memcpy(&stream->mem[stream->memlen], data, nread);
573
574 stream->len -= nread;
575 stream->memlen += nread;
576
577 data_s->state.drain++;
578
579 /* if we receive data for another handle, wake that up */
580 if(conn->data != data_s)
581 Curl_expire(data_s, 1); /* TODO: fix so that this can be set to 0 for
582 immediately? */
583
584 DEBUGF(infof(data_s, "%zu data received for stream %u "
585 "(%zu left in buffer %p, total %zu)\n",
586 nread, stream_id,
587 stream->len, stream->mem,
588 stream->memlen));
589
590 if(nread < len) {
591 stream->pausedata = data + nread;
592 stream->pauselen = len - nread;
593 DEBUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
594 ", stream %u\n",
595 len - nread, stream_id));
596 data_s->easy_conn->proto.httpc.pause_stream_id = stream_id;
597 return NGHTTP2_ERR_PAUSE;
598 }
599 return 0;
600}
601
602static int before_frame_send(nghttp2_session *session,
603 const nghttp2_frame *frame,
604 void *userp)
605{
606 struct SessionHandle *data_s;
607 (void)userp;
608
609 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
610 if(data_s) {
611 DEBUGF(infof(data_s, "before_frame_send() was called\n"));
612 }
613
614 return 0;
615}
616static int on_frame_send(nghttp2_session *session,
617 const nghttp2_frame *frame,
618 void *userp)
619{
620 struct SessionHandle *data_s;
621 (void)userp;
622
623 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
624 if(data_s) {
625 DEBUGF(infof(data_s, "on_frame_send() was called, length = %zd\n",
626 frame->hd.length));
627 }
628 return 0;
629}
630static int on_frame_not_send(nghttp2_session *session,
631 const nghttp2_frame *frame,
632 int lib_error_code, void *userp)
633{
634 struct SessionHandle *data_s;
635 (void)userp;
636
637 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
638 if(data_s) {
639 DEBUGF(infof(data_s,
640 "on_frame_not_send() was called, lib_error_code = %d\n",
641 lib_error_code));
642 }
643 return 0;
644}
645static int on_stream_close(nghttp2_session *session, int32_t stream_id,
646 uint32_t error_code, void *userp)
647{
648 struct SessionHandle *data_s;
649 struct HTTP *stream;
650 (void)session;
651 (void)stream_id;
652 (void)userp;
653
654 if(stream_id) {
655 /* get the stream from the hash based on Stream ID, stream ID zero is for
656 connection-oriented stuff */
657 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
658 if(!data_s) {
659 /* We could get stream ID not in the hash. For example, if we
660 decided to reject stream (e.g., PUSH_PROMISE). */
661 return 0;
662 }
663 DEBUGF(infof(data_s, "on_stream_close(), error_code = %d, stream %u\n",
664 error_code, stream_id));
665 stream = data_s->req.protop;
666 if(!stream)
667 return NGHTTP2_ERR_CALLBACK_FAILURE;
668
669 stream->error_code = error_code;
670 stream->closed = TRUE;
671
672 /* remove the entry from the hash as the stream is now gone */
673 nghttp2_session_set_stream_user_data(session, stream_id, 0);
674 DEBUGF(infof(data_s, "Removed stream %u hash!\n", stream_id));
675 }
676 return 0;
677}
678
679static int on_begin_headers(nghttp2_session *session,
680 const nghttp2_frame *frame, void *userp)
681{
682 struct HTTP *stream;
683 struct SessionHandle *data_s = NULL;
684 (void)userp;
685
686 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
687 if(!data_s) {
688 return 0;
689 }
690
691 DEBUGF(infof(data_s, "on_begin_headers() was called\n"));
692
693 if(frame->hd.type != NGHTTP2_HEADERS) {
694 return 0;
695 }
696
697 stream = data_s->req.protop;
698 if(!stream || !stream->bodystarted) {
699 return 0;
700 }
701
702 /* This is trailer HEADERS started. Allocate buffer for them. */
703 DEBUGF(infof(data_s, "trailer field started\n"));
704
705 assert(stream->trailer_recvbuf == NULL);
706
707 stream->trailer_recvbuf = Curl_add_buffer_init();
708 if(!stream->trailer_recvbuf) {
709 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
710 }
711
712 return 0;
713}
714
715/* Decode HTTP status code. Returns -1 if no valid status code was
716 decoded. */
717static int decode_status_code(const uint8_t *value, size_t len)
718{
719 int i;
720 int res;
721
722 if(len != 3) {
723 return -1;
724 }
725
726 res = 0;
727
728 for(i = 0; i < 3; ++i) {
729 char c = value[i];
730
731 if(c < '0' || c > '9') {
732 return -1;
733 }
734
735 res *= 10;
736 res += c - '0';
737 }
738
739 return res;
740}
741
742/* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
743static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
744 const uint8_t *name, size_t namelen,
745 const uint8_t *value, size_t valuelen,
746 uint8_t flags,
747 void *userp)
748{
749 struct HTTP *stream;
750 struct SessionHandle *data_s;
751 int32_t stream_id = frame->hd.stream_id;
752 struct connectdata *conn = (struct connectdata *)userp;
753 (void)flags;
754
755 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
756
757 /* get the stream from the hash based on Stream ID */
758 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
759 if(!data_s)
760 /* Receiving a Stream ID not in the hash should not happen, this is an
761 internal error more than anything else! */
762 return NGHTTP2_ERR_CALLBACK_FAILURE;
763
764 stream = data_s->req.protop;
765 if(!stream) {
766 failf(data_s, "Internal NULL stream! 5\n");
767 return NGHTTP2_ERR_CALLBACK_FAILURE;
768 }
769
770 /* Store received PUSH_PROMISE headers to be used when the subsequent
771 PUSH_PROMISE callback comes */
772 if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
773 char *h;
774
775 if(!stream->push_headers) {
776 stream->push_headers_alloc = 10;
777 stream->push_headers = malloc(stream->push_headers_alloc *
778 sizeof(char *));
779 stream->push_headers_used = 0;
780 }
781 else if(stream->push_headers_used ==
782 stream->push_headers_alloc) {
783 char **headp;
784 stream->push_headers_alloc *= 2;
785 headp = realloc(stream->push_headers,
786 stream->push_headers_alloc * sizeof(char *));
787 if(!headp) {
788 free(stream->push_headers);
789 stream->push_headers = NULL;
790 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
791 }
792 stream->push_headers = headp;
793 }
794 h = aprintf("%s:%s", name, value);
795 if(h)
796 stream->push_headers[stream->push_headers_used++] = h;
797 return 0;
798 }
799
800 if(stream->bodystarted) {
801 /* This is trailer fields. */
802 /* 3 is for ":" and "\r\n". */
803 uint32_t n = (uint32_t)(namelen + valuelen + 3);
804
805 DEBUGF(infof(data_s, "h2 trailer: %.*s: %.*s\n", namelen, name, valuelen,
806 value));
807
808 Curl_add_buffer(stream->trailer_recvbuf, &n, sizeof(n));
809 Curl_add_buffer(stream->trailer_recvbuf, name, namelen);
810 Curl_add_buffer(stream->trailer_recvbuf, ":", 1);
811 Curl_add_buffer(stream->trailer_recvbuf, value, valuelen);
812 Curl_add_buffer(stream->trailer_recvbuf, "\r\n\0", 3);
813
814 return 0;
815 }
816
817 if(namelen == sizeof(":status") - 1 &&
818 memcmp(":status", name, namelen) == 0) {
819 /* nghttp2 guarantees :status is received first and only once, and
820 value is 3 digits status code, and decode_status_code always
821 succeeds. */
822 stream->status_code = decode_status_code(value, valuelen);
823 DEBUGASSERT(stream->status_code != -1);
824
825 Curl_add_buffer(stream->header_recvbuf, "HTTP/2.0 ", 9);
826 Curl_add_buffer(stream->header_recvbuf, value, valuelen);
827 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
828 data_s->state.drain++;
829 /* if we receive data for another handle, wake that up */
830 if(conn->data != data_s)
831 Curl_expire(data_s, 1);
832
833 DEBUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)\n",
834 stream->status_code, data_s));
835 return 0;
836 }
837
838 /* nghttp2 guarantees that namelen > 0, and :status was already
839 received, and this is not pseudo-header field . */
840 /* convert to a HTTP1-style header */
841 Curl_add_buffer(stream->header_recvbuf, name, namelen);
842 Curl_add_buffer(stream->header_recvbuf, ":", 1);
843 Curl_add_buffer(stream->header_recvbuf, value, valuelen);
844 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
845 data_s->state.drain++;
846 /* if we receive data for another handle, wake that up */
847 if(conn->data != data_s)
848 Curl_expire(data_s, 1);
849
850 DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
851 value));
852
853 return 0; /* 0 is successful */
854}
855
856static ssize_t data_source_read_callback(nghttp2_session *session,
857 int32_t stream_id,
858 uint8_t *buf, size_t length,
859 uint32_t *data_flags,
860 nghttp2_data_source *source,
861 void *userp)
862{
863 struct SessionHandle *data_s;
864 struct HTTP *stream = NULL;
865 size_t nread;
866 (void)source;
867 (void)userp;
868
869 if(stream_id) {
870 /* get the stream from the hash based on Stream ID, stream ID zero is for
871 connection-oriented stuff */
872 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
873 if(!data_s)
874 /* Receiving a Stream ID not in the hash should not happen, this is an
875 internal error more than anything else! */
876 return NGHTTP2_ERR_CALLBACK_FAILURE;
877
878 stream = data_s->req.protop;
879 if(!stream)
880 return NGHTTP2_ERR_CALLBACK_FAILURE;
881 }
882 else
883 return NGHTTP2_ERR_INVALID_ARGUMENT;
884
885 nread = MIN(stream->upload_len, length);
886 if(nread > 0) {
887 memcpy(buf, stream->upload_mem, nread);
888 stream->upload_mem += nread;
889 stream->upload_len -= nread;
890 stream->upload_left -= nread;
891 }
892
893 if(stream->upload_left == 0)
894 *data_flags = 1;
895 else if(nread == 0)
896 return NGHTTP2_ERR_DEFERRED;
897
898 DEBUGF(infof(data_s, "data_source_read_callback: "
899 "returns %zu bytes stream %u\n",
900 nread, stream_id));
901
902 return nread;
903}
904
905/*
906 * The HTTP2 settings we send in the Upgrade request
907 */
908static nghttp2_settings_entry settings[] = {
909 { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 },
910 { NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, NGHTTP2_INITIAL_WINDOW_SIZE },
911};
912
913#define H2_BUFSIZE 32768
914
915/*
916 * Initialize nghttp2 for a Curl connection
917 */
918CURLcode Curl_http2_init(struct connectdata *conn)
919{
920 if(!conn->proto.httpc.h2) {
921 int rc;
922 nghttp2_session_callbacks *callbacks;
923
924 conn->proto.httpc.inbuf = malloc(H2_BUFSIZE);
925 if(conn->proto.httpc.inbuf == NULL)
926 return CURLE_OUT_OF_MEMORY;
927
928 rc = nghttp2_session_callbacks_new(&callbacks);
929
930 if(rc) {
931 failf(conn->data, "Couldn't initialize nghttp2 callbacks!");
932 return CURLE_OUT_OF_MEMORY; /* most likely at least */
933 }
934
935 /* nghttp2_send_callback */
936 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
937 /* nghttp2_on_frame_recv_callback */
938 nghttp2_session_callbacks_set_on_frame_recv_callback
939 (callbacks, on_frame_recv);
940 /* nghttp2_on_invalid_frame_recv_callback */
941 nghttp2_session_callbacks_set_on_invalid_frame_recv_callback
942 (callbacks, on_invalid_frame_recv);
943 /* nghttp2_on_data_chunk_recv_callback */
944 nghttp2_session_callbacks_set_on_data_chunk_recv_callback
945 (callbacks, on_data_chunk_recv);
946 /* nghttp2_before_frame_send_callback */
947 nghttp2_session_callbacks_set_before_frame_send_callback
948 (callbacks, before_frame_send);
949 /* nghttp2_on_frame_send_callback */
950 nghttp2_session_callbacks_set_on_frame_send_callback
951 (callbacks, on_frame_send);
952 /* nghttp2_on_frame_not_send_callback */
953 nghttp2_session_callbacks_set_on_frame_not_send_callback
954 (callbacks, on_frame_not_send);
955 /* nghttp2_on_stream_close_callback */
956 nghttp2_session_callbacks_set_on_stream_close_callback
957 (callbacks, on_stream_close);
958 /* nghttp2_on_begin_headers_callback */
959 nghttp2_session_callbacks_set_on_begin_headers_callback
960 (callbacks, on_begin_headers);
961 /* nghttp2_on_header_callback */
962 nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
963
964 /* The nghttp2 session is not yet setup, do it */
965 rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
966
967 nghttp2_session_callbacks_del(callbacks);
968
969 if(rc) {
970 failf(conn->data, "Couldn't initialize nghttp2!");
971 return CURLE_OUT_OF_MEMORY; /* most likely at least */
972 }
973 }
974 return CURLE_OK;
975}
976
977/*
978 * Append headers to ask for a HTTP1.1 to HTTP2 upgrade.
979 */
980CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req,
981 struct connectdata *conn)
982{
983 CURLcode result;
984 ssize_t binlen;
985 char *base64;
986 size_t blen;
987 struct SingleRequest *k = &conn->data->req;
988 uint8_t *binsettings = conn->proto.httpc.binsettings;
989
990 /* As long as we have a fixed set of settings, we don't have to dynamically
991 * figure out the base64 strings since it'll always be the same. However,
992 * the settings will likely not be fixed every time in the future.
993 */
994
995 /* this returns number of bytes it wrote */
996 binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
997 settings,
998 sizeof(settings)/sizeof(settings[0]));
999 if(!binlen) {
1000 failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
1001 return CURLE_FAILED_INIT;
1002 }
1003 conn->proto.httpc.binlen = binlen;
1004
1005 result = Curl_base64url_encode(conn->data, (const char *)binsettings, binlen,
1006 &base64, &blen);
1007 if(result)
1008 return result;
1009
1010 result = Curl_add_bufferf(req,
1011 "Connection: Upgrade, HTTP2-Settings\r\n"
1012 "Upgrade: %s\r\n"
1013 "HTTP2-Settings: %s\r\n",
1014 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
1015 free(base64);
1016
1017 k->upgr101 = UPGR101_REQUESTED;
1018
1019 return result;
1020}
1021
1022static ssize_t http2_handle_stream_close(struct connectdata *conn,
1023 struct SessionHandle *data,
1024 struct HTTP *stream, CURLcode *err) {
1025 char *trailer_pos, *trailer_end;
1026 CURLcode result;
1027 struct http_conn *httpc = &conn->proto.httpc;
1028
1029 if(httpc->pause_stream_id == stream->stream_id) {
1030 httpc->pause_stream_id = 0;
1031 }
1032 /* Reset to FALSE to prevent infinite loop in readwrite_data
1033 function. */
1034 stream->closed = FALSE;
1035 if(stream->error_code != NGHTTP2_NO_ERROR) {
1036 failf(data, "HTTP/2 stream %u was not closed cleanly: error_code = %d",
1037 stream->stream_id, stream->error_code);
1038 *err = CURLE_HTTP2;
1039 return -1;
1040 }
1041
1042 if(stream->trailer_recvbuf && stream->trailer_recvbuf->buffer) {
1043 trailer_pos = stream->trailer_recvbuf->buffer;
1044 trailer_end = trailer_pos + stream->trailer_recvbuf->size_used;
1045
1046 for(; trailer_pos < trailer_end;) {
1047 uint32_t n;
1048 memcpy(&n, trailer_pos, sizeof(n));
1049 trailer_pos += sizeof(n);
1050
1051 result = Curl_client_write(conn, CLIENTWRITE_HEADER, trailer_pos, n);
1052 if(result) {
1053 *err = result;
1054 return -1;
1055 }
1056
1057 trailer_pos += n + 1;
1058 }
1059 }
1060
1061 DEBUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close\n"));
1062 return 0;
1063}
1064
1065/*
1066 * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
1067 * and dependency to the peer. It also stores the updated values in the state
1068 * struct.
1069 */
1070
1071static void h2_pri_spec(struct SessionHandle *data,
1072 nghttp2_priority_spec *pri_spec)
1073{
1074 struct HTTP *depstream = (data->set.stream_depends_on?
1075 data->set.stream_depends_on->req.protop:NULL);
1076 int32_t depstream_id = depstream? depstream->stream_id:0;
1077 nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_weight,
1078 data->set.stream_depends_e);
1079 data->state.stream_weight = data->set.stream_weight;
1080 data->state.stream_depends_e = data->set.stream_depends_e;
1081 data->state.stream_depends_on = data->set.stream_depends_on;
1082}
1083
1084/*
1085 * h2_session_send() checks if there's been an update in the priority /
1086 * dependency settings and if so it submits a PRIORITY frame with the updated
1087 * info.
1088 */
1089static int h2_session_send(struct SessionHandle *data,
1090 nghttp2_session *h2)
1091{
1092 struct HTTP *stream = data->req.protop;
1093 if((data->set.stream_weight != data->state.stream_weight) ||
1094 (data->set.stream_depends_e != data->state.stream_depends_e) ||
1095 (data->set.stream_depends_on != data->state.stream_depends_on) ) {
1096 /* send new weight and/or dependency */
1097 nghttp2_priority_spec pri_spec;
1098 int rv;
1099
1100 h2_pri_spec(data, &pri_spec);
1101
1102 DEBUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)\n",
1103 stream->stream_id, data));
1104 rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
1105 &pri_spec);
1106 if(rv)
1107 return rv;
1108 }
1109
1110 return nghttp2_session_send(h2);
1111}
1112
1113/*
1114 * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return
1115 * a regular CURLcode value.
1116 */
1117static ssize_t http2_recv(struct connectdata *conn, int sockindex,
1118 char *mem, size_t len, CURLcode *err)
1119{
1120 CURLcode result = CURLE_OK;
1121 ssize_t rv;
1122 ssize_t nread;
1123 struct http_conn *httpc = &conn->proto.httpc;
1124 struct SessionHandle *data = conn->data;
1125 struct HTTP *stream = data->req.protop;
1126
1127 (void)sockindex; /* we always do HTTP2 on sockindex 0 */
1128
1129 /* If stream is closed, return 0 to signal the http routine to close
1130 the connection. We need to handle stream closure here,
1131 otherwise, we may be going to read from underlying connection,
1132 and gets EAGAIN, and we will get stuck there. */
1133 if(stream->memlen == 0 && stream->closed) {
1134 return http2_handle_stream_close(conn, data, stream, err);
1135 }
1136
1137 /* Nullify here because we call nghttp2_session_send() and they
1138 might refer to the old buffer. */
1139 stream->upload_mem = NULL;
1140 stream->upload_len = 0;
1141
1142 /*
1143 * At this point 'stream' is just in the SessionHandle the connection
1144 * identifies as its owner at this time.
1145 */
1146
1147 if(stream->bodystarted &&
1148 stream->nread_header_recvbuf < stream->header_recvbuf->size_used) {
1149 /* If there is body data pending for this stream to return, do that */
1150 size_t left =
1151 stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
1152 size_t ncopy = MIN(len, left);
1153 memcpy(mem, stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
1154 ncopy);
1155 stream->nread_header_recvbuf += ncopy;
1156
1157 DEBUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf\n",
1158 (int)ncopy));
1159 return ncopy;
1160 }
1161
1162 DEBUGF(infof(data, "http2_recv: easy %p (stream %u)\n",
1163 data, stream->stream_id));
1164
1165 if((data->state.drain) && stream->memlen) {
1166 DEBUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n",
1167 stream->memlen, stream->stream_id,
1168 stream->mem, mem));
1169 if(mem != stream->mem) {
1170 /* if we didn't get the same buffer this time, we must move the data to
1171 the beginning */
1172 memmove(mem, stream->mem, stream->memlen);
1173 stream->len = len - stream->memlen;
1174 stream->mem = mem;
1175 }
1176 }
1177 else if(stream->pausedata) {
1178 nread = MIN(len, stream->pauselen);
1179 memcpy(mem, stream->pausedata, nread);
1180
1181 stream->pausedata += nread;
1182 stream->pauselen -= nread;
1183
1184 infof(data, "%zu data bytes written\n", nread);
1185 if(stream->pauselen == 0) {
1186 DEBUGF(infof(data, "Unpaused by stream %u\n", stream->stream_id));
1187 assert(httpc->pause_stream_id == stream->stream_id);
1188 httpc->pause_stream_id = 0;
1189
1190 stream->pausedata = NULL;
1191 stream->pauselen = 0;
1192
1193 /* When NGHTTP2_ERR_PAUSE is returned from
1194 data_source_read_callback, we might not process DATA frame
1195 fully. Calling nghttp2_session_mem_recv() again will
1196 continue to process DATA frame, but if there is no incoming
1197 frames, then we have to call it again with 0-length data.
1198 Without this, on_stream_close callback will not be called,
1199 and stream could be hanged. */
1200 nghttp2_session_mem_recv(httpc->h2, NULL, 0);
1201 }
1202 DEBUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u\n",
1203 nread, stream->stream_id));
1204 return nread;
1205 }
1206 else if(httpc->pause_stream_id) {
1207 /* If a stream paused nghttp2_session_mem_recv previously, and has
1208 not processed all data, it still refers to the buffer in
1209 nghttp2_session. If we call nghttp2_session_mem_recv(), we may
1210 overwrite that buffer. To avoid that situation, just return
1211 here with CURLE_AGAIN. This could be busy loop since data in
1212 socket is not read. But it seems that usually streams are
1213 notified with its drain property, and socket is read again
1214 quickly. */
1215 *err = CURLE_AGAIN;
1216 return -1;
1217 }
1218 else {
1219 char *inbuf;
1220 /* remember where to store incoming data for this stream and how big the
1221 buffer is */
1222 stream->mem = mem;
1223 stream->len = len;
1224 stream->memlen = 0;
1225
1226 if(httpc->inbuflen == 0) {
1227 nread = ((Curl_recv *)httpc->recv_underlying)(
1228 conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
1229
1230 if(nread == -1) {
1231 if(result != CURLE_AGAIN)
1232 failf(data, "Failed receiving HTTP2 data");
1233 *err = result;
1234 return -1;
1235 }
1236
1237 if(nread == 0) {
1238 failf(data, "Unexpected EOF");
1239 *err = CURLE_RECV_ERROR;
1240 return -1;
1241 }
1242
1243 DEBUGF(infof(data, "nread=%zd\n", nread));
1244
1245 httpc->inbuflen = nread;
1246 inbuf = httpc->inbuf;
1247 }
1248 else {
1249 nread = httpc->inbuflen - httpc->nread_inbuf;
1250 inbuf = httpc->inbuf + httpc->nread_inbuf;
1251
1252 DEBUGF(infof(data, "Use data left in connection buffer, nread=%zd\n",
1253 nread));
1254 }
1255 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
1256
1257 if(nghttp2_is_fatal((int)rv)) {
1258 failf(data, "nghttp2_session_mem_recv() returned %d:%s\n",
1259 rv, nghttp2_strerror((int)rv));
1260 *err = CURLE_RECV_ERROR;
1261 return 0;
1262 }
1263 DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", rv));
1264 if(nread == rv) {
1265 DEBUGF(infof(data, "All data in connection buffer processed\n"));
1266 httpc->inbuflen = 0;
1267 httpc->nread_inbuf = 0;
1268 }
1269 else {
1270 httpc->nread_inbuf += rv;
1271 DEBUGF(infof(data, "%zu bytes left in connection buffer\n",
1272 httpc->inbuflen - httpc->nread_inbuf));
1273 }
1274 /* Always send pending frames in nghttp2 session, because
1275 nghttp2_session_mem_recv() may queue new frame */
1276 rv = h2_session_send(data, httpc->h2);
1277 if(rv != 0) {
1278 *err = CURLE_SEND_ERROR;
1279 return 0;
1280 }
1281 }
1282 if(stream->memlen) {
1283 ssize_t retlen = stream->memlen;
1284 DEBUGF(infof(data, "http2_recv: returns %zd for stream %u\n",
1285 retlen, stream->stream_id));
1286 stream->memlen = 0;
1287
1288 if(httpc->pause_stream_id == stream->stream_id) {
1289 /* data for this stream is returned now, but this stream caused a pause
1290 already so we need it called again asap */
1291 DEBUGF(infof(data, "Data returned for PAUSED stream %u\n",
1292 stream->stream_id));
1293 }
1294 else
1295 data->state.drain = 0; /* this stream is hereby drained */
1296
1297 return retlen;
1298 }
1299 /* If stream is closed, return 0 to signal the http routine to close
1300 the connection */
1301 if(stream->closed) {
1302 return http2_handle_stream_close(conn, data, stream, err);
1303 }
1304 *err = CURLE_AGAIN;
1305 DEBUGF(infof(data, "http2_recv returns AGAIN for stream %u\n",
1306 stream->stream_id));
1307 return -1;
1308}
1309
1310/* Index where :authority header field will appear in request header
1311 field list. */
1312#define AUTHORITY_DST_IDX 3
1313
1314/* return number of received (decrypted) bytes */
1315static ssize_t http2_send(struct connectdata *conn, int sockindex,
1316 const void *mem, size_t len, CURLcode *err)
1317{
1318 /*
1319 * BIG TODO: Currently, we send request in this function, but this
1320 * function is also used to send request body. It would be nice to
1321 * add dedicated function for request.
1322 */
1323 int rv;
1324 struct http_conn *httpc = &conn->proto.httpc;
1325 struct HTTP *stream = conn->data->req.protop;
1326 nghttp2_nv *nva;
1327 size_t nheader;
1328 size_t i;
1329 size_t authority_idx;
1330 char *hdbuf = (char*)mem;
1331 char *end;
1332 nghttp2_data_provider data_prd;
1333 int32_t stream_id;
1334 nghttp2_session *h2 = httpc->h2;
1335 nghttp2_priority_spec pri_spec;
1336
1337 (void)sockindex;
1338
1339 DEBUGF(infof(conn->data, "http2_send len=%zu\n", len));
1340
1341 if(stream->stream_id != -1) {
1342 /* If stream_id != -1, we have dispatched request HEADERS, and now
1343 are going to send or sending request body in DATA frame */
1344 stream->upload_mem = mem;
1345 stream->upload_len = len;
1346 nghttp2_session_resume_data(h2, stream->stream_id);
1347 rv = h2_session_send(conn->data, h2);
1348 if(nghttp2_is_fatal(rv)) {
1349 *err = CURLE_SEND_ERROR;
1350 return -1;
1351 }
1352 len -= stream->upload_len;
1353
1354 /* Nullify here because we call nghttp2_session_send() and they
1355 might refer to the old buffer. */
1356 stream->upload_mem = NULL;
1357 stream->upload_len = 0;
1358
1359 if(stream->upload_left) {
1360 /* we are sure that we have more data to send here. Calling the
1361 following API will make nghttp2_session_want_write() return
1362 nonzero if remote window allows it, which then libcurl checks
1363 socket is writable or not. See http2_perform_getsock(). */
1364 nghttp2_session_resume_data(h2, stream->stream_id);
1365 }
1366
1367 DEBUGF(infof(conn->data, "http2_send returns %zu for stream %u\n", len,
1368 stream->stream_id));
1369 return len;
1370 }
1371
1372 /* Calculate number of headers contained in [mem, mem + len) */
1373 /* Here, we assume the curl http code generate *correct* HTTP header
1374 field block */
1375 nheader = 0;
1376 for(i = 0; i < len; ++i) {
1377 if(hdbuf[i] == 0x0a) {
1378 ++nheader;
1379 }
1380 }
1381 /* We counted additional 2 \n in the first and last line. We need 3
1382 new headers: :method, :path and :scheme. Therefore we need one
1383 more space. */
1384 nheader += 1;
1385 nva = malloc(sizeof(nghttp2_nv) * nheader);
1386 if(nva == NULL) {
1387 *err = CURLE_OUT_OF_MEMORY;
1388 return -1;
1389 }
1390 /* Extract :method, :path from request line */
1391 end = strchr(hdbuf, ' ');
1392 if(!end)
1393 goto fail;
1394 nva[0].name = (unsigned char *)":method";
1395 nva[0].namelen = (uint16_t)strlen((char *)nva[0].name);
1396 nva[0].value = (unsigned char *)hdbuf;
1397 nva[0].valuelen = (uint16_t)(end - hdbuf);
1398 nva[0].flags = NGHTTP2_NV_FLAG_NONE;
1399
1400 hdbuf = end + 1;
1401
1402 end = strchr(hdbuf, ' ');
1403 if(!end)
1404 goto fail;
1405 nva[1].name = (unsigned char *)":path";
1406 nva[1].namelen = (uint16_t)strlen((char *)nva[1].name);
1407 nva[1].value = (unsigned char *)hdbuf;
1408 nva[1].valuelen = (uint16_t)(end - hdbuf);
1409 nva[1].flags = NGHTTP2_NV_FLAG_NONE;
1410
1411 nva[2].name = (unsigned char *)":scheme";
1412 nva[2].namelen = (uint16_t)strlen((char *)nva[2].name);
1413 if(conn->handler->flags & PROTOPT_SSL)
1414 nva[2].value = (unsigned char *)"https";
1415 else
1416 nva[2].value = (unsigned char *)"http";
1417 nva[2].valuelen = (uint16_t)strlen((char *)nva[2].value);
1418 nva[2].flags = NGHTTP2_NV_FLAG_NONE;
1419
1420 hdbuf = strchr(hdbuf, 0x0a);
1421 if(!hdbuf)
1422 goto fail;
1423 ++hdbuf;
1424
1425 authority_idx = 0;
1426
1427 i = 3;
1428 while(i < nheader) {
1429 size_t hlen;
1430 int skip = 0;
1431 end = strchr(hdbuf, ':');
1432 if(!end)
1433 goto fail;
1434 hlen = end - hdbuf;
1435 if(hlen == 10 && Curl_raw_nequal("connection", hdbuf, 10)) {
1436 /* skip Connection: headers! */
1437 skip = 1;
1438 --nheader;
1439 }
1440 else if(hlen == 4 && Curl_raw_nequal("host", hdbuf, 4)) {
1441 authority_idx = i;
1442 nva[i].name = (unsigned char *)":authority";
1443 nva[i].namelen = (uint16_t)strlen((char *)nva[i].name);
1444 }
1445 else {
1446 nva[i].name = (unsigned char *)hdbuf;
1447 nva[i].namelen = (uint16_t)(end - hdbuf);
1448 }
1449 hdbuf = end + 1;
1450 for(; *hdbuf == ' '; ++hdbuf);
1451 end = strchr(hdbuf, 0x0d);
1452 if(!end)
1453 goto fail;
1454 if(!skip) {
1455 nva[i].value = (unsigned char *)hdbuf;
1456 nva[i].valuelen = (uint16_t)(end - hdbuf);
1457 nva[i].flags = NGHTTP2_NV_FLAG_NONE;
1458 /* Inspect Content-Length header field and retrieve the request
1459 entity length so that we can set END_STREAM to the last DATA
1460 frame. */
1461 if(nva[i].namelen == 14 &&
1462 Curl_raw_nequal("content-length", (char*)nva[i].name, 14)) {
1463 size_t j;
1464 stream->upload_left = 0;
1465 for(j = 0; j < nva[i].valuelen; ++j) {
1466 stream->upload_left *= 10;
1467 stream->upload_left += nva[i].value[j] - '0';
1468 }
1469 DEBUGF(infof(conn->data,
1470 "request content-length=%"
1471 CURL_FORMAT_CURL_OFF_T
1472 "\n", stream->upload_left));
1473 }
1474 ++i;
1475 }
1476 hdbuf = end + 2;
1477 }
1478
1479 /* :authority must come before non-pseudo header fields */
1480 if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) {
1481 nghttp2_nv authority = nva[authority_idx];
1482 for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
1483 nva[i] = nva[i - 1];
1484 }
1485 nva[i] = authority;
1486 }
1487
1488 h2_pri_spec(conn->data, &pri_spec);
1489
1490 switch(conn->data->set.httpreq) {
1491 case HTTPREQ_POST:
1492 case HTTPREQ_POST_FORM:
1493 case HTTPREQ_PUT:
1494 data_prd.read_callback = data_source_read_callback;
1495 data_prd.source.ptr = NULL;
1496 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
1497 &data_prd, conn->data);
1498 break;
1499 default:
1500 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
1501 NULL, conn->data);
1502 }
1503
1504 Curl_safefree(nva);
1505
1506 if(stream_id < 0) {
1507 DEBUGF(infof(conn->data, "http2_send() send error\n"));
1508 *err = CURLE_SEND_ERROR;
1509 return -1;
1510 }
1511
1512 infof(conn->data, "Using Stream ID: %x (easy handle %p)\n",
1513 stream_id, conn->data);
1514 stream->stream_id = stream_id;
1515
1516 /* this does not call h2_session_send() since there can not have been any
1517 * priority upodate since the nghttp2_submit_request() call above */
1518 rv = nghttp2_session_send(h2);
1519
1520 if(rv != 0) {
1521 *err = CURLE_SEND_ERROR;
1522 return -1;
1523 }
1524
1525 if(stream->stream_id != -1) {
1526 /* If whole HEADERS frame was sent off to the underlying socket,
1527 the nghttp2 library calls data_source_read_callback. But only
1528 it found that no data available, so it deferred the DATA
1529 transmission. Which means that nghttp2_session_want_write()
1530 returns 0 on http2_perform_getsock(), which results that no
1531 writable socket check is performed. To workaround this, we
1532 issue nghttp2_session_resume_data() here to bring back DATA
1533 transmission from deferred state. */
1534 nghttp2_session_resume_data(h2, stream->stream_id);
1535 }
1536
1537 return len;
1538
1539fail:
1540 free(nva);
1541 *err = CURLE_SEND_ERROR;
1542 return -1;
1543}
1544
1545CURLcode Curl_http2_setup(struct connectdata *conn)
1546{
1547 CURLcode result;
1548 struct http_conn *httpc = &conn->proto.httpc;
1549 struct HTTP *stream = conn->data->req.protop;
1550
1551 stream->stream_id = -1;
1552
1553 if(!stream->header_recvbuf)
1554 stream->header_recvbuf = Curl_add_buffer_init();
1555
1556 if((conn->handler == &Curl_handler_http2_ssl) ||
1557 (conn->handler == &Curl_handler_http2))
1558 return CURLE_OK; /* already done */
1559
1560 if(conn->handler->flags & PROTOPT_SSL)
1561 conn->handler = &Curl_handler_http2_ssl;
1562 else
1563 conn->handler = &Curl_handler_http2;
1564
1565 result = Curl_http2_init(conn);
1566 if(result)
1567 return result;
1568
1569 infof(conn->data, "Using HTTP2, server supports multi-use\n");
1570 stream->upload_left = 0;
1571 stream->upload_mem = NULL;
1572 stream->upload_len = 0;
1573
1574 httpc->inbuflen = 0;
1575 httpc->nread_inbuf = 0;
1576
1577 httpc->pause_stream_id = 0;
1578
1579 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
1580 conn->httpversion = 20;
1581 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
1582
1583 infof(conn->data, "Connection state changed (HTTP/2 confirmed)\n");
1584 Curl_multi_connchanged(conn->data->multi);
1585
1586 /* switch on TCP_NODELAY as we need to send off packets without delay for
1587 maximum throughput */
1588 Curl_tcpnodelay(conn, conn->sock[FIRSTSOCKET]);
1589
1590 return CURLE_OK;
1591}
1592
1593CURLcode Curl_http2_switched(struct connectdata *conn,
1594 const char *mem, size_t nread)
1595{
1596 CURLcode result;
1597 struct http_conn *httpc = &conn->proto.httpc;
1598 int rv;
1599 ssize_t nproc;
1600 struct SessionHandle *data = conn->data;
1601 struct HTTP *stream = conn->data->req.protop;
1602
1603 result = Curl_http2_setup(conn);
1604 if(result)
1605 return result;
1606
1607 httpc->recv_underlying = (recving)conn->recv[FIRSTSOCKET];
1608 httpc->send_underlying = (sending)conn->send[FIRSTSOCKET];
1609 conn->recv[FIRSTSOCKET] = http2_recv;
1610 conn->send[FIRSTSOCKET] = http2_send;
1611
1612 if(conn->data->req.upgr101 == UPGR101_RECEIVED) {
1613 /* stream 1 is opened implicitly on upgrade */
1614 stream->stream_id = 1;
1615 /* queue SETTINGS frame (again) */
1616 rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings,
1617 httpc->binlen, NULL);
1618 if(rv != 0) {
1619 failf(data, "nghttp2_session_upgrade() failed: %s(%d)",
1620 nghttp2_strerror(rv), rv);
1621 return CURLE_HTTP2;
1622 }
1623
1624 nghttp2_session_set_stream_user_data(httpc->h2,
1625 stream->stream_id,
1626 conn->data);
1627 }
1628 else {
1629 /* stream ID is unknown at this point */
1630 stream->stream_id = -1;
1631 rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE, NULL, 0);
1632 if(rv != 0) {
1633 failf(data, "nghttp2_submit_settings() failed: %s(%d)",
1634 nghttp2_strerror(rv), rv);
1635 return CURLE_HTTP2;
1636 }
1637 }
1638
1639 /* we are going to copy mem to httpc->inbuf. This is required since
1640 mem is part of buffer pointed by stream->mem, and callbacks
1641 called by nghttp2_session_mem_recv() will write stream specific
1642 data into stream->mem, overwriting data already there. */
1643 if(H2_BUFSIZE < nread) {
1644 failf(data, "connection buffer size is too small to store data following "
1645 "HTTP Upgrade response header: buflen=%zu, datalen=%zu",
1646 H2_BUFSIZE, nread);
1647 return CURLE_HTTP2;
1648 }
1649
1650 infof(conn->data, "Copying HTTP/2 data in stream buffer to connection buffer"
1651 " after upgrade: len=%zu\n",
1652 nread);
1653
1654 memcpy(httpc->inbuf, mem, nread);
1655 httpc->inbuflen = nread;
1656
1657 nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf,
1658 httpc->inbuflen);
1659
1660 if(nghttp2_is_fatal((int)nproc)) {
1661 failf(data, "nghttp2_session_mem_recv() failed: %s(%d)",
1662 nghttp2_strerror((int)nproc), (int)nproc);
1663 return CURLE_HTTP2;
1664 }
1665
1666 DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", nproc));
1667
1668 if((ssize_t)nread == nproc) {
1669 httpc->inbuflen = 0;
1670 httpc->nread_inbuf = 0;
1671 }
1672 else {
1673 httpc->nread_inbuf += nproc;
1674 }
1675
1676 /* Try to send some frames since we may read SETTINGS already. */
1677 rv = h2_session_send(data, httpc->h2);
1678
1679 if(rv != 0) {
1680 failf(data, "nghttp2_session_send() failed: %s(%d)",
1681 nghttp2_strerror(rv), rv);
1682 return CURLE_HTTP2;
1683 }
1684
1685 return CURLE_OK;
1686}
1687
1688#else /* !USE_NGHTTP2 */
1689
1690/* Satisfy external references even if http2 is not compiled in. */
1691
1692#define CURL_DISABLE_TYPECHECK
1693#include <curl/curl.h>
1694
1695char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
1696{
1697 (void) h;
1698 (void) num;
1699 return NULL;
1700}
1701
1702char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
1703{
1704 (void) h;
1705 (void) header;
1706 return NULL;
1707}
1708
1709#endif /* USE_NGHTTP2 */
Note: See TracBrowser for help on using the repository browser.