comparison mupdf-source/thirdparty/curl/lib/http2.c @ 2:b50eed0cc0ef upstream

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