VirtualBox

source: vbox/trunk/src/libs/curl-7.83.1/lib/http2.c@ 95312

Last change on this file since 95312 was 95312, checked in by vboxsync, 3 years ago

libs/{curl,libxml2}: OSE export fixes, bugref:8515

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

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