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