VirtualBox

source: vbox/trunk/src/libs/curl-7.64.0/lib/http2.c@ 86643

Last change on this file since 86643 was 85671, checked in by vboxsync, 4 years ago

Export out internal curl copy to make it a lot simpler to build VBox (OSE) on Windows. bugref:9814

  • Property svn:eol-style set to native
File size: 72.8 KB
Line 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2019, Daniel Stenberg, <[email protected]>, 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 <nghttp2/nghttp2.h>
27#include "urldata.h"
28#include "http2.h"
29#include "http.h"
30#include "sendf.h"
31#include "select.h"
32#include "curl_base64.h"
33#include "strcase.h"
34#include "multiif.h"
35#include "url.h"
36#include "connect.h"
37#include "strtoofft.h"
38#include "strdup.h"
39/* The last 3 #include files should be in this order */
40#include "curl_printf.h"
41#include "curl_memory.h"
42#include "memdebug.h"
43
44#define H2_BUFSIZE 32768
45
46#if (NGHTTP2_VERSION_NUM < 0x010000)
47#error too old nghttp2 version, upgrade!
48#endif
49
50#if (NGHTTP2_VERSION_NUM > 0x010800)
51#define NGHTTP2_HAS_HTTP2_STRERROR 1
52#endif
53
54#if (NGHTTP2_VERSION_NUM >= 0x010900)
55/* nghttp2_session_callbacks_set_error_callback is present in nghttp2 1.9.0 or
56 later */
57#define NGHTTP2_HAS_ERROR_CALLBACK 1
58#else
59#define nghttp2_session_callbacks_set_error_callback(x,y)
60#endif
61
62#if (NGHTTP2_VERSION_NUM >= 0x010c00)
63#define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1
64#endif
65
66#define HTTP2_HUGE_WINDOW_SIZE (1 << 30)
67
68#ifdef DEBUG_HTTP2
69#define H2BUGF(x) x
70#else
71#define H2BUGF(x) do { } WHILE_FALSE
72#endif
73
74
75static ssize_t http2_recv(struct connectdata *conn, int sockindex,
76 char *mem, size_t len, CURLcode *err);
77static bool http2_connisdead(struct connectdata *conn);
78static int h2_session_send(struct Curl_easy *data,
79 nghttp2_session *h2);
80static int h2_process_pending_input(struct connectdata *conn,
81 struct http_conn *httpc,
82 CURLcode *err);
83
84/*
85 * Curl_http2_init_state() is called when the easy handle is created and
86 * allows for HTTP/2 specific init of state.
87 */
88void Curl_http2_init_state(struct UrlState *state)
89{
90 state->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
91}
92
93/*
94 * Curl_http2_init_userset() is called when the easy handle is created and
95 * allows for HTTP/2 specific user-set fields.
96 */
97void Curl_http2_init_userset(struct UserDefined *set)
98{
99 set->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
100}
101
102static int http2_perform_getsock(const struct connectdata *conn,
103 curl_socket_t *sock, /* points to
104 numsocks
105 number of
106 sockets */
107 int numsocks)
108{
109 const struct http_conn *c = &conn->proto.httpc;
110 struct SingleRequest *k = &conn->data->req;
111 int bitmap = GETSOCK_BLANK;
112 (void)numsocks;
113
114 /* TODO We should check underlying socket state if it is SSL socket
115 because of renegotiation. */
116 sock[0] = conn->sock[FIRSTSOCKET];
117
118 /* in a HTTP/2 connection we can basically always get a frame so we should
119 always be ready for one */
120 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
121
122 /* we're still uploading or the HTTP/2 layer wants to send data */
123 if(((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) ||
124 nghttp2_session_want_write(c->h2))
125 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
126
127 return bitmap;
128}
129
130static int http2_getsock(struct connectdata *conn,
131 curl_socket_t *sock, /* points to numsocks
132 number of sockets */
133 int numsocks)
134{
135 return http2_perform_getsock(conn, sock, numsocks);
136}
137
138/*
139 * http2_stream_free() free HTTP2 stream related data
140 */
141static void http2_stream_free(struct HTTP *http)
142{
143 if(http) {
144 Curl_add_buffer_free(&http->header_recvbuf);
145 Curl_add_buffer_free(&http->trailer_recvbuf);
146 for(; http->push_headers_used > 0; --http->push_headers_used) {
147 free(http->push_headers[http->push_headers_used - 1]);
148 }
149 free(http->push_headers);
150 http->push_headers = NULL;
151 }
152}
153
154/*
155 * Disconnects *a* connection used for HTTP/2. It might be an old one from the
156 * connection cache and not the "main" one. Don't touch the easy handle!
157 */
158
159static CURLcode http2_disconnect(struct connectdata *conn,
160 bool dead_connection)
161{
162 struct http_conn *c = &conn->proto.httpc;
163 (void)dead_connection;
164
165 H2BUGF(infof(conn->data, "HTTP/2 DISCONNECT starts now\n"));
166
167 nghttp2_session_del(c->h2);
168 Curl_safefree(c->inbuf);
169
170 H2BUGF(infof(conn->data, "HTTP/2 DISCONNECT done\n"));
171
172 return CURLE_OK;
173}
174
175/*
176 * The server may send us data at any point (e.g. PING frames). Therefore,
177 * we cannot assume that an HTTP/2 socket is dead just because it is readable.
178 *
179 * Instead, if it is readable, run Curl_connalive() to peek at the socket
180 * and distinguish between closed and data.
181 */
182static bool http2_connisdead(struct connectdata *conn)
183{
184 int sval;
185 bool dead = TRUE;
186
187 if(conn->bits.close)
188 return TRUE;
189
190 sval = SOCKET_READABLE(conn->sock[FIRSTSOCKET], 0);
191 if(sval == 0) {
192 /* timeout */
193 dead = FALSE;
194 }
195 else if(sval & CURL_CSELECT_ERR) {
196 /* socket is in an error state */
197 dead = TRUE;
198 }
199 else if(sval & CURL_CSELECT_IN) {
200 /* readable with no error. could still be closed */
201 dead = !Curl_connalive(conn);
202 if(!dead) {
203 /* This happens before we've sent off a request and the connection is
204 not in use by any other transfer, there shouldn't be any data here,
205 only "protocol frames" */
206 CURLcode result;
207 struct http_conn *httpc = &conn->proto.httpc;
208 ssize_t nread = -1;
209 if(httpc->recv_underlying)
210 /* if called "too early", this pointer isn't setup yet! */
211 nread = ((Curl_recv *)httpc->recv_underlying)(
212 conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
213 if(nread != -1) {
214 infof(conn->data,
215 "%d bytes stray data read before trying h2 connection\n",
216 (int)nread);
217 httpc->nread_inbuf = 0;
218 httpc->inbuflen = nread;
219 (void)h2_process_pending_input(conn, httpc, &result);
220 }
221 else
222 /* the read failed so let's say this is dead anyway */
223 dead = TRUE;
224 }
225 }
226
227 return dead;
228}
229
230static unsigned int http2_conncheck(struct connectdata *check,
231 unsigned int checks_to_perform)
232{
233 unsigned int ret_val = CONNRESULT_NONE;
234 struct http_conn *c = &check->proto.httpc;
235 int rc;
236 bool send_frames = false;
237
238 if(checks_to_perform & CONNCHECK_ISDEAD) {
239 if(http2_connisdead(check))
240 ret_val |= CONNRESULT_DEAD;
241 }
242
243 if(checks_to_perform & CONNCHECK_KEEPALIVE) {
244 struct curltime now = Curl_now();
245 time_t elapsed = Curl_timediff(now, check->keepalive);
246
247 if(elapsed > check->upkeep_interval_ms) {
248 /* Perform an HTTP/2 PING */
249 rc = nghttp2_submit_ping(c->h2, 0, ZERO_NULL);
250 if(!rc) {
251 /* Successfully added a PING frame to the session. Need to flag this
252 so the frame is sent. */
253 send_frames = true;
254 }
255 else {
256 failf(check->data, "nghttp2_submit_ping() failed: %s(%d)",
257 nghttp2_strerror(rc), rc);
258 }
259
260 check->keepalive = now;
261 }
262 }
263
264 if(send_frames) {
265 rc = nghttp2_session_send(c->h2);
266 if(rc)
267 failf(check->data, "nghttp2_session_send() failed: %s(%d)",
268 nghttp2_strerror(rc), rc);
269 }
270
271 return ret_val;
272}
273
274/* called from Curl_http_setup_conn */
275void Curl_http2_setup_req(struct Curl_easy *data)
276{
277 struct HTTP *http = data->req.protop;
278
279 http->nread_header_recvbuf = 0;
280 http->bodystarted = FALSE;
281 http->status_code = -1;
282 http->pausedata = NULL;
283 http->pauselen = 0;
284 http->closed = FALSE;
285 http->close_handled = FALSE;
286 http->mem = data->state.buffer;
287 http->len = data->set.buffer_size;
288 http->memlen = 0;
289}
290
291/* called from Curl_http_setup_conn */
292void Curl_http2_setup_conn(struct connectdata *conn)
293{
294 conn->proto.httpc.settings.max_concurrent_streams =
295 DEFAULT_MAX_CONCURRENT_STREAMS;
296 conn->proto.httpc.error_code = NGHTTP2_NO_ERROR;
297}
298
299/*
300 * HTTP2 handler interface. This isn't added to the general list of protocols
301 * but will be used at run-time when the protocol is dynamically switched from
302 * HTTP to HTTP2.
303 */
304static const struct Curl_handler Curl_handler_http2 = {
305 "HTTP", /* scheme */
306 ZERO_NULL, /* setup_connection */
307 Curl_http, /* do_it */
308 Curl_http_done, /* done */
309 ZERO_NULL, /* do_more */
310 ZERO_NULL, /* connect_it */
311 ZERO_NULL, /* connecting */
312 ZERO_NULL, /* doing */
313 http2_getsock, /* proto_getsock */
314 http2_getsock, /* doing_getsock */
315 ZERO_NULL, /* domore_getsock */
316 http2_perform_getsock, /* perform_getsock */
317 http2_disconnect, /* disconnect */
318 ZERO_NULL, /* readwrite */
319 http2_conncheck, /* connection_check */
320 PORT_HTTP, /* defport */
321 CURLPROTO_HTTP, /* protocol */
322 PROTOPT_STREAM /* flags */
323};
324
325static const struct Curl_handler Curl_handler_http2_ssl = {
326 "HTTPS", /* scheme */
327 ZERO_NULL, /* setup_connection */
328 Curl_http, /* do_it */
329 Curl_http_done, /* done */
330 ZERO_NULL, /* do_more */
331 ZERO_NULL, /* connect_it */
332 ZERO_NULL, /* connecting */
333 ZERO_NULL, /* doing */
334 http2_getsock, /* proto_getsock */
335 http2_getsock, /* doing_getsock */
336 ZERO_NULL, /* domore_getsock */
337 http2_perform_getsock, /* perform_getsock */
338 http2_disconnect, /* disconnect */
339 ZERO_NULL, /* readwrite */
340 http2_conncheck, /* connection_check */
341 PORT_HTTP, /* defport */
342 CURLPROTO_HTTPS, /* protocol */
343 PROTOPT_SSL | PROTOPT_STREAM /* flags */
344};
345
346/*
347 * Store nghttp2 version info in this buffer, Prefix with a space. Return
348 * total length written.
349 */
350int Curl_http2_ver(char *p, size_t len)
351{
352 nghttp2_info *h2 = nghttp2_version(0);
353 return msnprintf(p, len, " nghttp2/%s", h2->version_str);
354}
355
356/* HTTP/2 error code to name based on the Error Code Registry.
357https://tools.ietf.org/html/rfc7540#page-77
358nghttp2_error_code enums are identical.
359*/
360const char *Curl_http2_strerror(uint32_t err)
361{
362#ifndef NGHTTP2_HAS_HTTP2_STRERROR
363 const char *str[] = {
364 "NO_ERROR", /* 0x0 */
365 "PROTOCOL_ERROR", /* 0x1 */
366 "INTERNAL_ERROR", /* 0x2 */
367 "FLOW_CONTROL_ERROR", /* 0x3 */
368 "SETTINGS_TIMEOUT", /* 0x4 */
369 "STREAM_CLOSED", /* 0x5 */
370 "FRAME_SIZE_ERROR", /* 0x6 */
371 "REFUSED_STREAM", /* 0x7 */
372 "CANCEL", /* 0x8 */
373 "COMPRESSION_ERROR", /* 0x9 */
374 "CONNECT_ERROR", /* 0xA */
375 "ENHANCE_YOUR_CALM", /* 0xB */
376 "INADEQUATE_SECURITY", /* 0xC */
377 "HTTP_1_1_REQUIRED" /* 0xD */
378 };
379 return (err < sizeof(str) / sizeof(str[0])) ? str[err] : "unknown";
380#else
381 return nghttp2_http2_strerror(err);
382#endif
383}
384
385/*
386 * The implementation of nghttp2_send_callback type. Here we write |data| with
387 * size |length| to the network and return the number of bytes actually
388 * written. See the documentation of nghttp2_send_callback for the details.
389 */
390static ssize_t send_callback(nghttp2_session *h2,
391 const uint8_t *data, size_t length, int flags,
392 void *userp)
393{
394 struct connectdata *conn = (struct connectdata *)userp;
395 struct http_conn *c = &conn->proto.httpc;
396 ssize_t written;
397 CURLcode result = CURLE_OK;
398
399 (void)h2;
400 (void)flags;
401
402 if(!c->send_underlying)
403 /* called before setup properly! */
404 return NGHTTP2_ERR_CALLBACK_FAILURE;
405
406 written = ((Curl_send*)c->send_underlying)(conn, FIRSTSOCKET,
407 data, length, &result);
408
409 if(result == CURLE_AGAIN) {
410 return NGHTTP2_ERR_WOULDBLOCK;
411 }
412
413 if(written == -1) {
414 failf(conn->data, "Failed sending HTTP2 data");
415 return NGHTTP2_ERR_CALLBACK_FAILURE;
416 }
417
418 if(!written)
419 return NGHTTP2_ERR_WOULDBLOCK;
420
421 return written;
422}
423
424
425/* We pass a pointer to this struct in the push callback, but the contents of
426 the struct are hidden from the user. */
427struct curl_pushheaders {
428 struct Curl_easy *data;
429 const nghttp2_push_promise *frame;
430};
431
432/*
433 * push header access function. Only to be used from within the push callback
434 */
435char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
436{
437 /* Verify that we got a good easy handle in the push header struct, mostly to
438 detect rubbish input fast(er). */
439 if(!h || !GOOD_EASY_HANDLE(h->data))
440 return NULL;
441 else {
442 struct HTTP *stream = h->data->req.protop;
443 if(num < stream->push_headers_used)
444 return stream->push_headers[num];
445 }
446 return NULL;
447}
448
449/*
450 * push header access function. Only to be used from within the push callback
451 */
452char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
453{
454 /* Verify that we got a good easy handle in the push header struct,
455 mostly to detect rubbish input fast(er). Also empty header name
456 is just a rubbish too. We have to allow ":" at the beginning of
457 the header, but header == ":" must be rejected. If we have ':' in
458 the middle of header, it could be matched in middle of the value,
459 this is because we do prefix match.*/
460 if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] ||
461 !strcmp(header, ":") || strchr(header + 1, ':'))
462 return NULL;
463 else {
464 struct HTTP *stream = h->data->req.protop;
465 size_t len = strlen(header);
466 size_t i;
467 for(i = 0; i<stream->push_headers_used; i++) {
468 if(!strncmp(header, stream->push_headers[i], len)) {
469 /* sub-match, make sure that it is followed by a colon */
470 if(stream->push_headers[i][len] != ':')
471 continue;
472 return &stream->push_headers[i][len + 1];
473 }
474 }
475 }
476 return NULL;
477}
478
479/*
480 * This specific transfer on this connection has been "drained".
481 */
482static void drained_transfer(struct Curl_easy *data,
483 struct http_conn *httpc)
484{
485 DEBUGASSERT(httpc->drain_total >= data->state.drain);
486 httpc->drain_total -= data->state.drain;
487 data->state.drain = 0;
488}
489
490/*
491 * Mark this transfer to get "drained".
492 */
493static void drain_this(struct Curl_easy *data,
494 struct http_conn *httpc)
495{
496 data->state.drain++;
497 httpc->drain_total++;
498 DEBUGASSERT(httpc->drain_total >= data->state.drain);
499}
500
501static struct Curl_easy *duphandle(struct Curl_easy *data)
502{
503 struct Curl_easy *second = curl_easy_duphandle(data);
504 if(second) {
505 /* setup the request struct */
506 struct HTTP *http = calloc(1, sizeof(struct HTTP));
507 if(!http) {
508 (void)Curl_close(second);
509 second = NULL;
510 }
511 else {
512 second->req.protop = http;
513 http->header_recvbuf = Curl_add_buffer_init();
514 if(!http->header_recvbuf) {
515 free(http);
516 (void)Curl_close(second);
517 second = NULL;
518 }
519 else {
520 Curl_http2_setup_req(second);
521 second->state.stream_weight = data->state.stream_weight;
522 }
523 }
524 }
525 return second;
526}
527
528
529static int push_promise(struct Curl_easy *data,
530 struct connectdata *conn,
531 const nghttp2_push_promise *frame)
532{
533 int rv;
534 H2BUGF(infof(data, "PUSH_PROMISE received, stream %u!\n",
535 frame->promised_stream_id));
536 if(data->multi->push_cb) {
537 struct HTTP *stream;
538 struct HTTP *newstream;
539 struct curl_pushheaders heads;
540 CURLMcode rc;
541 struct http_conn *httpc;
542 size_t i;
543 /* clone the parent */
544 struct Curl_easy *newhandle = duphandle(data);
545 if(!newhandle) {
546 infof(data, "failed to duplicate handle\n");
547 rv = 1; /* FAIL HARD */
548 goto fail;
549 }
550
551 heads.data = data;
552 heads.frame = frame;
553 /* ask the application */
554 H2BUGF(infof(data, "Got PUSH_PROMISE, ask application!\n"));
555
556 stream = data->req.protop;
557 if(!stream) {
558 failf(data, "Internal NULL stream!\n");
559 (void)Curl_close(newhandle);
560 rv = 1;
561 goto fail;
562 }
563
564 Curl_set_in_callback(data, true);
565 rv = data->multi->push_cb(data, newhandle,
566 stream->push_headers_used, &heads,
567 data->multi->push_userp);
568 Curl_set_in_callback(data, false);
569
570 /* free the headers again */
571 for(i = 0; i<stream->push_headers_used; i++)
572 free(stream->push_headers[i]);
573 free(stream->push_headers);
574 stream->push_headers = NULL;
575 stream->push_headers_used = 0;
576
577 if(rv) {
578 /* denied, kill off the new handle again */
579 http2_stream_free(newhandle->req.protop);
580 newhandle->req.protop = NULL;
581 (void)Curl_close(newhandle);
582 goto fail;
583 }
584
585 newstream = newhandle->req.protop;
586 newstream->stream_id = frame->promised_stream_id;
587 newhandle->req.maxdownload = -1;
588 newhandle->req.size = -1;
589
590 /* approved, add to the multi handle and immediately switch to PERFORM
591 state with the given connection !*/
592 rc = Curl_multi_add_perform(data->multi, newhandle, conn);
593 if(rc) {
594 infof(data, "failed to add handle to multi\n");
595 http2_stream_free(newhandle->req.protop);
596 newhandle->req.protop = NULL;
597 Curl_close(newhandle);
598 rv = 1;
599 goto fail;
600 }
601
602 httpc = &conn->proto.httpc;
603 rv = nghttp2_session_set_stream_user_data(httpc->h2,
604 frame->promised_stream_id,
605 newhandle);
606 if(rv) {
607 infof(data, "failed to set user_data for stream %d\n",
608 frame->promised_stream_id);
609 DEBUGASSERT(0);
610 goto fail;
611 }
612 }
613 else {
614 H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n"));
615 rv = 1;
616 }
617 fail:
618 return rv;
619}
620
621static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
622 void *userp)
623{
624 struct connectdata *conn = (struct connectdata *)userp;
625 struct http_conn *httpc = &conn->proto.httpc;
626 struct Curl_easy *data_s = NULL;
627 struct HTTP *stream = NULL;
628 int rv;
629 size_t left, ncopy;
630 int32_t stream_id = frame->hd.stream_id;
631 CURLcode result;
632
633 if(!stream_id) {
634 /* stream ID zero is for connection-oriented stuff */
635 if(frame->hd.type == NGHTTP2_SETTINGS) {
636 uint32_t max_conn = httpc->settings.max_concurrent_streams;
637 H2BUGF(infof(conn->data, "Got SETTINGS\n"));
638 httpc->settings.max_concurrent_streams =
639 nghttp2_session_get_remote_settings(
640 session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
641 httpc->settings.enable_push =
642 nghttp2_session_get_remote_settings(
643 session, NGHTTP2_SETTINGS_ENABLE_PUSH);
644 H2BUGF(infof(conn->data, "MAX_CONCURRENT_STREAMS == %d\n",
645 httpc->settings.max_concurrent_streams));
646 H2BUGF(infof(conn->data, "ENABLE_PUSH == %s\n",
647 httpc->settings.enable_push?"TRUE":"false"));
648 if(max_conn != httpc->settings.max_concurrent_streams) {
649 /* only signal change if the value actually changed */
650 infof(conn->data,
651 "Connection state changed (MAX_CONCURRENT_STREAMS == %u)!\n",
652 httpc->settings.max_concurrent_streams);
653 Curl_multi_connchanged(conn->data->multi);
654 }
655 }
656 return 0;
657 }
658 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
659 if(!data_s) {
660 H2BUGF(infof(conn->data,
661 "No Curl_easy associated with stream: %x\n",
662 stream_id));
663 return 0;
664 }
665
666 stream = data_s->req.protop;
667 if(!stream) {
668 H2BUGF(infof(data_s, "No proto pointer for stream: %x\n",
669 stream_id));
670 return NGHTTP2_ERR_CALLBACK_FAILURE;
671 }
672
673 H2BUGF(infof(data_s, "on_frame_recv() header %x stream %x\n",
674 frame->hd.type, stream_id));
675
676 switch(frame->hd.type) {
677 case NGHTTP2_DATA:
678 /* If body started on this stream, then receiving DATA is illegal. */
679 if(!stream->bodystarted) {
680 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
681 stream_id, NGHTTP2_PROTOCOL_ERROR);
682
683 if(nghttp2_is_fatal(rv)) {
684 return NGHTTP2_ERR_CALLBACK_FAILURE;
685 }
686 }
687 break;
688 case NGHTTP2_HEADERS:
689 if(stream->bodystarted) {
690 /* Only valid HEADERS after body started is trailer HEADERS. We
691 buffer them in on_header callback. */
692 break;
693 }
694
695 /* nghttp2 guarantees that :status is received, and we store it to
696 stream->status_code. Fuzzing has proven this can still be reached
697 without status code having been set. */
698 if(stream->status_code == -1)
699 return NGHTTP2_ERR_CALLBACK_FAILURE;
700
701 /* Only final status code signals the end of header */
702 if(stream->status_code / 100 != 1) {
703 stream->bodystarted = TRUE;
704 stream->status_code = -1;
705 }
706
707 result = Curl_add_buffer(&stream->header_recvbuf, "\r\n", 2);
708 if(result)
709 return NGHTTP2_ERR_CALLBACK_FAILURE;
710
711 left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
712 ncopy = CURLMIN(stream->len, left);
713
714 memcpy(&stream->mem[stream->memlen],
715 stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
716 ncopy);
717 stream->nread_header_recvbuf += ncopy;
718
719 H2BUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n",
720 ncopy, stream_id, stream->mem));
721
722 stream->len -= ncopy;
723 stream->memlen += ncopy;
724
725 drain_this(data_s, httpc);
726 {
727 /* get the pointer from userp again since it was re-assigned above */
728 struct connectdata *conn_s = (struct connectdata *)userp;
729
730 /* if we receive data for another handle, wake that up */
731 if(conn_s->data != data_s)
732 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
733 }
734 break;
735 case NGHTTP2_PUSH_PROMISE:
736 rv = push_promise(data_s, conn, &frame->push_promise);
737 if(rv) { /* deny! */
738 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
739 frame->push_promise.promised_stream_id,
740 NGHTTP2_CANCEL);
741 if(nghttp2_is_fatal(rv)) {
742 return rv;
743 }
744 }
745 break;
746 default:
747 H2BUGF(infof(data_s, "Got frame type %x for stream %u!\n",
748 frame->hd.type, stream_id));
749 break;
750 }
751 return 0;
752}
753
754static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
755 int32_t stream_id,
756 const uint8_t *data, size_t len, void *userp)
757{
758 struct HTTP *stream;
759 struct Curl_easy *data_s;
760 size_t nread;
761 struct connectdata *conn = (struct connectdata *)userp;
762 (void)session;
763 (void)flags;
764 (void)data;
765
766 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
767
768 /* get the stream from the hash based on Stream ID */
769 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
770 if(!data_s)
771 /* Receiving a Stream ID not in the hash should not happen, this is an
772 internal error more than anything else! */
773 return NGHTTP2_ERR_CALLBACK_FAILURE;
774
775 stream = data_s->req.protop;
776 if(!stream)
777 return NGHTTP2_ERR_CALLBACK_FAILURE;
778
779 nread = CURLMIN(stream->len, len);
780 memcpy(&stream->mem[stream->memlen], data, nread);
781
782 stream->len -= nread;
783 stream->memlen += nread;
784
785 drain_this(data_s, &conn->proto.httpc);
786
787 /* if we receive data for another handle, wake that up */
788 if(conn->data != data_s)
789 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
790
791 H2BUGF(infof(data_s, "%zu data received for stream %u "
792 "(%zu left in buffer %p, total %zu)\n",
793 nread, stream_id,
794 stream->len, stream->mem,
795 stream->memlen));
796
797 if(nread < len) {
798 stream->pausedata = data + nread;
799 stream->pauselen = len - nread;
800 H2BUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
801 ", stream %u\n",
802 len - nread, stream_id));
803 data_s->conn->proto.httpc.pause_stream_id = stream_id;
804
805 return NGHTTP2_ERR_PAUSE;
806 }
807
808 /* pause execution of nghttp2 if we received data for another handle
809 in order to process them first. */
810 if(conn->data != data_s) {
811 data_s->conn->proto.httpc.pause_stream_id = stream_id;
812
813 return NGHTTP2_ERR_PAUSE;
814 }
815
816 return 0;
817}
818
819static int on_stream_close(nghttp2_session *session, int32_t stream_id,
820 uint32_t error_code, void *userp)
821{
822 struct Curl_easy *data_s;
823 struct HTTP *stream;
824 struct connectdata *conn = (struct connectdata *)userp;
825 int rv;
826 (void)session;
827 (void)stream_id;
828
829 if(stream_id) {
830 struct http_conn *httpc;
831 /* get the stream from the hash based on Stream ID, stream ID zero is for
832 connection-oriented stuff */
833 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
834 if(!data_s) {
835 /* We could get stream ID not in the hash. For example, if we
836 decided to reject stream (e.g., PUSH_PROMISE). */
837 return 0;
838 }
839 H2BUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u\n",
840 Curl_http2_strerror(error_code), error_code, stream_id));
841 stream = data_s->req.protop;
842 if(!stream)
843 return NGHTTP2_ERR_CALLBACK_FAILURE;
844
845 stream->closed = TRUE;
846 httpc = &conn->proto.httpc;
847 drain_this(data_s, httpc);
848 httpc->error_code = error_code;
849
850 /* remove the entry from the hash as the stream is now gone */
851 rv = nghttp2_session_set_stream_user_data(session, stream_id, 0);
852 if(rv) {
853 infof(data_s, "http/2: failed to clear user_data for stream %d!\n",
854 stream_id);
855 DEBUGASSERT(0);
856 }
857 if(stream_id == httpc->pause_stream_id) {
858 H2BUGF(infof(data_s, "Stopped the pause stream!\n"));
859 httpc->pause_stream_id = 0;
860 }
861 H2BUGF(infof(data_s, "Removed stream %u hash!\n", stream_id));
862 stream->stream_id = 0; /* cleared */
863 }
864 return 0;
865}
866
867static int on_begin_headers(nghttp2_session *session,
868 const nghttp2_frame *frame, void *userp)
869{
870 struct HTTP *stream;
871 struct Curl_easy *data_s = NULL;
872 (void)userp;
873
874 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
875 if(!data_s) {
876 return 0;
877 }
878
879 H2BUGF(infof(data_s, "on_begin_headers() was called\n"));
880
881 if(frame->hd.type != NGHTTP2_HEADERS) {
882 return 0;
883 }
884
885 stream = data_s->req.protop;
886 if(!stream || !stream->bodystarted) {
887 return 0;
888 }
889
890 if(!stream->trailer_recvbuf) {
891 stream->trailer_recvbuf = Curl_add_buffer_init();
892 if(!stream->trailer_recvbuf) {
893 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
894 }
895 }
896 return 0;
897}
898
899/* Decode HTTP status code. Returns -1 if no valid status code was
900 decoded. */
901static int decode_status_code(const uint8_t *value, size_t len)
902{
903 int i;
904 int res;
905
906 if(len != 3) {
907 return -1;
908 }
909
910 res = 0;
911
912 for(i = 0; i < 3; ++i) {
913 char c = value[i];
914
915 if(c < '0' || c > '9') {
916 return -1;
917 }
918
919 res *= 10;
920 res += c - '0';
921 }
922
923 return res;
924}
925
926/* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
927static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
928 const uint8_t *name, size_t namelen,
929 const uint8_t *value, size_t valuelen,
930 uint8_t flags,
931 void *userp)
932{
933 struct HTTP *stream;
934 struct Curl_easy *data_s;
935 int32_t stream_id = frame->hd.stream_id;
936 struct connectdata *conn = (struct connectdata *)userp;
937 CURLcode result;
938 (void)flags;
939
940 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
941
942 /* get the stream from the hash based on Stream ID */
943 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
944 if(!data_s)
945 /* Receiving a Stream ID not in the hash should not happen, this is an
946 internal error more than anything else! */
947 return NGHTTP2_ERR_CALLBACK_FAILURE;
948
949 stream = data_s->req.protop;
950 if(!stream) {
951 failf(data_s, "Internal NULL stream! 5\n");
952 return NGHTTP2_ERR_CALLBACK_FAILURE;
953 }
954
955 /* Store received PUSH_PROMISE headers to be used when the subsequent
956 PUSH_PROMISE callback comes */
957 if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
958 char *h;
959
960 if(!stream->push_headers) {
961 stream->push_headers_alloc = 10;
962 stream->push_headers = malloc(stream->push_headers_alloc *
963 sizeof(char *));
964 if(!stream->push_headers)
965 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
966 stream->push_headers_used = 0;
967 }
968 else if(stream->push_headers_used ==
969 stream->push_headers_alloc) {
970 char **headp;
971 stream->push_headers_alloc *= 2;
972 headp = Curl_saferealloc(stream->push_headers,
973 stream->push_headers_alloc * sizeof(char *));
974 if(!headp) {
975 stream->push_headers = NULL;
976 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
977 }
978 stream->push_headers = headp;
979 }
980 h = aprintf("%s:%s", name, value);
981 if(h)
982 stream->push_headers[stream->push_headers_used++] = h;
983 return 0;
984 }
985
986 if(stream->bodystarted) {
987 /* This is trailer fields. */
988 /* 4 is for ": " and "\r\n". */
989 uint32_t n = (uint32_t)(namelen + valuelen + 4);
990
991 H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s\n", namelen, name, valuelen,
992 value));
993
994 result = Curl_add_buffer(&stream->trailer_recvbuf, &n, sizeof(n));
995 if(result)
996 return NGHTTP2_ERR_CALLBACK_FAILURE;
997 result = Curl_add_buffer(&stream->trailer_recvbuf, name, namelen);
998 if(result)
999 return NGHTTP2_ERR_CALLBACK_FAILURE;
1000 result = Curl_add_buffer(&stream->trailer_recvbuf, ": ", 2);
1001 if(result)
1002 return NGHTTP2_ERR_CALLBACK_FAILURE;
1003 result = Curl_add_buffer(&stream->trailer_recvbuf, value, valuelen);
1004 if(result)
1005 return NGHTTP2_ERR_CALLBACK_FAILURE;
1006 result = Curl_add_buffer(&stream->trailer_recvbuf, "\r\n\0", 3);
1007 if(result)
1008 return NGHTTP2_ERR_CALLBACK_FAILURE;
1009
1010 return 0;
1011 }
1012
1013 if(namelen == sizeof(":status") - 1 &&
1014 memcmp(":status", name, namelen) == 0) {
1015 /* nghttp2 guarantees :status is received first and only once, and
1016 value is 3 digits status code, and decode_status_code always
1017 succeeds. */
1018 stream->status_code = decode_status_code(value, valuelen);
1019 DEBUGASSERT(stream->status_code != -1);
1020
1021 result = Curl_add_buffer(&stream->header_recvbuf, "HTTP/2 ", 7);
1022 if(result)
1023 return NGHTTP2_ERR_CALLBACK_FAILURE;
1024 result = Curl_add_buffer(&stream->header_recvbuf, value, valuelen);
1025 if(result)
1026 return NGHTTP2_ERR_CALLBACK_FAILURE;
1027 /* the space character after the status code is mandatory */
1028 result = Curl_add_buffer(&stream->header_recvbuf, " \r\n", 3);
1029 if(result)
1030 return NGHTTP2_ERR_CALLBACK_FAILURE;
1031 /* if we receive data for another handle, wake that up */
1032 if(conn->data != data_s)
1033 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
1034
1035 H2BUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)\n",
1036 stream->status_code, data_s));
1037 return 0;
1038 }
1039
1040 /* nghttp2 guarantees that namelen > 0, and :status was already
1041 received, and this is not pseudo-header field . */
1042 /* convert to a HTTP1-style header */
1043 result = Curl_add_buffer(&stream->header_recvbuf, name, namelen);
1044 if(result)
1045 return NGHTTP2_ERR_CALLBACK_FAILURE;
1046 result = Curl_add_buffer(&stream->header_recvbuf, ": ", 2);
1047 if(result)
1048 return NGHTTP2_ERR_CALLBACK_FAILURE;
1049 result = Curl_add_buffer(&stream->header_recvbuf, value, valuelen);
1050 if(result)
1051 return NGHTTP2_ERR_CALLBACK_FAILURE;
1052 result = Curl_add_buffer(&stream->header_recvbuf, "\r\n", 2);
1053 if(result)
1054 return NGHTTP2_ERR_CALLBACK_FAILURE;
1055 /* if we receive data for another handle, wake that up */
1056 if(conn->data != data_s)
1057 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
1058
1059 H2BUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
1060 value));
1061
1062 return 0; /* 0 is successful */
1063}
1064
1065static ssize_t data_source_read_callback(nghttp2_session *session,
1066 int32_t stream_id,
1067 uint8_t *buf, size_t length,
1068 uint32_t *data_flags,
1069 nghttp2_data_source *source,
1070 void *userp)
1071{
1072 struct Curl_easy *data_s;
1073 struct HTTP *stream = NULL;
1074 size_t nread;
1075 (void)source;
1076 (void)userp;
1077
1078 if(stream_id) {
1079 /* get the stream from the hash based on Stream ID, stream ID zero is for
1080 connection-oriented stuff */
1081 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
1082 if(!data_s)
1083 /* Receiving a Stream ID not in the hash should not happen, this is an
1084 internal error more than anything else! */
1085 return NGHTTP2_ERR_CALLBACK_FAILURE;
1086
1087 stream = data_s->req.protop;
1088 if(!stream)
1089 return NGHTTP2_ERR_CALLBACK_FAILURE;
1090 }
1091 else
1092 return NGHTTP2_ERR_INVALID_ARGUMENT;
1093
1094 nread = CURLMIN(stream->upload_len, length);
1095 if(nread > 0) {
1096 memcpy(buf, stream->upload_mem, nread);
1097 stream->upload_mem += nread;
1098 stream->upload_len -= nread;
1099 if(data_s->state.infilesize != -1)
1100 stream->upload_left -= nread;
1101 }
1102
1103 if(stream->upload_left == 0)
1104 *data_flags = NGHTTP2_DATA_FLAG_EOF;
1105 else if(nread == 0)
1106 return NGHTTP2_ERR_DEFERRED;
1107
1108 H2BUGF(infof(data_s, "data_source_read_callback: "
1109 "returns %zu bytes stream %u\n",
1110 nread, stream_id));
1111
1112 return nread;
1113}
1114
1115#if defined(NGHTTP2_HAS_ERROR_CALLBACK) && \
1116 !defined(CURL_DISABLE_VERBOSE_STRINGS)
1117static int error_callback(nghttp2_session *session,
1118 const char *msg,
1119 size_t len,
1120 void *userp)
1121{
1122 struct connectdata *conn = (struct connectdata *)userp;
1123 (void)session;
1124 infof(conn->data, "http2 error: %.*s\n", len, msg);
1125 return 0;
1126}
1127#endif
1128
1129static void populate_settings(struct connectdata *conn,
1130 struct http_conn *httpc)
1131{
1132 nghttp2_settings_entry *iv = httpc->local_settings;
1133
1134 iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
1135 iv[0].value = 100;
1136
1137 iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
1138 iv[1].value = HTTP2_HUGE_WINDOW_SIZE;
1139
1140 iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
1141 iv[2].value = conn->data->multi->push_cb != NULL;
1142
1143 httpc->local_settings_num = 3;
1144}
1145
1146void Curl_http2_done(struct connectdata *conn, bool premature)
1147{
1148 struct Curl_easy *data = conn->data;
1149 struct HTTP *http = data->req.protop;
1150 struct http_conn *httpc = &conn->proto.httpc;
1151
1152 /* there might be allocated resources done before this got the 'h2' pointer
1153 setup */
1154 if(http->header_recvbuf) {
1155 Curl_add_buffer_free(&http->header_recvbuf);
1156 Curl_add_buffer_free(&http->trailer_recvbuf);
1157 if(http->push_headers) {
1158 /* if they weren't used and then freed before */
1159 for(; http->push_headers_used > 0; --http->push_headers_used) {
1160 free(http->push_headers[http->push_headers_used - 1]);
1161 }
1162 free(http->push_headers);
1163 http->push_headers = NULL;
1164 }
1165 }
1166
1167 if(!httpc->h2) /* not HTTP/2 ? */
1168 return;
1169
1170 if(data->state.drain)
1171 drained_transfer(data, httpc);
1172
1173 if(premature) {
1174 /* RST_STREAM */
1175 if(!nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE,
1176 http->stream_id, NGHTTP2_STREAM_CLOSED))
1177 (void)nghttp2_session_send(httpc->h2);
1178
1179 if(http->stream_id == httpc->pause_stream_id) {
1180 infof(data, "stopped the pause stream!\n");
1181 httpc->pause_stream_id = 0;
1182 }
1183 }
1184 /* -1 means unassigned and 0 means cleared */
1185 if(http->stream_id > 0) {
1186 int rv = nghttp2_session_set_stream_user_data(httpc->h2,
1187 http->stream_id, 0);
1188 if(rv) {
1189 infof(data, "http/2: failed to clear user_data for stream %d!\n",
1190 http->stream_id);
1191 DEBUGASSERT(0);
1192 }
1193 http->stream_id = 0;
1194 }
1195}
1196
1197/*
1198 * Initialize nghttp2 for a Curl connection
1199 */
1200CURLcode Curl_http2_init(struct connectdata *conn)
1201{
1202 if(!conn->proto.httpc.h2) {
1203 int rc;
1204 nghttp2_session_callbacks *callbacks;
1205
1206 conn->proto.httpc.inbuf = malloc(H2_BUFSIZE);
1207 if(conn->proto.httpc.inbuf == NULL)
1208 return CURLE_OUT_OF_MEMORY;
1209
1210 rc = nghttp2_session_callbacks_new(&callbacks);
1211
1212 if(rc) {
1213 failf(conn->data, "Couldn't initialize nghttp2 callbacks!");
1214 return CURLE_OUT_OF_MEMORY; /* most likely at least */
1215 }
1216
1217 /* nghttp2_send_callback */
1218 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
1219 /* nghttp2_on_frame_recv_callback */
1220 nghttp2_session_callbacks_set_on_frame_recv_callback
1221 (callbacks, on_frame_recv);
1222 /* nghttp2_on_data_chunk_recv_callback */
1223 nghttp2_session_callbacks_set_on_data_chunk_recv_callback
1224 (callbacks, on_data_chunk_recv);
1225 /* nghttp2_on_stream_close_callback */
1226 nghttp2_session_callbacks_set_on_stream_close_callback
1227 (callbacks, on_stream_close);
1228 /* nghttp2_on_begin_headers_callback */
1229 nghttp2_session_callbacks_set_on_begin_headers_callback
1230 (callbacks, on_begin_headers);
1231 /* nghttp2_on_header_callback */
1232 nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
1233
1234#ifndef CURL_DISABLE_VERBOSE_STRINGS
1235 nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
1236#endif
1237
1238 /* The nghttp2 session is not yet setup, do it */
1239 rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
1240
1241 nghttp2_session_callbacks_del(callbacks);
1242
1243 if(rc) {
1244 failf(conn->data, "Couldn't initialize nghttp2!");
1245 return CURLE_OUT_OF_MEMORY; /* most likely at least */
1246 }
1247 }
1248 return CURLE_OK;
1249}
1250
1251/*
1252 * Append headers to ask for a HTTP1.1 to HTTP2 upgrade.
1253 */
1254CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req,
1255 struct connectdata *conn)
1256{
1257 CURLcode result;
1258 ssize_t binlen;
1259 char *base64;
1260 size_t blen;
1261 struct SingleRequest *k = &conn->data->req;
1262 uint8_t *binsettings = conn->proto.httpc.binsettings;
1263 struct http_conn *httpc = &conn->proto.httpc;
1264
1265 populate_settings(conn, httpc);
1266
1267 /* this returns number of bytes it wrote */
1268 binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
1269 httpc->local_settings,
1270 httpc->local_settings_num);
1271 if(!binlen) {
1272 failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
1273 Curl_add_buffer_free(&req);
1274 return CURLE_FAILED_INIT;
1275 }
1276 conn->proto.httpc.binlen = binlen;
1277
1278 result = Curl_base64url_encode(conn->data, (const char *)binsettings, binlen,
1279 &base64, &blen);
1280 if(result) {
1281 Curl_add_buffer_free(&req);
1282 return result;
1283 }
1284
1285 result = Curl_add_bufferf(&req,
1286 "Connection: Upgrade, HTTP2-Settings\r\n"
1287 "Upgrade: %s\r\n"
1288 "HTTP2-Settings: %s\r\n",
1289 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
1290 free(base64);
1291
1292 k->upgr101 = UPGR101_REQUESTED;
1293
1294 return result;
1295}
1296
1297/*
1298 * Returns nonzero if current HTTP/2 session should be closed.
1299 */
1300static int should_close_session(struct http_conn *httpc)
1301{
1302 return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) &&
1303 !nghttp2_session_want_write(httpc->h2);
1304}
1305
1306/*
1307 * h2_process_pending_input() processes pending input left in
1308 * httpc->inbuf. Then, call h2_session_send() to send pending data.
1309 * This function returns 0 if it succeeds, or -1 and error code will
1310 * be assigned to *err.
1311 */
1312static int h2_process_pending_input(struct connectdata *conn,
1313 struct http_conn *httpc,
1314 CURLcode *err)
1315{
1316 ssize_t nread;
1317 char *inbuf;
1318 ssize_t rv;
1319 struct Curl_easy *data = conn->data;
1320
1321 nread = httpc->inbuflen - httpc->nread_inbuf;
1322 inbuf = httpc->inbuf + httpc->nread_inbuf;
1323
1324 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
1325 if(rv < 0) {
1326 failf(data,
1327 "h2_process_pending_input: nghttp2_session_mem_recv() returned "
1328 "%zd:%s\n", rv, nghttp2_strerror((int)rv));
1329 *err = CURLE_RECV_ERROR;
1330 return -1;
1331 }
1332
1333 if(nread == rv) {
1334 H2BUGF(infof(data,
1335 "h2_process_pending_input: All data in connection buffer "
1336 "processed\n"));
1337 httpc->inbuflen = 0;
1338 httpc->nread_inbuf = 0;
1339 }
1340 else {
1341 httpc->nread_inbuf += rv;
1342 H2BUGF(infof(data,
1343 "h2_process_pending_input: %zu bytes left in connection "
1344 "buffer\n",
1345 httpc->inbuflen - httpc->nread_inbuf));
1346 }
1347
1348 rv = h2_session_send(data, httpc->h2);
1349 if(rv != 0) {
1350 *err = CURLE_SEND_ERROR;
1351 return -1;
1352 }
1353
1354 if(should_close_session(httpc)) {
1355 H2BUGF(infof(data,
1356 "h2_process_pending_input: nothing to do in this session\n"));
1357 if(httpc->error_code)
1358 *err = CURLE_HTTP2;
1359 else {
1360 /* not an error per se, but should still close the connection */
1361 connclose(conn, "GOAWAY received");
1362 *err = CURLE_OK;
1363 }
1364 return -1;
1365 }
1366
1367 return 0;
1368}
1369
1370/*
1371 * Called from transfer.c:done_sending when we stop uploading.
1372 */
1373CURLcode Curl_http2_done_sending(struct connectdata *conn)
1374{
1375 CURLcode result = CURLE_OK;
1376
1377 if((conn->handler == &Curl_handler_http2_ssl) ||
1378 (conn->handler == &Curl_handler_http2)) {
1379 /* make sure this is only attempted for HTTP/2 transfers */
1380
1381 struct HTTP *stream = conn->data->req.protop;
1382
1383 if(stream->upload_left) {
1384 /* If the stream still thinks there's data left to upload. */
1385 struct http_conn *httpc = &conn->proto.httpc;
1386 nghttp2_session *h2 = httpc->h2;
1387
1388 stream->upload_left = 0; /* DONE! */
1389
1390 /* resume sending here to trigger the callback to get called again so
1391 that it can signal EOF to nghttp2 */
1392 (void)nghttp2_session_resume_data(h2, stream->stream_id);
1393
1394 (void)h2_process_pending_input(conn, httpc, &result);
1395 }
1396 }
1397 return result;
1398}
1399
1400static ssize_t http2_handle_stream_close(struct connectdata *conn,
1401 struct Curl_easy *data,
1402 struct HTTP *stream, CURLcode *err)
1403{
1404 char *trailer_pos, *trailer_end;
1405 CURLcode result;
1406 struct http_conn *httpc = &conn->proto.httpc;
1407
1408 if(httpc->pause_stream_id == stream->stream_id) {
1409 httpc->pause_stream_id = 0;
1410 }
1411
1412 drained_transfer(data, httpc);
1413
1414 if(httpc->pause_stream_id == 0) {
1415 if(h2_process_pending_input(conn, httpc, err) != 0) {
1416 return -1;
1417 }
1418 }
1419
1420 DEBUGASSERT(data->state.drain == 0);
1421
1422 /* Reset to FALSE to prevent infinite loop in readwrite_data function. */
1423 stream->closed = FALSE;
1424 if(httpc->error_code == NGHTTP2_REFUSED_STREAM) {
1425 H2BUGF(infof(data, "REFUSED_STREAM (%d), try again on a new connection!\n",
1426 stream->stream_id));
1427 connclose(conn, "REFUSED_STREAM"); /* don't use this anymore */
1428 data->state.refused_stream = TRUE;
1429 *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */
1430 return -1;
1431 }
1432 else if(httpc->error_code != NGHTTP2_NO_ERROR) {
1433 failf(data, "HTTP/2 stream %d was not closed cleanly: %s (err %u)",
1434 stream->stream_id, Curl_http2_strerror(httpc->error_code),
1435 httpc->error_code);
1436 *err = CURLE_HTTP2_STREAM;
1437 return -1;
1438 }
1439
1440 if(!stream->bodystarted) {
1441 failf(data, "HTTP/2 stream %d was closed cleanly, but before getting "
1442 " all response header fields, treated as error",
1443 stream->stream_id);
1444 *err = CURLE_HTTP2_STREAM;
1445 return -1;
1446 }
1447
1448 if(stream->trailer_recvbuf && stream->trailer_recvbuf->buffer) {
1449 trailer_pos = stream->trailer_recvbuf->buffer;
1450 trailer_end = trailer_pos + stream->trailer_recvbuf->size_used;
1451
1452 for(; trailer_pos < trailer_end;) {
1453 uint32_t n;
1454 memcpy(&n, trailer_pos, sizeof(n));
1455 trailer_pos += sizeof(n);
1456
1457 result = Curl_client_write(conn, CLIENTWRITE_HEADER, trailer_pos, n);
1458 if(result) {
1459 *err = result;
1460 return -1;
1461 }
1462
1463 trailer_pos += n + 1;
1464 }
1465 }
1466
1467 stream->close_handled = TRUE;
1468
1469 H2BUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close\n"));
1470 return 0;
1471}
1472
1473/*
1474 * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
1475 * and dependency to the peer. It also stores the updated values in the state
1476 * struct.
1477 */
1478
1479static void h2_pri_spec(struct Curl_easy *data,
1480 nghttp2_priority_spec *pri_spec)
1481{
1482 struct HTTP *depstream = (data->set.stream_depends_on?
1483 data->set.stream_depends_on->req.protop:NULL);
1484 int32_t depstream_id = depstream? depstream->stream_id:0;
1485 nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_weight,
1486 data->set.stream_depends_e);
1487 data->state.stream_weight = data->set.stream_weight;
1488 data->state.stream_depends_e = data->set.stream_depends_e;
1489 data->state.stream_depends_on = data->set.stream_depends_on;
1490}
1491
1492/*
1493 * h2_session_send() checks if there's been an update in the priority /
1494 * dependency settings and if so it submits a PRIORITY frame with the updated
1495 * info.
1496 */
1497static int h2_session_send(struct Curl_easy *data,
1498 nghttp2_session *h2)
1499{
1500 struct HTTP *stream = data->req.protop;
1501 if((data->set.stream_weight != data->state.stream_weight) ||
1502 (data->set.stream_depends_e != data->state.stream_depends_e) ||
1503 (data->set.stream_depends_on != data->state.stream_depends_on) ) {
1504 /* send new weight and/or dependency */
1505 nghttp2_priority_spec pri_spec;
1506 int rv;
1507
1508 h2_pri_spec(data, &pri_spec);
1509
1510 H2BUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)\n",
1511 stream->stream_id, data));
1512 rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
1513 &pri_spec);
1514 if(rv)
1515 return rv;
1516 }
1517
1518 return nghttp2_session_send(h2);
1519}
1520
1521static ssize_t http2_recv(struct connectdata *conn, int sockindex,
1522 char *mem, size_t len, CURLcode *err)
1523{
1524 CURLcode result = CURLE_OK;
1525 ssize_t rv;
1526 ssize_t nread;
1527 struct http_conn *httpc = &conn->proto.httpc;
1528 struct Curl_easy *data = conn->data;
1529 struct HTTP *stream = data->req.protop;
1530
1531 (void)sockindex; /* we always do HTTP2 on sockindex 0 */
1532
1533 if(should_close_session(httpc)) {
1534 H2BUGF(infof(data,
1535 "http2_recv: nothing to do in this session\n"));
1536 *err = CURLE_HTTP2;
1537 return -1;
1538 }
1539
1540 /* Nullify here because we call nghttp2_session_send() and they
1541 might refer to the old buffer. */
1542 stream->upload_mem = NULL;
1543 stream->upload_len = 0;
1544
1545 /*
1546 * At this point 'stream' is just in the Curl_easy the connection
1547 * identifies as its owner at this time.
1548 */
1549
1550 if(stream->bodystarted &&
1551 stream->nread_header_recvbuf < stream->header_recvbuf->size_used) {
1552 /* If there is body data pending for this stream to return, do that */
1553 size_t left =
1554 stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
1555 size_t ncopy = CURLMIN(len, left);
1556 memcpy(mem, stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
1557 ncopy);
1558 stream->nread_header_recvbuf += ncopy;
1559
1560 H2BUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf\n",
1561 (int)ncopy));
1562 return ncopy;
1563 }
1564
1565 H2BUGF(infof(data, "http2_recv: easy %p (stream %u)\n",
1566 data, stream->stream_id));
1567
1568 if((data->state.drain) && stream->memlen) {
1569 H2BUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n",
1570 stream->memlen, stream->stream_id,
1571 stream->mem, mem));
1572 if(mem != stream->mem) {
1573 /* if we didn't get the same buffer this time, we must move the data to
1574 the beginning */
1575 memmove(mem, stream->mem, stream->memlen);
1576 stream->len = len - stream->memlen;
1577 stream->mem = mem;
1578 }
1579 if(httpc->pause_stream_id == stream->stream_id && !stream->pausedata) {
1580 /* We have paused nghttp2, but we have no pause data (see
1581 on_data_chunk_recv). */
1582 httpc->pause_stream_id = 0;
1583 if(h2_process_pending_input(conn, httpc, &result) != 0) {
1584 *err = result;
1585 return -1;
1586 }
1587 }
1588 }
1589 else if(stream->pausedata) {
1590 DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
1591 nread = CURLMIN(len, stream->pauselen);
1592 memcpy(mem, stream->pausedata, nread);
1593
1594 stream->pausedata += nread;
1595 stream->pauselen -= nread;
1596
1597 infof(data, "%zd data bytes written\n", nread);
1598 if(stream->pauselen == 0) {
1599 H2BUGF(infof(data, "Unpaused by stream %u\n", stream->stream_id));
1600 DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
1601 httpc->pause_stream_id = 0;
1602
1603 stream->pausedata = NULL;
1604 stream->pauselen = 0;
1605
1606 /* When NGHTTP2_ERR_PAUSE is returned from
1607 data_source_read_callback, we might not process DATA frame
1608 fully. Calling nghttp2_session_mem_recv() again will
1609 continue to process DATA frame, but if there is no incoming
1610 frames, then we have to call it again with 0-length data.
1611 Without this, on_stream_close callback will not be called,
1612 and stream could be hanged. */
1613 if(h2_process_pending_input(conn, httpc, &result) != 0) {
1614 *err = result;
1615 return -1;
1616 }
1617 }
1618 H2BUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u\n",
1619 nread, stream->stream_id));
1620 return nread;
1621 }
1622 else if(httpc->pause_stream_id) {
1623 /* If a stream paused nghttp2_session_mem_recv previously, and has
1624 not processed all data, it still refers to the buffer in
1625 nghttp2_session. If we call nghttp2_session_mem_recv(), we may
1626 overwrite that buffer. To avoid that situation, just return
1627 here with CURLE_AGAIN. This could be busy loop since data in
1628 socket is not read. But it seems that usually streams are
1629 notified with its drain property, and socket is read again
1630 quickly. */
1631 H2BUGF(infof(data, "stream %x is paused, pause id: %x\n",
1632 stream->stream_id, httpc->pause_stream_id));
1633 *err = CURLE_AGAIN;
1634 return -1;
1635 }
1636 else {
1637 char *inbuf;
1638 /* remember where to store incoming data for this stream and how big the
1639 buffer is */
1640 stream->mem = mem;
1641 stream->len = len;
1642 stream->memlen = 0;
1643
1644 if(httpc->inbuflen == 0) {
1645 nread = ((Curl_recv *)httpc->recv_underlying)(
1646 conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
1647
1648 if(nread == -1) {
1649 if(result != CURLE_AGAIN)
1650 failf(data, "Failed receiving HTTP2 data");
1651 else if(stream->closed)
1652 /* received when the stream was already closed! */
1653 return http2_handle_stream_close(conn, data, stream, err);
1654
1655 *err = result;
1656 return -1;
1657 }
1658
1659 if(nread == 0) {
1660 H2BUGF(infof(data, "end of stream\n"));
1661 *err = CURLE_OK;
1662 return 0;
1663 }
1664
1665 H2BUGF(infof(data, "nread=%zd\n", nread));
1666
1667 httpc->inbuflen = nread;
1668 inbuf = httpc->inbuf;
1669 }
1670 else {
1671 nread = httpc->inbuflen - httpc->nread_inbuf;
1672 inbuf = httpc->inbuf + httpc->nread_inbuf;
1673
1674 H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd\n",
1675 nread));
1676 }
1677 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
1678
1679 if(nghttp2_is_fatal((int)rv)) {
1680 failf(data, "nghttp2_session_mem_recv() returned %zd:%s\n",
1681 rv, nghttp2_strerror((int)rv));
1682 *err = CURLE_RECV_ERROR;
1683 return -1;
1684 }
1685 H2BUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", rv));
1686 if(nread == rv) {
1687 H2BUGF(infof(data, "All data in connection buffer processed\n"));
1688 httpc->inbuflen = 0;
1689 httpc->nread_inbuf = 0;
1690 }
1691 else {
1692 httpc->nread_inbuf += rv;
1693 H2BUGF(infof(data, "%zu bytes left in connection buffer\n",
1694 httpc->inbuflen - httpc->nread_inbuf));
1695 }
1696 /* Always send pending frames in nghttp2 session, because
1697 nghttp2_session_mem_recv() may queue new frame */
1698 rv = h2_session_send(data, httpc->h2);
1699 if(rv != 0) {
1700 *err = CURLE_SEND_ERROR;
1701 return -1;
1702 }
1703
1704 if(should_close_session(httpc)) {
1705 H2BUGF(infof(data, "http2_recv: nothing to do in this session\n"));
1706 *err = CURLE_HTTP2;
1707 return -1;
1708 }
1709 }
1710 if(stream->memlen) {
1711 ssize_t retlen = stream->memlen;
1712 H2BUGF(infof(data, "http2_recv: returns %zd for stream %u\n",
1713 retlen, stream->stream_id));
1714 stream->memlen = 0;
1715
1716 if(httpc->pause_stream_id == stream->stream_id) {
1717 /* data for this stream is returned now, but this stream caused a pause
1718 already so we need it called again asap */
1719 H2BUGF(infof(data, "Data returned for PAUSED stream %u\n",
1720 stream->stream_id));
1721 }
1722 else if(!stream->closed) {
1723 drained_transfer(data, httpc);
1724 }
1725
1726 return retlen;
1727 }
1728 /* If stream is closed, return 0 to signal the http routine to close
1729 the connection */
1730 if(stream->closed) {
1731 return http2_handle_stream_close(conn, data, stream, err);
1732 }
1733 *err = CURLE_AGAIN;
1734 H2BUGF(infof(data, "http2_recv returns AGAIN for stream %u\n",
1735 stream->stream_id));
1736 return -1;
1737}
1738
1739/* Index where :authority header field will appear in request header
1740 field list. */
1741#define AUTHORITY_DST_IDX 3
1742
1743#define HEADER_OVERFLOW(x) \
1744 (x.namelen > (uint16_t)-1 || x.valuelen > (uint16_t)-1 - x.namelen)
1745
1746/*
1747 * Check header memory for the token "trailers".
1748 * Parse the tokens as separated by comma and surrounded by whitespace.
1749 * Returns TRUE if found or FALSE if not.
1750 */
1751static bool contains_trailers(const char *p, size_t len)
1752{
1753 const char *end = p + len;
1754 for(;;) {
1755 for(; p != end && (*p == ' ' || *p == '\t'); ++p)
1756 ;
1757 if(p == end || (size_t)(end - p) < sizeof("trailers") - 1)
1758 return FALSE;
1759 if(strncasecompare("trailers", p, sizeof("trailers") - 1)) {
1760 p += sizeof("trailers") - 1;
1761 for(; p != end && (*p == ' ' || *p == '\t'); ++p)
1762 ;
1763 if(p == end || *p == ',')
1764 return TRUE;
1765 }
1766 /* skip to next token */
1767 for(; p != end && *p != ','; ++p)
1768 ;
1769 if(p == end)
1770 return FALSE;
1771 ++p;
1772 }
1773}
1774
1775typedef enum {
1776 /* Send header to server */
1777 HEADERINST_FORWARD,
1778 /* Don't send header to server */
1779 HEADERINST_IGNORE,
1780 /* Discard header, and replace it with "te: trailers" */
1781 HEADERINST_TE_TRAILERS
1782} header_instruction;
1783
1784/* Decides how to treat given header field. */
1785static header_instruction inspect_header(const char *name, size_t namelen,
1786 const char *value, size_t valuelen) {
1787 switch(namelen) {
1788 case 2:
1789 if(!strncasecompare("te", name, namelen))
1790 return HEADERINST_FORWARD;
1791
1792 return contains_trailers(value, valuelen) ?
1793 HEADERINST_TE_TRAILERS : HEADERINST_IGNORE;
1794 case 7:
1795 return strncasecompare("upgrade", name, namelen) ?
1796 HEADERINST_IGNORE : HEADERINST_FORWARD;
1797 case 10:
1798 return (strncasecompare("connection", name, namelen) ||
1799 strncasecompare("keep-alive", name, namelen)) ?
1800 HEADERINST_IGNORE : HEADERINST_FORWARD;
1801 case 16:
1802 return strncasecompare("proxy-connection", name, namelen) ?
1803 HEADERINST_IGNORE : HEADERINST_FORWARD;
1804 case 17:
1805 return strncasecompare("transfer-encoding", name, namelen) ?
1806 HEADERINST_IGNORE : HEADERINST_FORWARD;
1807 default:
1808 return HEADERINST_FORWARD;
1809 }
1810}
1811
1812static ssize_t http2_send(struct connectdata *conn, int sockindex,
1813 const void *mem, size_t len, CURLcode *err)
1814{
1815 /*
1816 * BIG TODO: Currently, we send request in this function, but this
1817 * function is also used to send request body. It would be nice to
1818 * add dedicated function for request.
1819 */
1820 int rv;
1821 struct http_conn *httpc = &conn->proto.httpc;
1822 struct HTTP *stream = conn->data->req.protop;
1823 nghttp2_nv *nva = NULL;
1824 size_t nheader;
1825 size_t i;
1826 size_t authority_idx;
1827 char *hdbuf = (char *)mem;
1828 char *end, *line_end;
1829 nghttp2_data_provider data_prd;
1830 int32_t stream_id;
1831 nghttp2_session *h2 = httpc->h2;
1832 nghttp2_priority_spec pri_spec;
1833
1834 (void)sockindex;
1835
1836 H2BUGF(infof(conn->data, "http2_send len=%zu\n", len));
1837
1838 if(stream->stream_id != -1) {
1839 if(stream->close_handled) {
1840 infof(conn->data, "stream %d closed\n", stream->stream_id);
1841 *err = CURLE_HTTP2_STREAM;
1842 return -1;
1843 }
1844 else if(stream->closed) {
1845 return http2_handle_stream_close(conn, conn->data, stream, err);
1846 }
1847 /* If stream_id != -1, we have dispatched request HEADERS, and now
1848 are going to send or sending request body in DATA frame */
1849 stream->upload_mem = mem;
1850 stream->upload_len = len;
1851 nghttp2_session_resume_data(h2, stream->stream_id);
1852 rv = h2_session_send(conn->data, h2);
1853 if(nghttp2_is_fatal(rv)) {
1854 *err = CURLE_SEND_ERROR;
1855 return -1;
1856 }
1857 len -= stream->upload_len;
1858
1859 /* Nullify here because we call nghttp2_session_send() and they
1860 might refer to the old buffer. */
1861 stream->upload_mem = NULL;
1862 stream->upload_len = 0;
1863
1864 if(should_close_session(httpc)) {
1865 H2BUGF(infof(conn->data, "http2_send: nothing to do in this session\n"));
1866 *err = CURLE_HTTP2;
1867 return -1;
1868 }
1869
1870 if(stream->upload_left) {
1871 /* we are sure that we have more data to send here. Calling the
1872 following API will make nghttp2_session_want_write() return
1873 nonzero if remote window allows it, which then libcurl checks
1874 socket is writable or not. See http2_perform_getsock(). */
1875 nghttp2_session_resume_data(h2, stream->stream_id);
1876 }
1877
1878 H2BUGF(infof(conn->data, "http2_send returns %zu for stream %u\n", len,
1879 stream->stream_id));
1880 return len;
1881 }
1882
1883 /* Calculate number of headers contained in [mem, mem + len) */
1884 /* Here, we assume the curl http code generate *correct* HTTP header
1885 field block */
1886 nheader = 0;
1887 for(i = 1; i < len; ++i) {
1888 if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') {
1889 ++nheader;
1890 ++i;
1891 }
1892 }
1893 if(nheader < 2)
1894 goto fail;
1895
1896 /* We counted additional 2 \r\n in the first and last line. We need 3
1897 new headers: :method, :path and :scheme. Therefore we need one
1898 more space. */
1899 nheader += 1;
1900 nva = malloc(sizeof(nghttp2_nv) * nheader);
1901 if(nva == NULL) {
1902 *err = CURLE_OUT_OF_MEMORY;
1903 return -1;
1904 }
1905
1906 /* Extract :method, :path from request line
1907 We do line endings with CRLF so checking for CR is enough */
1908 line_end = memchr(hdbuf, '\r', len);
1909 if(!line_end)
1910 goto fail;
1911
1912 /* Method does not contain spaces */
1913 end = memchr(hdbuf, ' ', line_end - hdbuf);
1914 if(!end || end == hdbuf)
1915 goto fail;
1916 nva[0].name = (unsigned char *)":method";
1917 nva[0].namelen = strlen((char *)nva[0].name);
1918 nva[0].value = (unsigned char *)hdbuf;
1919 nva[0].valuelen = (size_t)(end - hdbuf);
1920 nva[0].flags = NGHTTP2_NV_FLAG_NONE;
1921 if(HEADER_OVERFLOW(nva[0])) {
1922 failf(conn->data, "Failed sending HTTP request: Header overflow");
1923 goto fail;
1924 }
1925
1926 hdbuf = end + 1;
1927
1928 /* Path may contain spaces so scan backwards */
1929 end = NULL;
1930 for(i = (size_t)(line_end - hdbuf); i; --i) {
1931 if(hdbuf[i - 1] == ' ') {
1932 end = &hdbuf[i - 1];
1933 break;
1934 }
1935 }
1936 if(!end || end == hdbuf)
1937 goto fail;
1938 nva[1].name = (unsigned char *)":path";
1939 nva[1].namelen = strlen((char *)nva[1].name);
1940 nva[1].value = (unsigned char *)hdbuf;
1941 nva[1].valuelen = (size_t)(end - hdbuf);
1942 nva[1].flags = NGHTTP2_NV_FLAG_NONE;
1943 if(HEADER_OVERFLOW(nva[1])) {
1944 failf(conn->data, "Failed sending HTTP request: Header overflow");
1945 goto fail;
1946 }
1947
1948 nva[2].name = (unsigned char *)":scheme";
1949 nva[2].namelen = strlen((char *)nva[2].name);
1950 if(conn->handler->flags & PROTOPT_SSL)
1951 nva[2].value = (unsigned char *)"https";
1952 else
1953 nva[2].value = (unsigned char *)"http";
1954 nva[2].valuelen = strlen((char *)nva[2].value);
1955 nva[2].flags = NGHTTP2_NV_FLAG_NONE;
1956 if(HEADER_OVERFLOW(nva[2])) {
1957 failf(conn->data, "Failed sending HTTP request: Header overflow");
1958 goto fail;
1959 }
1960
1961 authority_idx = 0;
1962 i = 3;
1963 while(i < nheader) {
1964 size_t hlen;
1965
1966 hdbuf = line_end + 2;
1967
1968 /* check for next CR, but only within the piece of data left in the given
1969 buffer */
1970 line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem));
1971 if(!line_end || (line_end == hdbuf))
1972 goto fail;
1973
1974 /* header continuation lines are not supported */
1975 if(*hdbuf == ' ' || *hdbuf == '\t')
1976 goto fail;
1977
1978 for(end = hdbuf; end < line_end && *end != ':'; ++end)
1979 ;
1980 if(end == hdbuf || end == line_end)
1981 goto fail;
1982 hlen = end - hdbuf;
1983
1984 if(hlen == 4 && strncasecompare("host", hdbuf, 4)) {
1985 authority_idx = i;
1986 nva[i].name = (unsigned char *)":authority";
1987 nva[i].namelen = strlen((char *)nva[i].name);
1988 }
1989 else {
1990 nva[i].name = (unsigned char *)hdbuf;
1991 nva[i].namelen = (size_t)(end - hdbuf);
1992 }
1993 hdbuf = end + 1;
1994 while(*hdbuf == ' ' || *hdbuf == '\t')
1995 ++hdbuf;
1996 end = line_end;
1997
1998 switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf,
1999 end - hdbuf)) {
2000 case HEADERINST_IGNORE:
2001 /* skip header fields prohibited by HTTP/2 specification. */
2002 --nheader;
2003 continue;
2004 case HEADERINST_TE_TRAILERS:
2005 nva[i].value = (uint8_t*)"trailers";
2006 nva[i].valuelen = sizeof("trailers") - 1;
2007 break;
2008 default:
2009 nva[i].value = (unsigned char *)hdbuf;
2010 nva[i].valuelen = (size_t)(end - hdbuf);
2011 }
2012
2013 nva[i].flags = NGHTTP2_NV_FLAG_NONE;
2014 if(HEADER_OVERFLOW(nva[i])) {
2015 failf(conn->data, "Failed sending HTTP request: Header overflow");
2016 goto fail;
2017 }
2018 ++i;
2019 }
2020
2021 /* :authority must come before non-pseudo header fields */
2022 if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) {
2023 nghttp2_nv authority = nva[authority_idx];
2024 for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
2025 nva[i] = nva[i - 1];
2026 }
2027 nva[i] = authority;
2028 }
2029
2030 /* Warn stream may be rejected if cumulative length of headers is too large.
2031 It appears nghttp2 will not send a header frame larger than 64KB. */
2032#define MAX_ACC 60000 /* <64KB to account for some overhead */
2033 {
2034 size_t acc = 0;
2035
2036 for(i = 0; i < nheader; ++i) {
2037 acc += nva[i].namelen + nva[i].valuelen;
2038
2039 H2BUGF(infof(conn->data, "h2 header: %.*s:%.*s\n",
2040 nva[i].namelen, nva[i].name,
2041 nva[i].valuelen, nva[i].value));
2042 }
2043
2044 if(acc > MAX_ACC) {
2045 infof(conn->data, "http2_send: Warning: The cumulative length of all "
2046 "headers exceeds %zu bytes and that could cause the "
2047 "stream to be rejected.\n", MAX_ACC);
2048 }
2049 }
2050
2051 h2_pri_spec(conn->data, &pri_spec);
2052
2053 switch(conn->data->set.httpreq) {
2054 case HTTPREQ_POST:
2055 case HTTPREQ_POST_FORM:
2056 case HTTPREQ_POST_MIME:
2057 case HTTPREQ_PUT:
2058 if(conn->data->state.infilesize != -1)
2059 stream->upload_left = conn->data->state.infilesize;
2060 else
2061 /* data sending without specifying the data amount up front */
2062 stream->upload_left = -1; /* unknown, but not zero */
2063
2064 data_prd.read_callback = data_source_read_callback;
2065 data_prd.source.ptr = NULL;
2066 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
2067 &data_prd, conn->data);
2068 break;
2069 default:
2070 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
2071 NULL, conn->data);
2072 }
2073
2074 Curl_safefree(nva);
2075
2076 if(stream_id < 0) {
2077 H2BUGF(infof(conn->data, "http2_send() send error\n"));
2078 *err = CURLE_SEND_ERROR;
2079 return -1;
2080 }
2081
2082 infof(conn->data, "Using Stream ID: %x (easy handle %p)\n",
2083 stream_id, (void *)conn->data);
2084 stream->stream_id = stream_id;
2085
2086 /* this does not call h2_session_send() since there can not have been any
2087 * priority upodate since the nghttp2_submit_request() call above */
2088 rv = nghttp2_session_send(h2);
2089
2090 if(rv != 0) {
2091 *err = CURLE_SEND_ERROR;
2092 return -1;
2093 }
2094
2095 if(should_close_session(httpc)) {
2096 H2BUGF(infof(conn->data, "http2_send: nothing to do in this session\n"));
2097 *err = CURLE_HTTP2;
2098 return -1;
2099 }
2100
2101 if(stream->stream_id != -1) {
2102 /* If whole HEADERS frame was sent off to the underlying socket,
2103 the nghttp2 library calls data_source_read_callback. But only
2104 it found that no data available, so it deferred the DATA
2105 transmission. Which means that nghttp2_session_want_write()
2106 returns 0 on http2_perform_getsock(), which results that no
2107 writable socket check is performed. To workaround this, we
2108 issue nghttp2_session_resume_data() here to bring back DATA
2109 transmission from deferred state. */
2110 nghttp2_session_resume_data(h2, stream->stream_id);
2111 }
2112
2113 return len;
2114
2115fail:
2116 free(nva);
2117 *err = CURLE_SEND_ERROR;
2118 return -1;
2119}
2120
2121CURLcode Curl_http2_setup(struct connectdata *conn)
2122{
2123 CURLcode result;
2124 struct http_conn *httpc = &conn->proto.httpc;
2125 struct HTTP *stream = conn->data->req.protop;
2126
2127 stream->stream_id = -1;
2128
2129 if(!stream->header_recvbuf) {
2130 stream->header_recvbuf = Curl_add_buffer_init();
2131 if(!stream->header_recvbuf)
2132 return CURLE_OUT_OF_MEMORY;
2133 }
2134
2135 if((conn->handler == &Curl_handler_http2_ssl) ||
2136 (conn->handler == &Curl_handler_http2))
2137 return CURLE_OK; /* already done */
2138
2139 if(conn->handler->flags & PROTOPT_SSL)
2140 conn->handler = &Curl_handler_http2_ssl;
2141 else
2142 conn->handler = &Curl_handler_http2;
2143
2144 result = Curl_http2_init(conn);
2145 if(result) {
2146 Curl_add_buffer_free(&stream->header_recvbuf);
2147 return result;
2148 }
2149
2150 infof(conn->data, "Using HTTP2, server supports multi-use\n");
2151 stream->upload_left = 0;
2152 stream->upload_mem = NULL;
2153 stream->upload_len = 0;
2154
2155 httpc->inbuflen = 0;
2156 httpc->nread_inbuf = 0;
2157
2158 httpc->pause_stream_id = 0;
2159 httpc->drain_total = 0;
2160
2161 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2162 conn->httpversion = 20;
2163 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
2164
2165 infof(conn->data, "Connection state changed (HTTP/2 confirmed)\n");
2166 Curl_multi_connchanged(conn->data->multi);
2167
2168 return CURLE_OK;
2169}
2170
2171CURLcode Curl_http2_switched(struct connectdata *conn,
2172 const char *mem, size_t nread)
2173{
2174 CURLcode result;
2175 struct http_conn *httpc = &conn->proto.httpc;
2176 int rv;
2177 ssize_t nproc;
2178 struct Curl_easy *data = conn->data;
2179 struct HTTP *stream = conn->data->req.protop;
2180
2181 result = Curl_http2_setup(conn);
2182 if(result)
2183 return result;
2184
2185 httpc->recv_underlying = conn->recv[FIRSTSOCKET];
2186 httpc->send_underlying = conn->send[FIRSTSOCKET];
2187 conn->recv[FIRSTSOCKET] = http2_recv;
2188 conn->send[FIRSTSOCKET] = http2_send;
2189
2190 if(conn->data->req.upgr101 == UPGR101_RECEIVED) {
2191 /* stream 1 is opened implicitly on upgrade */
2192 stream->stream_id = 1;
2193 /* queue SETTINGS frame (again) */
2194 rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings,
2195 httpc->binlen, NULL);
2196 if(rv != 0) {
2197 failf(data, "nghttp2_session_upgrade() failed: %s(%d)",
2198 nghttp2_strerror(rv), rv);
2199 return CURLE_HTTP2;
2200 }
2201
2202 rv = nghttp2_session_set_stream_user_data(httpc->h2,
2203 stream->stream_id,
2204 data);
2205 if(rv) {
2206 infof(data, "http/2: failed to set user_data for stream %d!\n",
2207 stream->stream_id);
2208 DEBUGASSERT(0);
2209 }
2210 }
2211 else {
2212 populate_settings(conn, httpc);
2213
2214 /* stream ID is unknown at this point */
2215 stream->stream_id = -1;
2216 rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE,
2217 httpc->local_settings,
2218 httpc->local_settings_num);
2219 if(rv != 0) {
2220 failf(data, "nghttp2_submit_settings() failed: %s(%d)",
2221 nghttp2_strerror(rv), rv);
2222 return CURLE_HTTP2;
2223 }
2224 }
2225
2226#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
2227 rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, 0,
2228 HTTP2_HUGE_WINDOW_SIZE);
2229 if(rv != 0) {
2230 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
2231 nghttp2_strerror(rv), rv);
2232 return CURLE_HTTP2;
2233 }
2234#endif
2235
2236 /* we are going to copy mem to httpc->inbuf. This is required since
2237 mem is part of buffer pointed by stream->mem, and callbacks
2238 called by nghttp2_session_mem_recv() will write stream specific
2239 data into stream->mem, overwriting data already there. */
2240 if(H2_BUFSIZE < nread) {
2241 failf(data, "connection buffer size is too small to store data following "
2242 "HTTP Upgrade response header: buflen=%zu, datalen=%zu",
2243 H2_BUFSIZE, nread);
2244 return CURLE_HTTP2;
2245 }
2246
2247 infof(conn->data, "Copying HTTP/2 data in stream buffer to connection buffer"
2248 " after upgrade: len=%zu\n",
2249 nread);
2250
2251 if(nread)
2252 memcpy(httpc->inbuf, mem, nread);
2253 httpc->inbuflen = nread;
2254
2255 nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf,
2256 httpc->inbuflen);
2257
2258 if(nghttp2_is_fatal((int)nproc)) {
2259 failf(data, "nghttp2_session_mem_recv() failed: %s(%d)",
2260 nghttp2_strerror((int)nproc), (int)nproc);
2261 return CURLE_HTTP2;
2262 }
2263
2264 H2BUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", nproc));
2265
2266 if((ssize_t)nread == nproc) {
2267 httpc->inbuflen = 0;
2268 httpc->nread_inbuf = 0;
2269 }
2270 else {
2271 httpc->nread_inbuf += nproc;
2272 }
2273
2274 /* Try to send some frames since we may read SETTINGS already. */
2275 rv = h2_session_send(data, httpc->h2);
2276
2277 if(rv != 0) {
2278 failf(data, "nghttp2_session_send() failed: %s(%d)",
2279 nghttp2_strerror(rv), rv);
2280 return CURLE_HTTP2;
2281 }
2282
2283 if(should_close_session(httpc)) {
2284 H2BUGF(infof(data,
2285 "nghttp2_session_send(): nothing to do in this session\n"));
2286 return CURLE_HTTP2;
2287 }
2288
2289 return CURLE_OK;
2290}
2291
2292CURLcode Curl_http2_add_child(struct Curl_easy *parent,
2293 struct Curl_easy *child,
2294 bool exclusive)
2295{
2296 if(parent) {
2297 struct Curl_http2_dep **tail;
2298 struct Curl_http2_dep *dep = calloc(1, sizeof(struct Curl_http2_dep));
2299 if(!dep)
2300 return CURLE_OUT_OF_MEMORY;
2301 dep->data = child;
2302
2303 if(parent->set.stream_dependents && exclusive) {
2304 struct Curl_http2_dep *node = parent->set.stream_dependents;
2305 while(node) {
2306 node->data->set.stream_depends_on = child;
2307 node = node->next;
2308 }
2309
2310 tail = &child->set.stream_dependents;
2311 while(*tail)
2312 tail = &(*tail)->next;
2313
2314 DEBUGASSERT(!*tail);
2315 *tail = parent->set.stream_dependents;
2316 parent->set.stream_dependents = 0;
2317 }
2318
2319 tail = &parent->set.stream_dependents;
2320 while(*tail) {
2321 (*tail)->data->set.stream_depends_e = FALSE;
2322 tail = &(*tail)->next;
2323 }
2324
2325 DEBUGASSERT(!*tail);
2326 *tail = dep;
2327 }
2328
2329 child->set.stream_depends_on = parent;
2330 child->set.stream_depends_e = exclusive;
2331 return CURLE_OK;
2332}
2333
2334void Curl_http2_remove_child(struct Curl_easy *parent, struct Curl_easy *child)
2335{
2336 struct Curl_http2_dep *last = 0;
2337 struct Curl_http2_dep *data = parent->set.stream_dependents;
2338 DEBUGASSERT(child->set.stream_depends_on == parent);
2339
2340 while(data && data->data != child) {
2341 last = data;
2342 data = data->next;
2343 }
2344
2345 DEBUGASSERT(data);
2346
2347 if(data) {
2348 if(last) {
2349 last->next = data->next;
2350 }
2351 else {
2352 parent->set.stream_dependents = data->next;
2353 }
2354 free(data);
2355 }
2356
2357 child->set.stream_depends_on = 0;
2358 child->set.stream_depends_e = FALSE;
2359}
2360
2361void Curl_http2_cleanup_dependencies(struct Curl_easy *data)
2362{
2363 while(data->set.stream_dependents) {
2364 struct Curl_easy *tmp = data->set.stream_dependents->data;
2365 Curl_http2_remove_child(data, tmp);
2366 if(data->set.stream_depends_on)
2367 Curl_http2_add_child(data->set.stream_depends_on, tmp, FALSE);
2368 }
2369
2370 if(data->set.stream_depends_on)
2371 Curl_http2_remove_child(data->set.stream_depends_on, data);
2372}
2373
2374/* Only call this function for a transfer that already got a HTTP/2
2375 CURLE_HTTP2_STREAM error! */
2376bool Curl_h2_http_1_1_error(struct connectdata *conn)
2377{
2378 struct http_conn *httpc = &conn->proto.httpc;
2379 return (httpc->error_code == NGHTTP2_HTTP_1_1_REQUIRED);
2380}
2381
2382#else /* !USE_NGHTTP2 */
2383
2384/* Satisfy external references even if http2 is not compiled in. */
2385
2386#define CURL_DISABLE_TYPECHECK
2387#include <curl/curl.h>
2388
2389char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
2390{
2391 (void) h;
2392 (void) num;
2393 return NULL;
2394}
2395
2396char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
2397{
2398 (void) h;
2399 (void) header;
2400 return NULL;
2401}
2402
2403#endif /* USE_NGHTTP2 */
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette