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