1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 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 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25 #include "curl_setup.h"
26
27 #ifdef USE_NGHTTP2
28 #include <stdint.h>
29 #include <nghttp2/nghttp2.h>
30 #include "urldata.h"
31 #include "bufq.h"
32 #include "http1.h"
33 #include "http2.h"
34 #include "http.h"
35 #include "sendf.h"
36 #include "select.h"
37 #include "curl_base64.h"
38 #include "strcase.h"
39 #include "multiif.h"
40 #include "url.h"
41 #include "urlapi-int.h"
42 #include "cfilters.h"
43 #include "connect.h"
44 #include "rand.h"
45 #include "strtoofft.h"
46 #include "strdup.h"
47 #include "transfer.h"
48 #include "dynbuf.h"
49 #include "headers.h"
50 /* The last 3 #include files should be in this order */
51 #include "curl_printf.h"
52 #include "curl_memory.h"
53 #include "memdebug.h"
54
55 #if (NGHTTP2_VERSION_NUM < 0x010c00)
56 #error too old nghttp2 version, upgrade!
57 #endif
58
59 #ifdef CURL_DISABLE_VERBOSE_STRINGS
60 #define nghttp2_session_callbacks_set_error_callback(x,y)
61 #endif
62
63 #if (NGHTTP2_VERSION_NUM >= 0x010c00)
64 #define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1
65 #endif
66
67
68 /* buffer dimensioning:
69 * use 16K as chunk size, as that fits H2 DATA frames well */
70 #define H2_CHUNK_SIZE (16 * 1024)
71 /* this is how much we want "in flight" for a stream */
72 #define H2_STREAM_WINDOW_SIZE (10 * 1024 * 1024)
73 /* on receiving from TLS, we prep for holding a full stream window */
74 #define H2_NW_RECV_CHUNKS (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE)
75 /* on send into TLS, we just want to accumulate small frames */
76 #define H2_NW_SEND_CHUNKS 1
77 /* stream recv/send chunks are a result of window / chunk sizes */
78 #define H2_STREAM_RECV_CHUNKS (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE)
79 /* keep smaller stream upload buffer (default h2 window size) to have
80 * our progress bars and "upload done" reporting closer to reality */
81 #define H2_STREAM_SEND_CHUNKS ((64 * 1024) / H2_CHUNK_SIZE)
82 /* spare chunks we keep for a full window */
83 #define H2_STREAM_POOL_SPARES (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE)
84
85 /* We need to accommodate the max number of streams with their window
86 * sizes on the overall connection. Streams might become PAUSED which
87 * will block their received QUOTA in the connection window. And if we
88 * run out of space, the server is blocked from sending us any data.
89 * See #10988 for an issue with this. */
90 #define HTTP2_HUGE_WINDOW_SIZE (100 * H2_STREAM_WINDOW_SIZE)
91
92 #define H2_SETTINGS_IV_LEN 3
93 #define H2_BINSETTINGS_LEN 80
94
populate_settings(nghttp2_settings_entry * iv,struct Curl_easy * data)95 static int populate_settings(nghttp2_settings_entry *iv,
96 struct Curl_easy *data)
97 {
98 iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
99 iv[0].value = Curl_multi_max_concurrent_streams(data->multi);
100
101 iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
102 iv[1].value = H2_STREAM_WINDOW_SIZE;
103
104 iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
105 iv[2].value = data->multi->push_cb != NULL;
106
107 return 3;
108 }
109
populate_binsettings(uint8_t * binsettings,struct Curl_easy * data)110 static size_t populate_binsettings(uint8_t *binsettings,
111 struct Curl_easy *data)
112 {
113 nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN];
114 int ivlen;
115
116 ivlen = populate_settings(iv, data);
117 /* this returns number of bytes it wrote */
118 return nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
119 iv, ivlen);
120 }
121
122 struct cf_h2_ctx {
123 nghttp2_session *h2;
124 uint32_t max_concurrent_streams;
125 /* The easy handle used in the current filter call, cleared at return */
126 struct cf_call_data call_data;
127
128 struct bufq inbufq; /* network input */
129 struct bufq outbufq; /* network output */
130 struct bufc_pool stream_bufcp; /* spares for stream buffers */
131
132 size_t drain_total; /* sum of all stream's UrlState drain */
133 int32_t goaway_error;
134 int32_t last_stream_id;
135 BIT(conn_closed);
136 BIT(goaway);
137 BIT(enable_push);
138 BIT(nw_out_blocked);
139 };
140
141 /* How to access `call_data` from a cf_h2 filter */
142 #undef CF_CTX_CALL_DATA
143 #define CF_CTX_CALL_DATA(cf) \
144 ((struct cf_h2_ctx *)(cf)->ctx)->call_data
145
cf_h2_ctx_clear(struct cf_h2_ctx * ctx)146 static void cf_h2_ctx_clear(struct cf_h2_ctx *ctx)
147 {
148 struct cf_call_data save = ctx->call_data;
149
150 if(ctx->h2) {
151 nghttp2_session_del(ctx->h2);
152 }
153 Curl_bufq_free(&ctx->inbufq);
154 Curl_bufq_free(&ctx->outbufq);
155 Curl_bufcp_free(&ctx->stream_bufcp);
156 memset(ctx, 0, sizeof(*ctx));
157 ctx->call_data = save;
158 }
159
cf_h2_ctx_free(struct cf_h2_ctx * ctx)160 static void cf_h2_ctx_free(struct cf_h2_ctx *ctx)
161 {
162 if(ctx) {
163 cf_h2_ctx_clear(ctx);
164 free(ctx);
165 }
166 }
167
168 static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
169 struct Curl_easy *data);
170
171 /**
172 * All about the H3 internals of a stream
173 */
174 struct stream_ctx {
175 /*********** for HTTP/2 we store stream-local data here *************/
176 int32_t id; /* HTTP/2 protocol identifier for stream */
177 struct bufq recvbuf; /* response buffer */
178 struct bufq sendbuf; /* request buffer */
179 struct h1_req_parser h1; /* parsing the request */
180 struct dynhds resp_trailers; /* response trailer fields */
181 size_t resp_hds_len; /* amount of response header bytes in recvbuf */
182 size_t upload_blocked_len;
183 curl_off_t upload_left; /* number of request bytes left to upload */
184
185 char **push_headers; /* allocated array */
186 size_t push_headers_used; /* number of entries filled in */
187 size_t push_headers_alloc; /* number of entries allocated */
188
189 int status_code; /* HTTP response status code */
190 uint32_t error; /* stream error code */
191 uint32_t local_window_size; /* the local recv window size */
192 bool resp_hds_complete; /* we have a complete, final response */
193 bool closed; /* TRUE on stream close */
194 bool reset; /* TRUE on stream reset */
195 bool close_handled; /* TRUE if stream closure is handled by libcurl */
196 bool bodystarted;
197 bool send_closed; /* transfer is done sending, we might have still
198 buffered data in stream->sendbuf to upload. */
199 };
200
201 #define H2_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \
202 ((struct HTTP *)(d)->req.p.http)->h2_ctx \
203 : NULL))
204 #define H2_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h2_ctx
205 #define H2_STREAM_ID(d) (H2_STREAM_CTX(d)? \
206 H2_STREAM_CTX(d)->id : -2)
207
208 /*
209 * Mark this transfer to get "drained".
210 */
drain_stream(struct Curl_cfilter * cf,struct Curl_easy * data,struct stream_ctx * stream)211 static void drain_stream(struct Curl_cfilter *cf,
212 struct Curl_easy *data,
213 struct stream_ctx *stream)
214 {
215 unsigned char bits;
216
217 (void)cf;
218 bits = CURL_CSELECT_IN;
219 if(!stream->send_closed &&
220 (stream->upload_left || stream->upload_blocked_len))
221 bits |= CURL_CSELECT_OUT;
222 if(data->state.dselect_bits != bits) {
223 CURL_TRC_CF(data, cf, "[%d] DRAIN dselect_bits=%x",
224 stream->id, bits);
225 data->state.dselect_bits = bits;
226 Curl_expire(data, 0, EXPIRE_RUN_NOW);
227 }
228 }
229
http2_data_setup(struct Curl_cfilter * cf,struct Curl_easy * data,struct stream_ctx ** pstream)230 static CURLcode http2_data_setup(struct Curl_cfilter *cf,
231 struct Curl_easy *data,
232 struct stream_ctx **pstream)
233 {
234 struct cf_h2_ctx *ctx = cf->ctx;
235 struct stream_ctx *stream;
236
237 (void)cf;
238 DEBUGASSERT(data);
239 if(!data->req.p.http) {
240 failf(data, "initialization failure, transfer not http initialized");
241 return CURLE_FAILED_INIT;
242 }
243 stream = H2_STREAM_CTX(data);
244 if(stream) {
245 *pstream = stream;
246 return CURLE_OK;
247 }
248
249 stream = calloc(1, sizeof(*stream));
250 if(!stream)
251 return CURLE_OUT_OF_MEMORY;
252
253 stream->id = -1;
254 Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp,
255 H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
256 Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp,
257 H2_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
258 Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
259 Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST);
260 stream->resp_hds_len = 0;
261 stream->bodystarted = FALSE;
262 stream->status_code = -1;
263 stream->closed = FALSE;
264 stream->close_handled = FALSE;
265 stream->error = NGHTTP2_NO_ERROR;
266 stream->local_window_size = H2_STREAM_WINDOW_SIZE;
267 stream->upload_left = 0;
268
269 H2_STREAM_LCTX(data) = stream;
270 *pstream = stream;
271 return CURLE_OK;
272 }
273
http2_data_done(struct Curl_cfilter * cf,struct Curl_easy * data,bool premature)274 static void http2_data_done(struct Curl_cfilter *cf,
275 struct Curl_easy *data, bool premature)
276 {
277 struct cf_h2_ctx *ctx = cf->ctx;
278 struct stream_ctx *stream = H2_STREAM_CTX(data);
279
280 DEBUGASSERT(ctx);
281 (void)premature;
282 if(!stream)
283 return;
284
285 if(ctx->h2) {
286 bool flush_egress = FALSE;
287 /* returns error if stream not known, which is fine here */
288 (void)nghttp2_session_set_stream_user_data(ctx->h2, stream->id, NULL);
289
290 if(!stream->closed && stream->id > 0) {
291 /* RST_STREAM */
292 CURL_TRC_CF(data, cf, "[%d] premature DATA_DONE, RST stream",
293 stream->id);
294 stream->closed = TRUE;
295 stream->reset = TRUE;
296 stream->send_closed = TRUE;
297 nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
298 stream->id, NGHTTP2_STREAM_CLOSED);
299 flush_egress = TRUE;
300 }
301 if(!Curl_bufq_is_empty(&stream->recvbuf)) {
302 /* Anything in the recvbuf is still being counted
303 * in stream and connection window flow control. Need
304 * to free that space or the connection window might get
305 * exhausted eventually. */
306 nghttp2_session_consume(ctx->h2, stream->id,
307 Curl_bufq_len(&stream->recvbuf));
308 /* give WINDOW_UPATE a chance to be sent, but ignore any error */
309 flush_egress = TRUE;
310 }
311
312 if(flush_egress)
313 nghttp2_session_send(ctx->h2);
314 }
315
316 Curl_bufq_free(&stream->sendbuf);
317 Curl_bufq_free(&stream->recvbuf);
318 Curl_h1_req_parse_free(&stream->h1);
319 Curl_dynhds_free(&stream->resp_trailers);
320 if(stream->push_headers) {
321 /* if they weren't used and then freed before */
322 for(; stream->push_headers_used > 0; --stream->push_headers_used) {
323 free(stream->push_headers[stream->push_headers_used - 1]);
324 }
325 free(stream->push_headers);
326 stream->push_headers = NULL;
327 }
328
329 free(stream);
330 H2_STREAM_LCTX(data) = NULL;
331 }
332
h2_client_new(struct Curl_cfilter * cf,nghttp2_session_callbacks * cbs)333 static int h2_client_new(struct Curl_cfilter *cf,
334 nghttp2_session_callbacks *cbs)
335 {
336 struct cf_h2_ctx *ctx = cf->ctx;
337 nghttp2_option *o;
338
339 int rc = nghttp2_option_new(&o);
340 if(rc)
341 return rc;
342 /* We handle window updates ourself to enforce buffer limits */
343 nghttp2_option_set_no_auto_window_update(o, 1);
344 #if NGHTTP2_VERSION_NUM >= 0x013200
345 /* with 1.50.0 */
346 /* turn off RFC 9113 leading and trailing white spaces validation against
347 HTTP field value. */
348 nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1);
349 #endif
350 rc = nghttp2_session_client_new2(&ctx->h2, cbs, cf, o);
351 nghttp2_option_del(o);
352 return rc;
353 }
354
nw_in_reader(void * reader_ctx,unsigned char * buf,size_t buflen,CURLcode * err)355 static ssize_t nw_in_reader(void *reader_ctx,
356 unsigned char *buf, size_t buflen,
357 CURLcode *err)
358 {
359 struct Curl_cfilter *cf = reader_ctx;
360 struct Curl_easy *data = CF_DATA_CURRENT(cf);
361
362 return Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err);
363 }
364
nw_out_writer(void * writer_ctx,const unsigned char * buf,size_t buflen,CURLcode * err)365 static ssize_t nw_out_writer(void *writer_ctx,
366 const unsigned char *buf, size_t buflen,
367 CURLcode *err)
368 {
369 struct Curl_cfilter *cf = writer_ctx;
370 struct Curl_easy *data = CF_DATA_CURRENT(cf);
371 ssize_t nwritten;
372
373 nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, err);
374 if(nwritten > 0)
375 CURL_TRC_CF(data, cf, "[0] egress: wrote %zd bytes", nwritten);
376 return nwritten;
377 }
378
379 static ssize_t send_callback(nghttp2_session *h2,
380 const uint8_t *mem, size_t length, int flags,
381 void *userp);
382 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
383 void *userp);
384 #ifndef CURL_DISABLE_VERBOSE_STRINGS
385 static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame,
386 void *userp);
387 #endif
388 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
389 int32_t stream_id,
390 const uint8_t *mem, size_t len, void *userp);
391 static int on_stream_close(nghttp2_session *session, int32_t stream_id,
392 uint32_t error_code, void *userp);
393 static int on_begin_headers(nghttp2_session *session,
394 const nghttp2_frame *frame, void *userp);
395 static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
396 const uint8_t *name, size_t namelen,
397 const uint8_t *value, size_t valuelen,
398 uint8_t flags,
399 void *userp);
400 static int error_callback(nghttp2_session *session, const char *msg,
401 size_t len, void *userp);
402
403 /*
404 * Initialize the cfilter context
405 */
cf_h2_ctx_init(struct Curl_cfilter * cf,struct Curl_easy * data,bool via_h1_upgrade)406 static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
407 struct Curl_easy *data,
408 bool via_h1_upgrade)
409 {
410 struct cf_h2_ctx *ctx = cf->ctx;
411 struct stream_ctx *stream;
412 CURLcode result = CURLE_OUT_OF_MEMORY;
413 int rc;
414 nghttp2_session_callbacks *cbs = NULL;
415
416 DEBUGASSERT(!ctx->h2);
417 Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES);
418 Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0);
419 Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0);
420 ctx->last_stream_id = 2147483647;
421
422 rc = nghttp2_session_callbacks_new(&cbs);
423 if(rc) {
424 failf(data, "Couldn't initialize nghttp2 callbacks");
425 goto out;
426 }
427
428 nghttp2_session_callbacks_set_send_callback(cbs, send_callback);
429 nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv);
430 #ifndef CURL_DISABLE_VERBOSE_STRINGS
431 nghttp2_session_callbacks_set_on_frame_send_callback(cbs, on_frame_send);
432 #endif
433 nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
434 cbs, on_data_chunk_recv);
435 nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close);
436 nghttp2_session_callbacks_set_on_begin_headers_callback(
437 cbs, on_begin_headers);
438 nghttp2_session_callbacks_set_on_header_callback(cbs, on_header);
439 nghttp2_session_callbacks_set_error_callback(cbs, error_callback);
440
441 /* The nghttp2 session is not yet setup, do it */
442 rc = h2_client_new(cf, cbs);
443 if(rc) {
444 failf(data, "Couldn't initialize nghttp2");
445 goto out;
446 }
447 ctx->max_concurrent_streams = DEFAULT_MAX_CONCURRENT_STREAMS;
448
449 if(via_h1_upgrade) {
450 /* HTTP/1.1 Upgrade issued. H2 Settings have already been submitted
451 * in the H1 request and we upgrade from there. This stream
452 * is opened implicitly as #1. */
453 uint8_t binsettings[H2_BINSETTINGS_LEN];
454 size_t binlen; /* length of the binsettings data */
455
456 binlen = populate_binsettings(binsettings, data);
457
458 result = http2_data_setup(cf, data, &stream);
459 if(result)
460 goto out;
461 DEBUGASSERT(stream);
462 stream->id = 1;
463 /* queue SETTINGS frame (again) */
464 rc = nghttp2_session_upgrade2(ctx->h2, binsettings, binlen,
465 data->state.httpreq == HTTPREQ_HEAD,
466 NULL);
467 if(rc) {
468 failf(data, "nghttp2_session_upgrade2() failed: %s(%d)",
469 nghttp2_strerror(rc), rc);
470 result = CURLE_HTTP2;
471 goto out;
472 }
473
474 rc = nghttp2_session_set_stream_user_data(ctx->h2, stream->id,
475 data);
476 if(rc) {
477 infof(data, "http/2: failed to set user_data for stream %u",
478 stream->id);
479 DEBUGASSERT(0);
480 }
481 CURL_TRC_CF(data, cf, "created session via Upgrade");
482 }
483 else {
484 nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN];
485 int ivlen;
486
487 ivlen = populate_settings(iv, data);
488 rc = nghttp2_submit_settings(ctx->h2, NGHTTP2_FLAG_NONE,
489 iv, ivlen);
490 if(rc) {
491 failf(data, "nghttp2_submit_settings() failed: %s(%d)",
492 nghttp2_strerror(rc), rc);
493 result = CURLE_HTTP2;
494 goto out;
495 }
496 }
497
498 rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0,
499 HTTP2_HUGE_WINDOW_SIZE);
500 if(rc) {
501 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
502 nghttp2_strerror(rc), rc);
503 result = CURLE_HTTP2;
504 goto out;
505 }
506
507 /* all set, traffic will be send on connect */
508 result = CURLE_OK;
509 CURL_TRC_CF(data, cf, "[0] created h2 session%s",
510 via_h1_upgrade? " (via h1 upgrade)" : "");
511
512 out:
513 if(cbs)
514 nghttp2_session_callbacks_del(cbs);
515 return result;
516 }
517
518 /*
519 * Returns nonzero if current HTTP/2 session should be closed.
520 */
should_close_session(struct cf_h2_ctx * ctx)521 static int should_close_session(struct cf_h2_ctx *ctx)
522 {
523 return ctx->drain_total == 0 && !nghttp2_session_want_read(ctx->h2) &&
524 !nghttp2_session_want_write(ctx->h2);
525 }
526
527 /*
528 * Processes pending input left in network input buffer.
529 * This function returns 0 if it succeeds, or -1 and error code will
530 * be assigned to *err.
531 */
h2_process_pending_input(struct Curl_cfilter * cf,struct Curl_easy * data,CURLcode * err)532 static int h2_process_pending_input(struct Curl_cfilter *cf,
533 struct Curl_easy *data,
534 CURLcode *err)
535 {
536 struct cf_h2_ctx *ctx = cf->ctx;
537 const unsigned char *buf;
538 size_t blen;
539 ssize_t rv;
540
541 while(Curl_bufq_peek(&ctx->inbufq, &buf, &blen)) {
542
543 rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)buf, blen);
544 if(rv < 0) {
545 failf(data,
546 "process_pending_input: nghttp2_session_mem_recv() returned "
547 "%zd:%s", rv, nghttp2_strerror((int)rv));
548 *err = CURLE_RECV_ERROR;
549 return -1;
550 }
551 Curl_bufq_skip(&ctx->inbufq, (size_t)rv);
552 if(Curl_bufq_is_empty(&ctx->inbufq)) {
553 break;
554 }
555 else {
556 CURL_TRC_CF(data, cf, "process_pending_input: %zu bytes left "
557 "in connection buffer", Curl_bufq_len(&ctx->inbufq));
558 }
559 }
560
561 if(nghttp2_session_check_request_allowed(ctx->h2) == 0) {
562 /* No more requests are allowed in the current session, so
563 the connection may not be reused. This is set when a
564 GOAWAY frame has been received or when the limit of stream
565 identifiers has been reached. */
566 connclose(cf->conn, "http/2: No new requests allowed");
567 }
568
569 return 0;
570 }
571
572 /*
573 * The server may send us data at any point (e.g. PING frames). Therefore,
574 * we cannot assume that an HTTP/2 socket is dead just because it is readable.
575 *
576 * Check the lower filters first and, if successful, peek at the socket
577 * and distinguish between closed and data.
578 */
http2_connisalive(struct Curl_cfilter * cf,struct Curl_easy * data,bool * input_pending)579 static bool http2_connisalive(struct Curl_cfilter *cf, struct Curl_easy *data,
580 bool *input_pending)
581 {
582 struct cf_h2_ctx *ctx = cf->ctx;
583 bool alive = TRUE;
584
585 *input_pending = FALSE;
586 if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
587 return FALSE;
588
589 if(*input_pending) {
590 /* This happens before we've sent off a request and the connection is
591 not in use by any other transfer, there shouldn't be any data here,
592 only "protocol frames" */
593 CURLcode result;
594 ssize_t nread = -1;
595
596 *input_pending = FALSE;
597 nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result);
598 if(nread != -1) {
599 CURL_TRC_CF(data, cf, "%zd bytes stray data read before trying "
600 "h2 connection", nread);
601 if(h2_process_pending_input(cf, data, &result) < 0)
602 /* immediate error, considered dead */
603 alive = FALSE;
604 else {
605 alive = !should_close_session(ctx);
606 }
607 }
608 else if(result != CURLE_AGAIN) {
609 /* the read failed so let's say this is dead anyway */
610 alive = FALSE;
611 }
612 }
613
614 return alive;
615 }
616
http2_send_ping(struct Curl_cfilter * cf,struct Curl_easy * data)617 static CURLcode http2_send_ping(struct Curl_cfilter *cf,
618 struct Curl_easy *data)
619 {
620 struct cf_h2_ctx *ctx = cf->ctx;
621 int rc;
622
623 rc = nghttp2_submit_ping(ctx->h2, 0, ZERO_NULL);
624 if(rc) {
625 failf(data, "nghttp2_submit_ping() failed: %s(%d)",
626 nghttp2_strerror(rc), rc);
627 return CURLE_HTTP2;
628 }
629
630 rc = nghttp2_session_send(ctx->h2);
631 if(rc) {
632 failf(data, "nghttp2_session_send() failed: %s(%d)",
633 nghttp2_strerror(rc), rc);
634 return CURLE_SEND_ERROR;
635 }
636 return CURLE_OK;
637 }
638
639 /*
640 * Store nghttp2 version info in this buffer.
641 */
Curl_http2_ver(char * p,size_t len)642 void Curl_http2_ver(char *p, size_t len)
643 {
644 nghttp2_info *h2 = nghttp2_version(0);
645 (void)msnprintf(p, len, "nghttp2/%s", h2->version_str);
646 }
647
nw_out_flush(struct Curl_cfilter * cf,struct Curl_easy * data)648 static CURLcode nw_out_flush(struct Curl_cfilter *cf,
649 struct Curl_easy *data)
650 {
651 struct cf_h2_ctx *ctx = cf->ctx;
652 ssize_t nwritten;
653 CURLcode result;
654
655 (void)data;
656 if(Curl_bufq_is_empty(&ctx->outbufq))
657 return CURLE_OK;
658
659 nwritten = Curl_bufq_pass(&ctx->outbufq, nw_out_writer, cf, &result);
660 if(nwritten < 0) {
661 if(result == CURLE_AGAIN) {
662 CURL_TRC_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN",
663 Curl_bufq_len(&ctx->outbufq));
664 ctx->nw_out_blocked = 1;
665 }
666 return result;
667 }
668 return Curl_bufq_is_empty(&ctx->outbufq)? CURLE_OK: CURLE_AGAIN;
669 }
670
671 /*
672 * The implementation of nghttp2_send_callback type. Here we write |data| with
673 * size |length| to the network and return the number of bytes actually
674 * written. See the documentation of nghttp2_send_callback for the details.
675 */
send_callback(nghttp2_session * h2,const uint8_t * buf,size_t blen,int flags,void * userp)676 static ssize_t send_callback(nghttp2_session *h2,
677 const uint8_t *buf, size_t blen, int flags,
678 void *userp)
679 {
680 struct Curl_cfilter *cf = userp;
681 struct cf_h2_ctx *ctx = cf->ctx;
682 struct Curl_easy *data = CF_DATA_CURRENT(cf);
683 ssize_t nwritten;
684 CURLcode result = CURLE_OK;
685
686 (void)h2;
687 (void)flags;
688 DEBUGASSERT(data);
689
690 nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen,
691 nw_out_writer, cf, &result);
692 if(nwritten < 0) {
693 if(result == CURLE_AGAIN) {
694 ctx->nw_out_blocked = 1;
695 return NGHTTP2_ERR_WOULDBLOCK;
696 }
697 failf(data, "Failed sending HTTP2 data");
698 return NGHTTP2_ERR_CALLBACK_FAILURE;
699 }
700
701 if(!nwritten) {
702 ctx->nw_out_blocked = 1;
703 return NGHTTP2_ERR_WOULDBLOCK;
704 }
705 return nwritten;
706 }
707
708
709 /* We pass a pointer to this struct in the push callback, but the contents of
710 the struct are hidden from the user. */
711 struct curl_pushheaders {
712 struct Curl_easy *data;
713 const nghttp2_push_promise *frame;
714 };
715
716 /*
717 * push header access function. Only to be used from within the push callback
718 */
curl_pushheader_bynum(struct curl_pushheaders * h,size_t num)719 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
720 {
721 /* Verify that we got a good easy handle in the push header struct, mostly to
722 detect rubbish input fast(er). */
723 if(!h || !GOOD_EASY_HANDLE(h->data))
724 return NULL;
725 else {
726 struct stream_ctx *stream = H2_STREAM_CTX(h->data);
727 if(stream && num < stream->push_headers_used)
728 return stream->push_headers[num];
729 }
730 return NULL;
731 }
732
733 /*
734 * push header access function. Only to be used from within the push callback
735 */
curl_pushheader_byname(struct curl_pushheaders * h,const char * header)736 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
737 {
738 struct stream_ctx *stream;
739 size_t len;
740 size_t i;
741 /* Verify that we got a good easy handle in the push header struct,
742 mostly to detect rubbish input fast(er). Also empty header name
743 is just a rubbish too. We have to allow ":" at the beginning of
744 the header, but header == ":" must be rejected. If we have ':' in
745 the middle of header, it could be matched in middle of the value,
746 this is because we do prefix match.*/
747 if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] ||
748 !strcmp(header, ":") || strchr(header + 1, ':'))
749 return NULL;
750
751 stream = H2_STREAM_CTX(h->data);
752 if(!stream)
753 return NULL;
754
755 len = strlen(header);
756 for(i = 0; i<stream->push_headers_used; i++) {
757 if(!strncmp(header, stream->push_headers[i], len)) {
758 /* sub-match, make sure that it is followed by a colon */
759 if(stream->push_headers[i][len] != ':')
760 continue;
761 return &stream->push_headers[i][len + 1];
762 }
763 }
764 return NULL;
765 }
766
h2_duphandle(struct Curl_cfilter * cf,struct Curl_easy * data)767 static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf,
768 struct Curl_easy *data)
769 {
770 struct Curl_easy *second = curl_easy_duphandle(data);
771 if(second) {
772 /* setup the request struct */
773 struct HTTP *http = calloc(1, sizeof(struct HTTP));
774 if(!http) {
775 (void)Curl_close(&second);
776 }
777 else {
778 struct stream_ctx *second_stream;
779
780 second->req.p.http = http;
781 http2_data_setup(cf, second, &second_stream);
782 second->state.priority.weight = data->state.priority.weight;
783 }
784 }
785 return second;
786 }
787
set_transfer_url(struct Curl_easy * data,struct curl_pushheaders * hp)788 static int set_transfer_url(struct Curl_easy *data,
789 struct curl_pushheaders *hp)
790 {
791 const char *v;
792 CURLUcode uc;
793 char *url = NULL;
794 int rc = 0;
795 CURLU *u = curl_url();
796
797 if(!u)
798 return 5;
799
800 v = curl_pushheader_byname(hp, HTTP_PSEUDO_SCHEME);
801 if(v) {
802 uc = curl_url_set(u, CURLUPART_SCHEME, v, 0);
803 if(uc) {
804 rc = 1;
805 goto fail;
806 }
807 }
808
809 v = curl_pushheader_byname(hp, HTTP_PSEUDO_AUTHORITY);
810 if(v) {
811 uc = Curl_url_set_authority(u, v, CURLU_DISALLOW_USER);
812 if(uc) {
813 rc = 2;
814 goto fail;
815 }
816 }
817
818 v = curl_pushheader_byname(hp, HTTP_PSEUDO_PATH);
819 if(v) {
820 uc = curl_url_set(u, CURLUPART_PATH, v, 0);
821 if(uc) {
822 rc = 3;
823 goto fail;
824 }
825 }
826
827 uc = curl_url_get(u, CURLUPART_URL, &url, 0);
828 if(uc)
829 rc = 4;
830 fail:
831 curl_url_cleanup(u);
832 if(rc)
833 return rc;
834
835 if(data->state.url_alloc)
836 free(data->state.url);
837 data->state.url_alloc = TRUE;
838 data->state.url = url;
839 return 0;
840 }
841
discard_newhandle(struct Curl_cfilter * cf,struct Curl_easy * newhandle)842 static void discard_newhandle(struct Curl_cfilter *cf,
843 struct Curl_easy *newhandle)
844 {
845 if(!newhandle->req.p.http) {
846 http2_data_done(cf, newhandle, TRUE);
847 newhandle->req.p.http = NULL;
848 }
849 (void)Curl_close(&newhandle);
850 }
851
push_promise(struct Curl_cfilter * cf,struct Curl_easy * data,const nghttp2_push_promise * frame)852 static int push_promise(struct Curl_cfilter *cf,
853 struct Curl_easy *data,
854 const nghttp2_push_promise *frame)
855 {
856 struct cf_h2_ctx *ctx = cf->ctx;
857 int rv; /* one of the CURL_PUSH_* defines */
858
859 CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE received",
860 frame->promised_stream_id);
861 if(data->multi->push_cb) {
862 struct stream_ctx *stream;
863 struct stream_ctx *newstream;
864 struct curl_pushheaders heads;
865 CURLMcode rc;
866 CURLcode result;
867 size_t i;
868 /* clone the parent */
869 struct Curl_easy *newhandle = h2_duphandle(cf, data);
870 if(!newhandle) {
871 infof(data, "failed to duplicate handle");
872 rv = CURL_PUSH_DENY; /* FAIL HARD */
873 goto fail;
874 }
875
876 heads.data = data;
877 heads.frame = frame;
878 /* ask the application */
879 CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ask application");
880
881 stream = H2_STREAM_CTX(data);
882 if(!stream) {
883 failf(data, "Internal NULL stream");
884 discard_newhandle(cf, newhandle);
885 rv = CURL_PUSH_DENY;
886 goto fail;
887 }
888
889 rv = set_transfer_url(newhandle, &heads);
890 if(rv) {
891 discard_newhandle(cf, newhandle);
892 rv = CURL_PUSH_DENY;
893 goto fail;
894 }
895
896 result = http2_data_setup(cf, newhandle, &newstream);
897 if(result) {
898 failf(data, "error setting up stream: %d", result);
899 discard_newhandle(cf, newhandle);
900 rv = CURL_PUSH_DENY;
901 goto fail;
902 }
903 DEBUGASSERT(stream);
904
905 Curl_set_in_callback(data, true);
906 rv = data->multi->push_cb(data, newhandle,
907 stream->push_headers_used, &heads,
908 data->multi->push_userp);
909 Curl_set_in_callback(data, false);
910
911 /* free the headers again */
912 for(i = 0; i<stream->push_headers_used; i++)
913 free(stream->push_headers[i]);
914 free(stream->push_headers);
915 stream->push_headers = NULL;
916 stream->push_headers_used = 0;
917
918 if(rv) {
919 DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
920 /* denied, kill off the new handle again */
921 discard_newhandle(cf, newhandle);
922 goto fail;
923 }
924
925 newstream->id = frame->promised_stream_id;
926 newhandle->req.maxdownload = -1;
927 newhandle->req.size = -1;
928
929 /* approved, add to the multi handle and immediately switch to PERFORM
930 state with the given connection !*/
931 rc = Curl_multi_add_perform(data->multi, newhandle, cf->conn);
932 if(rc) {
933 infof(data, "failed to add handle to multi");
934 discard_newhandle(cf, newhandle);
935 rv = CURL_PUSH_DENY;
936 goto fail;
937 }
938
939 rv = nghttp2_session_set_stream_user_data(ctx->h2,
940 newstream->id,
941 newhandle);
942 if(rv) {
943 infof(data, "failed to set user_data for stream %u",
944 newstream->id);
945 DEBUGASSERT(0);
946 rv = CURL_PUSH_DENY;
947 goto fail;
948 }
949 }
950 else {
951 CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ignore it");
952 rv = CURL_PUSH_DENY;
953 }
954 fail:
955 return rv;
956 }
957
recvbuf_write_hds(struct Curl_cfilter * cf,struct Curl_easy * data,const char * buf,size_t blen)958 static CURLcode recvbuf_write_hds(struct Curl_cfilter *cf,
959 struct Curl_easy *data,
960 const char *buf, size_t blen)
961 {
962 struct stream_ctx *stream = H2_STREAM_CTX(data);
963 ssize_t nwritten;
964 CURLcode result;
965
966 (void)cf;
967 nwritten = Curl_bufq_write(&stream->recvbuf,
968 (const unsigned char *)buf, blen, &result);
969 if(nwritten < 0)
970 return result;
971 stream->resp_hds_len += (size_t)nwritten;
972 DEBUGASSERT((size_t)nwritten == blen);
973 return CURLE_OK;
974 }
975
on_stream_frame(struct Curl_cfilter * cf,struct Curl_easy * data,const nghttp2_frame * frame)976 static CURLcode on_stream_frame(struct Curl_cfilter *cf,
977 struct Curl_easy *data,
978 const nghttp2_frame *frame)
979 {
980 struct cf_h2_ctx *ctx = cf->ctx;
981 struct stream_ctx *stream = H2_STREAM_CTX(data);
982 int32_t stream_id = frame->hd.stream_id;
983 CURLcode result;
984 size_t rbuflen;
985 int rv;
986
987 if(!stream) {
988 CURL_TRC_CF(data, cf, "[%d] No stream_ctx set", stream_id);
989 return CURLE_FAILED_INIT;
990 }
991
992 switch(frame->hd.type) {
993 case NGHTTP2_DATA:
994 rbuflen = Curl_bufq_len(&stream->recvbuf);
995 CURL_TRC_CF(data, cf, "[%d] DATA, buffered=%zu, window=%d/%d",
996 stream_id, rbuflen,
997 nghttp2_session_get_stream_effective_recv_data_length(
998 ctx->h2, stream->id),
999 nghttp2_session_get_stream_effective_local_window_size(
1000 ctx->h2, stream->id));
1001 /* If !body started on this stream, then receiving DATA is illegal. */
1002 if(!stream->bodystarted) {
1003 rv = nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
1004 stream_id, NGHTTP2_PROTOCOL_ERROR);
1005
1006 if(nghttp2_is_fatal(rv)) {
1007 return CURLE_RECV_ERROR;
1008 }
1009 }
1010 if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
1011 drain_stream(cf, data, stream);
1012 }
1013 else if(rbuflen > stream->local_window_size) {
1014 int32_t wsize = nghttp2_session_get_stream_local_window_size(
1015 ctx->h2, stream->id);
1016 if(wsize > 0 && (uint32_t)wsize != stream->local_window_size) {
1017 /* H2 flow control is not absolute, as the server might not have the
1018 * same view, yet. When we receive more than we want, we enforce
1019 * the local window size again to make nghttp2 send WINDOW_UPATEs
1020 * accordingly. */
1021 nghttp2_session_set_local_window_size(ctx->h2,
1022 NGHTTP2_FLAG_NONE,
1023 stream->id,
1024 stream->local_window_size);
1025 }
1026 }
1027 break;
1028 case NGHTTP2_HEADERS:
1029 if(stream->bodystarted) {
1030 /* Only valid HEADERS after body started is trailer HEADERS. We
1031 buffer them in on_header callback. */
1032 break;
1033 }
1034
1035 /* nghttp2 guarantees that :status is received, and we store it to
1036 stream->status_code. Fuzzing has proven this can still be reached
1037 without status code having been set. */
1038 if(stream->status_code == -1)
1039 return CURLE_RECV_ERROR;
1040
1041 /* Only final status code signals the end of header */
1042 if(stream->status_code / 100 != 1) {
1043 stream->bodystarted = TRUE;
1044 stream->status_code = -1;
1045 }
1046
1047 result = recvbuf_write_hds(cf, data, STRCONST("\r\n"));
1048 if(result)
1049 return result;
1050
1051 if(stream->status_code / 100 != 1) {
1052 stream->resp_hds_complete = TRUE;
1053 }
1054 drain_stream(cf, data, stream);
1055 break;
1056 case NGHTTP2_PUSH_PROMISE:
1057 rv = push_promise(cf, data, &frame->push_promise);
1058 if(rv) { /* deny! */
1059 DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
1060 rv = nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
1061 frame->push_promise.promised_stream_id,
1062 NGHTTP2_CANCEL);
1063 if(nghttp2_is_fatal(rv))
1064 return CURLE_SEND_ERROR;
1065 else if(rv == CURL_PUSH_ERROROUT) {
1066 CURL_TRC_CF(data, cf, "[%d] fail in PUSH_PROMISE received",
1067 stream_id);
1068 return CURLE_RECV_ERROR;
1069 }
1070 }
1071 break;
1072 case NGHTTP2_RST_STREAM:
1073 stream->closed = TRUE;
1074 if(frame->rst_stream.error_code) {
1075 stream->reset = TRUE;
1076 }
1077 stream->send_closed = TRUE;
1078 data->req.keepon &= ~KEEP_SEND_HOLD;
1079 drain_stream(cf, data, stream);
1080 break;
1081 case NGHTTP2_WINDOW_UPDATE:
1082 if((data->req.keepon & KEEP_SEND_HOLD) &&
1083 (data->req.keepon & KEEP_SEND)) {
1084 data->req.keepon &= ~KEEP_SEND_HOLD;
1085 drain_stream(cf, data, stream);
1086 CURL_TRC_CF(data, cf, "[%d] un-holding after win update",
1087 stream_id);
1088 }
1089 break;
1090 default:
1091 break;
1092 }
1093 return CURLE_OK;
1094 }
1095
1096 #ifndef CURL_DISABLE_VERBOSE_STRINGS
fr_print(const nghttp2_frame * frame,char * buffer,size_t blen)1097 static int fr_print(const nghttp2_frame *frame, char *buffer, size_t blen)
1098 {
1099 switch(frame->hd.type) {
1100 case NGHTTP2_DATA: {
1101 return msnprintf(buffer, blen,
1102 "FRAME[DATA, len=%d, eos=%d, padlen=%d]",
1103 (int)frame->hd.length,
1104 !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM),
1105 (int)frame->data.padlen);
1106 }
1107 case NGHTTP2_HEADERS: {
1108 return msnprintf(buffer, blen,
1109 "FRAME[HEADERS, len=%d, hend=%d, eos=%d]",
1110 (int)frame->hd.length,
1111 !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
1112 !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM));
1113 }
1114 case NGHTTP2_PRIORITY: {
1115 return msnprintf(buffer, blen,
1116 "FRAME[PRIORITY, len=%d, flags=%d]",
1117 (int)frame->hd.length, frame->hd.flags);
1118 }
1119 case NGHTTP2_RST_STREAM: {
1120 return msnprintf(buffer, blen,
1121 "FRAME[RST_STREAM, len=%d, flags=%d, error=%u]",
1122 (int)frame->hd.length, frame->hd.flags,
1123 frame->rst_stream.error_code);
1124 }
1125 case NGHTTP2_SETTINGS: {
1126 if(frame->hd.flags & NGHTTP2_FLAG_ACK) {
1127 return msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]");
1128 }
1129 return msnprintf(buffer, blen,
1130 "FRAME[SETTINGS, len=%d]", (int)frame->hd.length);
1131 }
1132 case NGHTTP2_PUSH_PROMISE: {
1133 return msnprintf(buffer, blen,
1134 "FRAME[PUSH_PROMISE, len=%d, hend=%d]",
1135 (int)frame->hd.length,
1136 !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS));
1137 }
1138 case NGHTTP2_PING: {
1139 return msnprintf(buffer, blen,
1140 "FRAME[PING, len=%d, ack=%d]",
1141 (int)frame->hd.length,
1142 frame->hd.flags&NGHTTP2_FLAG_ACK);
1143 }
1144 case NGHTTP2_GOAWAY: {
1145 char scratch[128];
1146 size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
1147 size_t len = (frame->goaway.opaque_data_len < s_len)?
1148 frame->goaway.opaque_data_len : s_len-1;
1149 if(len)
1150 memcpy(scratch, frame->goaway.opaque_data, len);
1151 scratch[len] = '\0';
1152 return msnprintf(buffer, blen, "FRAME[GOAWAY, error=%d, reason='%s', "
1153 "last_stream=%d]", frame->goaway.error_code,
1154 scratch, frame->goaway.last_stream_id);
1155 }
1156 case NGHTTP2_WINDOW_UPDATE: {
1157 return msnprintf(buffer, blen,
1158 "FRAME[WINDOW_UPDATE, incr=%d]",
1159 frame->window_update.window_size_increment);
1160 }
1161 default:
1162 return msnprintf(buffer, blen, "FRAME[%d, len=%d, flags=%d]",
1163 frame->hd.type, (int)frame->hd.length,
1164 frame->hd.flags);
1165 }
1166 }
1167
on_frame_send(nghttp2_session * session,const nghttp2_frame * frame,void * userp)1168 static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame,
1169 void *userp)
1170 {
1171 struct Curl_cfilter *cf = userp;
1172 struct Curl_easy *data = CF_DATA_CURRENT(cf);
1173
1174 (void)session;
1175 DEBUGASSERT(data);
1176 if(data && Curl_trc_cf_is_verbose(cf, data)) {
1177 char buffer[256];
1178 int len;
1179 len = fr_print(frame, buffer, sizeof(buffer)-1);
1180 buffer[len] = 0;
1181 CURL_TRC_CF(data, cf, "[%d] -> %s", frame->hd.stream_id, buffer);
1182 }
1183 return 0;
1184 }
1185 #endif /* !CURL_DISABLE_VERBOSE_STRINGS */
1186
on_frame_recv(nghttp2_session * session,const nghttp2_frame * frame,void * userp)1187 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
1188 void *userp)
1189 {
1190 struct Curl_cfilter *cf = userp;
1191 struct cf_h2_ctx *ctx = cf->ctx;
1192 struct Curl_easy *data = CF_DATA_CURRENT(cf), *data_s;
1193 int32_t stream_id = frame->hd.stream_id;
1194
1195 DEBUGASSERT(data);
1196 #ifndef CURL_DISABLE_VERBOSE_STRINGS
1197 if(Curl_trc_cf_is_verbose(cf, data)) {
1198 char buffer[256];
1199 int len;
1200 len = fr_print(frame, buffer, sizeof(buffer)-1);
1201 buffer[len] = 0;
1202 CURL_TRC_CF(data, cf, "[%d] <- %s",frame->hd.stream_id, buffer);
1203 }
1204 #endif /* !CURL_DISABLE_VERBOSE_STRINGS */
1205
1206 if(!stream_id) {
1207 /* stream ID zero is for connection-oriented stuff */
1208 DEBUGASSERT(data);
1209 switch(frame->hd.type) {
1210 case NGHTTP2_SETTINGS: {
1211 if(!(frame->hd.flags & NGHTTP2_FLAG_ACK)) {
1212 uint32_t max_conn = ctx->max_concurrent_streams;
1213 ctx->max_concurrent_streams = nghttp2_session_get_remote_settings(
1214 session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
1215 ctx->enable_push = nghttp2_session_get_remote_settings(
1216 session, NGHTTP2_SETTINGS_ENABLE_PUSH) != 0;
1217 CURL_TRC_CF(data, cf, "[0] MAX_CONCURRENT_STREAMS: %d",
1218 ctx->max_concurrent_streams);
1219 CURL_TRC_CF(data, cf, "[0] ENABLE_PUSH: %s",
1220 ctx->enable_push ? "TRUE" : "false");
1221 if(data && max_conn != ctx->max_concurrent_streams) {
1222 /* only signal change if the value actually changed */
1223 CURL_TRC_CF(data, cf, "[0] notify MAX_CONCURRENT_STREAMS: %u",
1224 ctx->max_concurrent_streams);
1225 Curl_multi_connchanged(data->multi);
1226 }
1227 /* Since the initial stream window is 64K, a request might be on HOLD,
1228 * due to exhaustion. The (initial) SETTINGS may announce a much larger
1229 * window and *assume* that we treat this like a WINDOW_UPDATE. Some
1230 * servers send an explicit WINDOW_UPDATE, but not all seem to do that.
1231 * To be safe, we UNHOLD a stream in order not to stall. */
1232 if((data->req.keepon & KEEP_SEND_HOLD) &&
1233 (data->req.keepon & KEEP_SEND)) {
1234 struct stream_ctx *stream = H2_STREAM_CTX(data);
1235 data->req.keepon &= ~KEEP_SEND_HOLD;
1236 if(stream) {
1237 drain_stream(cf, data, stream);
1238 CURL_TRC_CF(data, cf, "[%d] un-holding after SETTINGS",
1239 stream_id);
1240 }
1241 }
1242 }
1243 break;
1244 }
1245 case NGHTTP2_GOAWAY:
1246 ctx->goaway = TRUE;
1247 ctx->goaway_error = frame->goaway.error_code;
1248 ctx->last_stream_id = frame->goaway.last_stream_id;
1249 if(data) {
1250 infof(data, "received GOAWAY, error=%d, last_stream=%u",
1251 ctx->goaway_error, ctx->last_stream_id);
1252 Curl_multi_connchanged(data->multi);
1253 }
1254 break;
1255 default:
1256 break;
1257 }
1258 return 0;
1259 }
1260
1261 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
1262 if(!data_s) {
1263 CURL_TRC_CF(data, cf, "[%d] No Curl_easy associated", stream_id);
1264 return 0;
1265 }
1266
1267 return on_stream_frame(cf, data_s, frame)? NGHTTP2_ERR_CALLBACK_FAILURE : 0;
1268 }
1269
on_data_chunk_recv(nghttp2_session * session,uint8_t flags,int32_t stream_id,const uint8_t * mem,size_t len,void * userp)1270 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
1271 int32_t stream_id,
1272 const uint8_t *mem, size_t len, void *userp)
1273 {
1274 struct Curl_cfilter *cf = userp;
1275 struct stream_ctx *stream;
1276 struct Curl_easy *data_s;
1277 ssize_t nwritten;
1278 CURLcode result;
1279 (void)flags;
1280
1281 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
1282 DEBUGASSERT(CF_DATA_CURRENT(cf));
1283
1284 /* get the stream from the hash based on Stream ID */
1285 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
1286 if(!data_s) {
1287 /* Receiving a Stream ID not in the hash should not happen - unless
1288 we have aborted a transfer artificially and there were more data
1289 in the pipeline. Silently ignore. */
1290 CURL_TRC_CF(CF_DATA_CURRENT(cf), cf, "[%d] Data for unknown",
1291 stream_id);
1292 /* consumed explicitly as no one will read it */
1293 nghttp2_session_consume(session, stream_id, len);
1294 return 0;
1295 }
1296
1297 stream = H2_STREAM_CTX(data_s);
1298 if(!stream)
1299 return NGHTTP2_ERR_CALLBACK_FAILURE;
1300
1301 nwritten = Curl_bufq_write(&stream->recvbuf, mem, len, &result);
1302 if(nwritten < 0) {
1303 if(result != CURLE_AGAIN)
1304 return NGHTTP2_ERR_CALLBACK_FAILURE;
1305
1306 nwritten = 0;
1307 }
1308
1309 /* if we receive data for another handle, wake that up */
1310 drain_stream(cf, data_s, stream);
1311
1312 DEBUGASSERT((size_t)nwritten == len);
1313 return 0;
1314 }
1315
on_stream_close(nghttp2_session * session,int32_t stream_id,uint32_t error_code,void * userp)1316 static int on_stream_close(nghttp2_session *session, int32_t stream_id,
1317 uint32_t error_code, void *userp)
1318 {
1319 struct Curl_cfilter *cf = userp;
1320 struct Curl_easy *data_s, *call_data = CF_DATA_CURRENT(cf);
1321 struct stream_ctx *stream;
1322 int rv;
1323 (void)session;
1324
1325 DEBUGASSERT(call_data);
1326 /* get the stream from the hash based on Stream ID, stream ID zero is for
1327 connection-oriented stuff */
1328 data_s = stream_id?
1329 nghttp2_session_get_stream_user_data(session, stream_id) : NULL;
1330 if(!data_s) {
1331 CURL_TRC_CF(call_data, cf,
1332 "[%d] on_stream_close, no easy set on stream", stream_id);
1333 return 0;
1334 }
1335 if(!GOOD_EASY_HANDLE(data_s)) {
1336 /* nghttp2 still has an easy registered for the stream which has
1337 * been freed be libcurl. This points to a code path that does not
1338 * trigger DONE or DETACH events as it must. */
1339 CURL_TRC_CF(call_data, cf,
1340 "[%d] on_stream_close, not a GOOD easy on stream", stream_id);
1341 (void)nghttp2_session_set_stream_user_data(session, stream_id, 0);
1342 return NGHTTP2_ERR_CALLBACK_FAILURE;
1343 }
1344 stream = H2_STREAM_CTX(data_s);
1345 if(!stream) {
1346 CURL_TRC_CF(data_s, cf,
1347 "[%d] on_stream_close, GOOD easy but no stream", stream_id);
1348 return NGHTTP2_ERR_CALLBACK_FAILURE;
1349 }
1350
1351 stream->closed = TRUE;
1352 stream->error = error_code;
1353 if(stream->error) {
1354 stream->reset = TRUE;
1355 stream->send_closed = TRUE;
1356 }
1357 data_s->req.keepon &= ~KEEP_SEND_HOLD;
1358
1359 if(stream->error)
1360 CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %d)",
1361 stream_id, nghttp2_http2_strerror(error_code), error_code);
1362 else
1363 CURL_TRC_CF(data_s, cf, "[%d] CLOSED", stream_id);
1364 drain_stream(cf, data_s, stream);
1365
1366 /* remove `data_s` from the nghttp2 stream */
1367 rv = nghttp2_session_set_stream_user_data(session, stream_id, 0);
1368 if(rv) {
1369 infof(data_s, "http/2: failed to clear user_data for stream %u",
1370 stream_id);
1371 DEBUGASSERT(0);
1372 }
1373 return 0;
1374 }
1375
on_begin_headers(nghttp2_session * session,const nghttp2_frame * frame,void * userp)1376 static int on_begin_headers(nghttp2_session *session,
1377 const nghttp2_frame *frame, void *userp)
1378 {
1379 struct Curl_cfilter *cf = userp;
1380 struct stream_ctx *stream;
1381 struct Curl_easy *data_s = NULL;
1382
1383 (void)cf;
1384 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
1385 if(!data_s) {
1386 return 0;
1387 }
1388
1389 if(frame->hd.type != NGHTTP2_HEADERS) {
1390 return 0;
1391 }
1392
1393 stream = H2_STREAM_CTX(data_s);
1394 if(!stream || !stream->bodystarted) {
1395 return 0;
1396 }
1397
1398 return 0;
1399 }
1400
1401 /* 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)1402 static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
1403 const uint8_t *name, size_t namelen,
1404 const uint8_t *value, size_t valuelen,
1405 uint8_t flags,
1406 void *userp)
1407 {
1408 struct Curl_cfilter *cf = userp;
1409 struct stream_ctx *stream;
1410 struct Curl_easy *data_s;
1411 int32_t stream_id = frame->hd.stream_id;
1412 CURLcode result;
1413 (void)flags;
1414
1415 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
1416
1417 /* get the stream from the hash based on Stream ID */
1418 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
1419 if(!data_s)
1420 /* Receiving a Stream ID not in the hash should not happen, this is an
1421 internal error more than anything else! */
1422 return NGHTTP2_ERR_CALLBACK_FAILURE;
1423
1424 stream = H2_STREAM_CTX(data_s);
1425 if(!stream) {
1426 failf(data_s, "Internal NULL stream");
1427 return NGHTTP2_ERR_CALLBACK_FAILURE;
1428 }
1429
1430 /* Store received PUSH_PROMISE headers to be used when the subsequent
1431 PUSH_PROMISE callback comes */
1432 if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
1433 char *h;
1434
1435 if(!strcmp(HTTP_PSEUDO_AUTHORITY, (const char *)name)) {
1436 /* pseudo headers are lower case */
1437 int rc = 0;
1438 char *check = aprintf("%s:%d", cf->conn->host.name,
1439 cf->conn->remote_port);
1440 if(!check)
1441 /* no memory */
1442 return NGHTTP2_ERR_CALLBACK_FAILURE;
1443 if(!strcasecompare(check, (const char *)value) &&
1444 ((cf->conn->remote_port != cf->conn->given->defport) ||
1445 !strcasecompare(cf->conn->host.name, (const char *)value))) {
1446 /* This is push is not for the same authority that was asked for in
1447 * the URL. RFC 7540 section 8.2 says: "A client MUST treat a
1448 * PUSH_PROMISE for which the server is not authoritative as a stream
1449 * error of type PROTOCOL_ERROR."
1450 */
1451 (void)nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
1452 stream_id, NGHTTP2_PROTOCOL_ERROR);
1453 rc = NGHTTP2_ERR_CALLBACK_FAILURE;
1454 }
1455 free(check);
1456 if(rc)
1457 return rc;
1458 }
1459
1460 if(!stream->push_headers) {
1461 stream->push_headers_alloc = 10;
1462 stream->push_headers = malloc(stream->push_headers_alloc *
1463 sizeof(char *));
1464 if(!stream->push_headers)
1465 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1466 stream->push_headers_used = 0;
1467 }
1468 else if(stream->push_headers_used ==
1469 stream->push_headers_alloc) {
1470 char **headp;
1471 if(stream->push_headers_alloc > 1000) {
1472 /* this is beyond crazy many headers, bail out */
1473 failf(data_s, "Too many PUSH_PROMISE headers");
1474 Curl_safefree(stream->push_headers);
1475 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1476 }
1477 stream->push_headers_alloc *= 2;
1478 headp = Curl_saferealloc(stream->push_headers,
1479 stream->push_headers_alloc * sizeof(char *));
1480 if(!headp) {
1481 stream->push_headers = NULL;
1482 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1483 }
1484 stream->push_headers = headp;
1485 }
1486 h = aprintf("%s:%s", name, value);
1487 if(h)
1488 stream->push_headers[stream->push_headers_used++] = h;
1489 return 0;
1490 }
1491
1492 if(stream->bodystarted) {
1493 /* This is a trailer */
1494 CURL_TRC_CF(data_s, cf, "[%d] trailer: %.*s: %.*s",
1495 stream->id, (int)namelen, name, (int)valuelen, value);
1496 result = Curl_dynhds_add(&stream->resp_trailers,
1497 (const char *)name, namelen,
1498 (const char *)value, valuelen);
1499 if(result)
1500 return NGHTTP2_ERR_CALLBACK_FAILURE;
1501
1502 return 0;
1503 }
1504
1505 if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 &&
1506 memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) {
1507 /* nghttp2 guarantees :status is received first and only once. */
1508 char buffer[32];
1509 result = Curl_http_decode_status(&stream->status_code,
1510 (const char *)value, valuelen);
1511 if(result)
1512 return NGHTTP2_ERR_CALLBACK_FAILURE;
1513 msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%u\r",
1514 stream->status_code);
1515 result = Curl_headers_push(data_s, buffer, CURLH_PSEUDO);
1516 if(result)
1517 return NGHTTP2_ERR_CALLBACK_FAILURE;
1518 result = recvbuf_write_hds(cf, data_s, STRCONST("HTTP/2 "));
1519 if(result)
1520 return NGHTTP2_ERR_CALLBACK_FAILURE;
1521 result = recvbuf_write_hds(cf, data_s, (const char *)value, valuelen);
1522 if(result)
1523 return NGHTTP2_ERR_CALLBACK_FAILURE;
1524 /* the space character after the status code is mandatory */
1525 result = recvbuf_write_hds(cf, data_s, STRCONST(" \r\n"));
1526 if(result)
1527 return NGHTTP2_ERR_CALLBACK_FAILURE;
1528 /* if we receive data for another handle, wake that up */
1529 if(CF_DATA_CURRENT(cf) != data_s)
1530 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
1531
1532 CURL_TRC_CF(data_s, cf, "[%d] status: HTTP/2 %03d",
1533 stream->id, stream->status_code);
1534 return 0;
1535 }
1536
1537 /* nghttp2 guarantees that namelen > 0, and :status was already
1538 received, and this is not pseudo-header field . */
1539 /* convert to an HTTP1-style header */
1540 result = recvbuf_write_hds(cf, data_s, (const char *)name, namelen);
1541 if(result)
1542 return NGHTTP2_ERR_CALLBACK_FAILURE;
1543 result = recvbuf_write_hds(cf, data_s, STRCONST(": "));
1544 if(result)
1545 return NGHTTP2_ERR_CALLBACK_FAILURE;
1546 result = recvbuf_write_hds(cf, data_s, (const char *)value, valuelen);
1547 if(result)
1548 return NGHTTP2_ERR_CALLBACK_FAILURE;
1549 result = recvbuf_write_hds(cf, data_s, STRCONST("\r\n"));
1550 if(result)
1551 return NGHTTP2_ERR_CALLBACK_FAILURE;
1552 /* if we receive data for another handle, wake that up */
1553 if(CF_DATA_CURRENT(cf) != data_s)
1554 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
1555
1556 CURL_TRC_CF(data_s, cf, "[%d] header: %.*s: %.*s",
1557 stream->id, (int)namelen, name, (int)valuelen, value);
1558
1559 return 0; /* 0 is successful */
1560 }
1561
req_body_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)1562 static ssize_t req_body_read_callback(nghttp2_session *session,
1563 int32_t stream_id,
1564 uint8_t *buf, size_t length,
1565 uint32_t *data_flags,
1566 nghttp2_data_source *source,
1567 void *userp)
1568 {
1569 struct Curl_cfilter *cf = userp;
1570 struct Curl_easy *data_s;
1571 struct stream_ctx *stream = NULL;
1572 CURLcode result;
1573 ssize_t nread;
1574 (void)source;
1575
1576 (void)cf;
1577 if(stream_id) {
1578 /* get the stream from the hash based on Stream ID, stream ID zero is for
1579 connection-oriented stuff */
1580 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
1581 if(!data_s)
1582 /* Receiving a Stream ID not in the hash should not happen, this is an
1583 internal error more than anything else! */
1584 return NGHTTP2_ERR_CALLBACK_FAILURE;
1585
1586 stream = H2_STREAM_CTX(data_s);
1587 if(!stream)
1588 return NGHTTP2_ERR_CALLBACK_FAILURE;
1589 }
1590 else
1591 return NGHTTP2_ERR_INVALID_ARGUMENT;
1592
1593 nread = Curl_bufq_read(&stream->sendbuf, buf, length, &result);
1594 if(nread < 0) {
1595 if(result != CURLE_AGAIN)
1596 return NGHTTP2_ERR_CALLBACK_FAILURE;
1597 nread = 0;
1598 }
1599
1600 if(nread > 0 && stream->upload_left != -1)
1601 stream->upload_left -= nread;
1602
1603 CURL_TRC_CF(data_s, cf, "[%d] req_body_read(len=%zu) left=%"
1604 CURL_FORMAT_CURL_OFF_T " -> %zd, %d",
1605 stream_id, length, stream->upload_left, nread, result);
1606
1607 if(stream->upload_left == 0)
1608 *data_flags = NGHTTP2_DATA_FLAG_EOF;
1609 else if(nread == 0)
1610 return NGHTTP2_ERR_DEFERRED;
1611
1612 return nread;
1613 }
1614
1615 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
error_callback(nghttp2_session * session,const char * msg,size_t len,void * userp)1616 static int error_callback(nghttp2_session *session,
1617 const char *msg,
1618 size_t len,
1619 void *userp)
1620 {
1621 (void)session;
1622 (void)msg;
1623 (void)len;
1624 (void)userp;
1625 return 0;
1626 }
1627 #endif
1628
1629 /*
1630 * Append headers to ask for an HTTP1.1 to HTTP2 upgrade.
1631 */
Curl_http2_request_upgrade(struct dynbuf * req,struct Curl_easy * data)1632 CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
1633 struct Curl_easy *data)
1634 {
1635 CURLcode result;
1636 char *base64;
1637 size_t blen;
1638 struct SingleRequest *k = &data->req;
1639 uint8_t binsettings[H2_BINSETTINGS_LEN];
1640 size_t binlen; /* length of the binsettings data */
1641
1642 binlen = populate_binsettings(binsettings, data);
1643 if(binlen <= 0) {
1644 failf(data, "nghttp2 unexpectedly failed on pack_settings_payload");
1645 Curl_dyn_free(req);
1646 return CURLE_FAILED_INIT;
1647 }
1648
1649 result = Curl_base64url_encode((const char *)binsettings, binlen,
1650 &base64, &blen);
1651 if(result) {
1652 Curl_dyn_free(req);
1653 return result;
1654 }
1655
1656 result = Curl_dyn_addf(req,
1657 "Connection: Upgrade, HTTP2-Settings\r\n"
1658 "Upgrade: %s\r\n"
1659 "HTTP2-Settings: %s\r\n",
1660 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
1661 free(base64);
1662
1663 k->upgr101 = UPGR101_H2;
1664
1665 return result;
1666 }
1667
http2_data_done_send(struct Curl_cfilter * cf,struct Curl_easy * data)1668 static CURLcode http2_data_done_send(struct Curl_cfilter *cf,
1669 struct Curl_easy *data)
1670 {
1671 struct cf_h2_ctx *ctx = cf->ctx;
1672 CURLcode result = CURLE_OK;
1673 struct stream_ctx *stream = H2_STREAM_CTX(data);
1674
1675 if(!ctx || !ctx->h2 || !stream)
1676 goto out;
1677
1678 CURL_TRC_CF(data, cf, "[%d] data done send", stream->id);
1679 if(!stream->send_closed) {
1680 stream->send_closed = TRUE;
1681 if(stream->upload_left) {
1682 /* we now know that everything that is buffered is all there is. */
1683 stream->upload_left = Curl_bufq_len(&stream->sendbuf);
1684 /* resume sending here to trigger the callback to get called again so
1685 that it can signal EOF to nghttp2 */
1686 (void)nghttp2_session_resume_data(ctx->h2, stream->id);
1687 drain_stream(cf, data, stream);
1688 }
1689 }
1690
1691 out:
1692 return result;
1693 }
1694
http2_handle_stream_close(struct Curl_cfilter * cf,struct Curl_easy * data,struct stream_ctx * stream,CURLcode * err)1695 static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
1696 struct Curl_easy *data,
1697 struct stream_ctx *stream,
1698 CURLcode *err)
1699 {
1700 ssize_t rv = 0;
1701
1702 if(stream->error == NGHTTP2_REFUSED_STREAM) {
1703 CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new "
1704 "connection", stream->id);
1705 connclose(cf->conn, "REFUSED_STREAM"); /* don't use this anymore */
1706 data->state.refused_stream = TRUE;
1707 *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */
1708 return -1;
1709 }
1710 else if(stream->error != NGHTTP2_NO_ERROR) {
1711 failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)",
1712 stream->id, nghttp2_http2_strerror(stream->error),
1713 stream->error);
1714 *err = CURLE_HTTP2_STREAM;
1715 return -1;
1716 }
1717 else if(stream->reset) {
1718 failf(data, "HTTP/2 stream %u was reset", stream->id);
1719 *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
1720 return -1;
1721 }
1722
1723 if(!stream->bodystarted) {
1724 failf(data, "HTTP/2 stream %u was closed cleanly, but before getting "
1725 " all response header fields, treated as error",
1726 stream->id);
1727 *err = CURLE_HTTP2_STREAM;
1728 return -1;
1729 }
1730
1731 if(Curl_dynhds_count(&stream->resp_trailers)) {
1732 struct dynhds_entry *e;
1733 struct dynbuf dbuf;
1734 size_t i;
1735
1736 *err = CURLE_OK;
1737 Curl_dyn_init(&dbuf, DYN_TRAILERS);
1738 for(i = 0; i < Curl_dynhds_count(&stream->resp_trailers); ++i) {
1739 e = Curl_dynhds_getn(&stream->resp_trailers, i);
1740 if(!e)
1741 break;
1742 Curl_dyn_reset(&dbuf);
1743 *err = Curl_dyn_addf(&dbuf, "%.*s: %.*s\x0d\x0a",
1744 (int)e->namelen, e->name,
1745 (int)e->valuelen, e->value);
1746 if(*err)
1747 break;
1748 Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&dbuf),
1749 Curl_dyn_len(&dbuf));
1750 *err = Curl_client_write(data, CLIENTWRITE_HEADER|CLIENTWRITE_TRAILER,
1751 Curl_dyn_ptr(&dbuf), Curl_dyn_len(&dbuf));
1752 if(*err)
1753 break;
1754 }
1755 Curl_dyn_free(&dbuf);
1756 if(*err)
1757 goto out;
1758 }
1759
1760 stream->close_handled = TRUE;
1761 *err = CURLE_OK;
1762 rv = 0;
1763
1764 out:
1765 CURL_TRC_CF(data, cf, "handle_stream_close -> %zd, %d", rv, *err);
1766 return rv;
1767 }
1768
sweight_wanted(const struct Curl_easy * data)1769 static int sweight_wanted(const struct Curl_easy *data)
1770 {
1771 /* 0 weight is not set by user and we take the nghttp2 default one */
1772 return data->set.priority.weight?
1773 data->set.priority.weight : NGHTTP2_DEFAULT_WEIGHT;
1774 }
1775
sweight_in_effect(const struct Curl_easy * data)1776 static int sweight_in_effect(const struct Curl_easy *data)
1777 {
1778 /* 0 weight is not set by user and we take the nghttp2 default one */
1779 return data->state.priority.weight?
1780 data->state.priority.weight : NGHTTP2_DEFAULT_WEIGHT;
1781 }
1782
1783 /*
1784 * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
1785 * and dependency to the peer. It also stores the updated values in the state
1786 * struct.
1787 */
1788
h2_pri_spec(struct Curl_easy * data,nghttp2_priority_spec * pri_spec)1789 static void h2_pri_spec(struct Curl_easy *data,
1790 nghttp2_priority_spec *pri_spec)
1791 {
1792 struct Curl_data_priority *prio = &data->set.priority;
1793 struct stream_ctx *depstream = H2_STREAM_CTX(prio->parent);
1794 int32_t depstream_id = depstream? depstream->id:0;
1795 nghttp2_priority_spec_init(pri_spec, depstream_id,
1796 sweight_wanted(data),
1797 data->set.priority.exclusive);
1798 data->state.priority = *prio;
1799 }
1800
1801 /*
1802 * Check if there's been an update in the priority /
1803 * dependency settings and if so it submits a PRIORITY frame with the updated
1804 * info.
1805 * Flush any out data pending in the network buffer.
1806 */
h2_progress_egress(struct Curl_cfilter * cf,struct Curl_easy * data)1807 static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
1808 struct Curl_easy *data)
1809 {
1810 struct cf_h2_ctx *ctx = cf->ctx;
1811 struct stream_ctx *stream = H2_STREAM_CTX(data);
1812 int rv = 0;
1813
1814 if(stream && stream->id > 0 &&
1815 ((sweight_wanted(data) != sweight_in_effect(data)) ||
1816 (data->set.priority.exclusive != data->state.priority.exclusive) ||
1817 (data->set.priority.parent != data->state.priority.parent)) ) {
1818 /* send new weight and/or dependency */
1819 nghttp2_priority_spec pri_spec;
1820
1821 h2_pri_spec(data, &pri_spec);
1822 CURL_TRC_CF(data, cf, "[%d] Queuing PRIORITY", stream->id);
1823 DEBUGASSERT(stream->id != -1);
1824 rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE,
1825 stream->id, &pri_spec);
1826 if(rv)
1827 goto out;
1828 }
1829
1830 ctx->nw_out_blocked = 0;
1831 while(!rv && !ctx->nw_out_blocked && nghttp2_session_want_write(ctx->h2))
1832 rv = nghttp2_session_send(ctx->h2);
1833
1834 out:
1835 if(nghttp2_is_fatal(rv)) {
1836 CURL_TRC_CF(data, cf, "nghttp2_session_send error (%s)%d",
1837 nghttp2_strerror(rv), rv);
1838 return CURLE_SEND_ERROR;
1839 }
1840 return nw_out_flush(cf, data);
1841 }
1842
stream_recv(struct Curl_cfilter * cf,struct Curl_easy * data,struct stream_ctx * stream,char * buf,size_t len,CURLcode * err)1843 static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
1844 struct stream_ctx *stream,
1845 char *buf, size_t len, CURLcode *err)
1846 {
1847 struct cf_h2_ctx *ctx = cf->ctx;
1848 ssize_t nread = -1;
1849
1850 *err = CURLE_AGAIN;
1851 if(!Curl_bufq_is_empty(&stream->recvbuf)) {
1852 nread = Curl_bufq_read(&stream->recvbuf,
1853 (unsigned char *)buf, len, err);
1854 if(nread < 0)
1855 goto out;
1856 DEBUGASSERT(nread > 0);
1857 }
1858
1859 if(nread < 0) {
1860 if(stream->closed) {
1861 CURL_TRC_CF(data, cf, "[%d] returning CLOSE", stream->id);
1862 nread = http2_handle_stream_close(cf, data, stream, err);
1863 }
1864 else if(stream->reset ||
1865 (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) ||
1866 (ctx->goaway && ctx->last_stream_id < stream->id)) {
1867 CURL_TRC_CF(data, cf, "[%d] returning ERR", stream->id);
1868 *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
1869 nread = -1;
1870 }
1871 }
1872 else if(nread == 0) {
1873 *err = CURLE_AGAIN;
1874 nread = -1;
1875 }
1876
1877 out:
1878 if(nread < 0 && *err != CURLE_AGAIN)
1879 CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %zd, %d",
1880 stream->id, len, nread, *err);
1881 return nread;
1882 }
1883
h2_progress_ingress(struct Curl_cfilter * cf,struct Curl_easy * data)1884 static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
1885 struct Curl_easy *data)
1886 {
1887 struct cf_h2_ctx *ctx = cf->ctx;
1888 struct stream_ctx *stream;
1889 CURLcode result = CURLE_OK;
1890 ssize_t nread;
1891
1892 /* Process network input buffer fist */
1893 if(!Curl_bufq_is_empty(&ctx->inbufq)) {
1894 CURL_TRC_CF(data, cf, "Process %zu bytes in connection buffer",
1895 Curl_bufq_len(&ctx->inbufq));
1896 if(h2_process_pending_input(cf, data, &result) < 0)
1897 return result;
1898 }
1899
1900 /* Receive data from the "lower" filters, e.g. network until
1901 * it is time to stop due to connection close or us not processing
1902 * all network input */
1903 while(!ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) {
1904 stream = H2_STREAM_CTX(data);
1905 if(stream && (stream->closed || Curl_bufq_is_full(&stream->recvbuf))) {
1906 /* We would like to abort here and stop processing, so that
1907 * the transfer loop can handle the data/close here. However,
1908 * this may leave data in underlying buffers that will not
1909 * be consumed. */
1910 if(!cf->next || !cf->next->cft->has_data_pending(cf->next, data))
1911 break;
1912 }
1913
1914 nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result);
1915 if(nread < 0) {
1916 if(result != CURLE_AGAIN) {
1917 failf(data, "Failed receiving HTTP2 data: %d(%s)", result,
1918 curl_easy_strerror(result));
1919 return result;
1920 }
1921 break;
1922 }
1923 else if(nread == 0) {
1924 CURL_TRC_CF(data, cf, "[0] ingress: connection closed");
1925 ctx->conn_closed = TRUE;
1926 break;
1927 }
1928 else {
1929 CURL_TRC_CF(data, cf, "[0] ingress: read %zd bytes",
1930 nread);
1931 }
1932
1933 if(h2_process_pending_input(cf, data, &result))
1934 return result;
1935 }
1936
1937 if(ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) {
1938 connclose(cf->conn, "GOAWAY received");
1939 }
1940
1941 return CURLE_OK;
1942 }
1943
cf_h2_recv(struct Curl_cfilter * cf,struct Curl_easy * data,char * buf,size_t len,CURLcode * err)1944 static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
1945 char *buf, size_t len, CURLcode *err)
1946 {
1947 struct cf_h2_ctx *ctx = cf->ctx;
1948 struct stream_ctx *stream = H2_STREAM_CTX(data);
1949 ssize_t nread = -1;
1950 CURLcode result;
1951 struct cf_call_data save;
1952
1953 if(!stream) {
1954 /* Abnormal call sequence: either this transfer has never opened a stream
1955 * (unlikely) or the transfer has been done, cleaned up its resources, but
1956 * a read() is called anyway. It is not clear what the calling sequence
1957 * is for such a case. */
1958 failf(data, "[%zd-%zd], http/2 recv on a transfer never opened "
1959 "or already cleared", (ssize_t)data->id,
1960 (ssize_t)cf->conn->connection_id);
1961 *err = CURLE_HTTP2;
1962 return -1;
1963 }
1964
1965 CF_DATA_SAVE(save, cf, data);
1966
1967 nread = stream_recv(cf, data, stream, buf, len, err);
1968 if(nread < 0 && *err != CURLE_AGAIN)
1969 goto out;
1970
1971 if(nread < 0) {
1972 *err = h2_progress_ingress(cf, data);
1973 if(*err)
1974 goto out;
1975
1976 nread = stream_recv(cf, data, stream, buf, len, err);
1977 }
1978
1979 if(nread > 0) {
1980 size_t data_consumed = (size_t)nread;
1981 /* Now that we transferred this to the upper layer, we report
1982 * the actual amount of DATA consumed to the H2 session, so
1983 * that it adjusts stream flow control */
1984 if(stream->resp_hds_len >= data_consumed) {
1985 stream->resp_hds_len -= data_consumed; /* no DATA */
1986 }
1987 else {
1988 if(stream->resp_hds_len) {
1989 data_consumed -= stream->resp_hds_len;
1990 stream->resp_hds_len = 0;
1991 }
1992 if(data_consumed) {
1993 nghttp2_session_consume(ctx->h2, stream->id, data_consumed);
1994 }
1995 }
1996
1997 if(stream->closed) {
1998 CURL_TRC_CF(data, cf, "[%d] DRAIN closed stream", stream->id);
1999 drain_stream(cf, data, stream);
2000 }
2001 }
2002
2003 out:
2004 result = h2_progress_egress(cf, data);
2005 if(result == CURLE_AGAIN) {
2006 /* pending data to send, need to be called again. Ideally, we'd
2007 * monitor the socket for POLLOUT, but we might not be in SENDING
2008 * transfer state any longer and are unable to make this happen.
2009 */
2010 drain_stream(cf, data, stream);
2011 }
2012 else if(result) {
2013 *err = result;
2014 nread = -1;
2015 }
2016 CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d, "
2017 "buffered=%zu, window=%d/%d, connection %d/%d",
2018 stream->id, len, nread, *err,
2019 Curl_bufq_len(&stream->recvbuf),
2020 nghttp2_session_get_stream_effective_recv_data_length(
2021 ctx->h2, stream->id),
2022 nghttp2_session_get_stream_effective_local_window_size(
2023 ctx->h2, stream->id),
2024 nghttp2_session_get_local_window_size(ctx->h2),
2025 HTTP2_HUGE_WINDOW_SIZE);
2026
2027 CF_DATA_RESTORE(cf, save);
2028 return nread;
2029 }
2030
h2_submit(struct stream_ctx ** pstream,struct Curl_cfilter * cf,struct Curl_easy * data,const void * buf,size_t len,CURLcode * err)2031 static ssize_t h2_submit(struct stream_ctx **pstream,
2032 struct Curl_cfilter *cf, struct Curl_easy *data,
2033 const void *buf, size_t len, CURLcode *err)
2034 {
2035 struct cf_h2_ctx *ctx = cf->ctx;
2036 struct stream_ctx *stream = NULL;
2037 struct dynhds h2_headers;
2038 nghttp2_nv *nva = NULL;
2039 const void *body = NULL;
2040 size_t nheader, bodylen, i;
2041 nghttp2_data_provider data_prd;
2042 int32_t stream_id;
2043 nghttp2_priority_spec pri_spec;
2044 ssize_t nwritten;
2045
2046 Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
2047
2048 *err = http2_data_setup(cf, data, &stream);
2049 if(*err) {
2050 nwritten = -1;
2051 goto out;
2052 }
2053
2054 nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
2055 if(nwritten < 0)
2056 goto out;
2057 if(!stream->h1.done) {
2058 /* need more data */
2059 goto out;
2060 }
2061 DEBUGASSERT(stream->h1.req);
2062
2063 *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data);
2064 if(*err) {
2065 nwritten = -1;
2066 goto out;
2067 }
2068 /* no longer needed */
2069 Curl_h1_req_parse_free(&stream->h1);
2070
2071 nheader = Curl_dynhds_count(&h2_headers);
2072 nva = malloc(sizeof(nghttp2_nv) * nheader);
2073 if(!nva) {
2074 *err = CURLE_OUT_OF_MEMORY;
2075 nwritten = -1;
2076 goto out;
2077 }
2078
2079 for(i = 0; i < nheader; ++i) {
2080 struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i);
2081 nva[i].name = (unsigned char *)e->name;
2082 nva[i].namelen = e->namelen;
2083 nva[i].value = (unsigned char *)e->value;
2084 nva[i].valuelen = e->valuelen;
2085 nva[i].flags = NGHTTP2_NV_FLAG_NONE;
2086 }
2087
2088 h2_pri_spec(data, &pri_spec);
2089 if(!nghttp2_session_check_request_allowed(ctx->h2))
2090 CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)");
2091
2092 switch(data->state.httpreq) {
2093 case HTTPREQ_POST:
2094 case HTTPREQ_POST_FORM:
2095 case HTTPREQ_POST_MIME:
2096 case HTTPREQ_PUT:
2097 if(data->state.infilesize != -1)
2098 stream->upload_left = data->state.infilesize;
2099 else
2100 /* data sending without specifying the data amount up front */
2101 stream->upload_left = -1; /* unknown */
2102
2103 data_prd.read_callback = req_body_read_callback;
2104 data_prd.source.ptr = NULL;
2105 stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader,
2106 &data_prd, data);
2107 break;
2108 default:
2109 stream->upload_left = 0; /* no request body */
2110 stream_id = nghttp2_submit_request(ctx->h2, &pri_spec, nva, nheader,
2111 NULL, data);
2112 }
2113
2114 if(stream_id < 0) {
2115 CURL_TRC_CF(data, cf, "send: nghttp2_submit_request error (%s)%u",
2116 nghttp2_strerror(stream_id), stream_id);
2117 *err = CURLE_SEND_ERROR;
2118 nwritten = -1;
2119 goto out;
2120 }
2121
2122 #define MAX_ACC 60000 /* <64KB to account for some overhead */
2123 if(Curl_trc_is_verbose(data)) {
2124 size_t acc = 0;
2125
2126 infof(data, "[HTTP/2] [%d] OPENED stream for %s",
2127 stream_id, data->state.url);
2128 for(i = 0; i < nheader; ++i) {
2129 acc += nva[i].namelen + nva[i].valuelen;
2130
2131 infof(data, "[HTTP/2] [%d] [%.*s: %.*s]", stream_id,
2132 (int)nva[i].namelen, nva[i].name,
2133 (int)nva[i].valuelen, nva[i].value);
2134 }
2135
2136 if(acc > MAX_ACC) {
2137 infof(data, "[HTTP/2] Warning: The cumulative length of all "
2138 "headers exceeds %d bytes and that could cause the "
2139 "stream to be rejected.", MAX_ACC);
2140 }
2141 }
2142
2143 stream->id = stream_id;
2144 stream->local_window_size = H2_STREAM_WINDOW_SIZE;
2145 if(data->set.max_recv_speed) {
2146 /* We are asked to only receive `max_recv_speed` bytes per second.
2147 * Let's limit our stream window size around that, otherwise the server
2148 * will send in large bursts only. We make the window 50% larger to
2149 * allow for data in flight and avoid stalling. */
2150 curl_off_t n = (((data->set.max_recv_speed - 1) / H2_CHUNK_SIZE) + 1);
2151 n += CURLMAX((n/2), 1);
2152 if(n < (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) &&
2153 n < (UINT_MAX / H2_CHUNK_SIZE)) {
2154 stream->local_window_size = (uint32_t)n * H2_CHUNK_SIZE;
2155 }
2156 }
2157
2158 body = (const char *)buf + nwritten;
2159 bodylen = len - nwritten;
2160
2161 if(bodylen) {
2162 /* We have request body to send in DATA frame */
2163 ssize_t n = Curl_bufq_write(&stream->sendbuf, body, bodylen, err);
2164 if(n < 0) {
2165 *err = CURLE_SEND_ERROR;
2166 nwritten = -1;
2167 goto out;
2168 }
2169 nwritten += n;
2170 }
2171
2172 out:
2173 CURL_TRC_CF(data, cf, "[%d] submit -> %zd, %d",
2174 stream? stream->id : -1, nwritten, *err);
2175 Curl_safefree(nva);
2176 *pstream = stream;
2177 Curl_dynhds_free(&h2_headers);
2178 return nwritten;
2179 }
2180
cf_h2_send(struct Curl_cfilter * cf,struct Curl_easy * data,const void * buf,size_t len,CURLcode * err)2181 static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
2182 const void *buf, size_t len, CURLcode *err)
2183 {
2184 struct cf_h2_ctx *ctx = cf->ctx;
2185 struct stream_ctx *stream = H2_STREAM_CTX(data);
2186 struct cf_call_data save;
2187 int rv;
2188 ssize_t nwritten;
2189 CURLcode result;
2190 int blocked = 0, was_blocked = 0;
2191
2192 CF_DATA_SAVE(save, cf, data);
2193
2194 if(stream && stream->id != -1) {
2195 if(stream->upload_blocked_len) {
2196 /* the data in `buf` has already been submitted or added to the
2197 * buffers, but have been EAGAINed on the last invocation. */
2198 /* TODO: this assertion triggers in OSSFuzz runs and it is not
2199 * clear why. Disable for now to let OSSFuzz continue its tests. */
2200 DEBUGASSERT(len >= stream->upload_blocked_len);
2201 if(len < stream->upload_blocked_len) {
2202 /* Did we get called again with a smaller `len`? This should not
2203 * happen. We are not prepared to handle that. */
2204 failf(data, "HTTP/2 send again with decreased length (%zd vs %zd)",
2205 len, stream->upload_blocked_len);
2206 *err = CURLE_HTTP2;
2207 nwritten = -1;
2208 goto out;
2209 }
2210 nwritten = (ssize_t)stream->upload_blocked_len;
2211 stream->upload_blocked_len = 0;
2212 was_blocked = 1;
2213 }
2214 else if(stream->closed) {
2215 if(stream->resp_hds_complete) {
2216 /* Server decided to close the stream after having sent us a findl
2217 * response. This is valid if it is not interested in the request
2218 * body. This happens on 30x or 40x responses.
2219 * We silently discard the data sent, since this is not a transport
2220 * error situation. */
2221 CURL_TRC_CF(data, cf, "[%d] discarding data"
2222 "on closed stream with response", stream->id);
2223 *err = CURLE_OK;
2224 nwritten = (ssize_t)len;
2225 goto out;
2226 }
2227 infof(data, "stream %u closed", stream->id);
2228 *err = CURLE_SEND_ERROR;
2229 nwritten = -1;
2230 goto out;
2231 }
2232 else {
2233 /* If stream_id != -1, we have dispatched request HEADERS and
2234 * optionally request body, and now are going to send or sending
2235 * more request body in DATA frame */
2236 nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err);
2237 if(nwritten < 0 && *err != CURLE_AGAIN)
2238 goto out;
2239 }
2240
2241 if(!Curl_bufq_is_empty(&stream->sendbuf)) {
2242 /* req body data is buffered, resume the potentially suspended stream */
2243 rv = nghttp2_session_resume_data(ctx->h2, stream->id);
2244 if(nghttp2_is_fatal(rv)) {
2245 *err = CURLE_SEND_ERROR;
2246 nwritten = -1;
2247 goto out;
2248 }
2249 }
2250 }
2251 else {
2252 nwritten = h2_submit(&stream, cf, data, buf, len, err);
2253 if(nwritten < 0) {
2254 goto out;
2255 }
2256 DEBUGASSERT(stream);
2257 }
2258
2259 /* Call the nghttp2 send loop and flush to write ALL buffered data,
2260 * headers and/or request body completely out to the network */
2261 result = h2_progress_egress(cf, data);
2262 /* if the stream has been closed in egress handling (nghttp2 does that
2263 * when it does not like the headers, for example */
2264 if(stream && stream->closed && !was_blocked) {
2265 infof(data, "stream %u closed", stream->id);
2266 *err = CURLE_SEND_ERROR;
2267 nwritten = -1;
2268 goto out;
2269 }
2270 else if(result == CURLE_AGAIN) {
2271 blocked = 1;
2272 }
2273 else if(result) {
2274 *err = result;
2275 nwritten = -1;
2276 goto out;
2277 }
2278 else if(stream && !Curl_bufq_is_empty(&stream->sendbuf)) {
2279 /* although we wrote everything that nghttp2 wants to send now,
2280 * there is data left in our stream send buffer unwritten. This may
2281 * be due to the stream's HTTP/2 flow window being exhausted. */
2282 blocked = 1;
2283 }
2284
2285 if(stream && blocked && nwritten > 0) {
2286 /* Unable to send all data, due to connection blocked or H2 window
2287 * exhaustion. Data is left in our stream buffer, or nghttp2's internal
2288 * frame buffer or our network out buffer. */
2289 size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2,
2290 stream->id);
2291 if(rwin == 0) {
2292 /* H2 flow window exhaustion. We need to HOLD upload until we get
2293 * a WINDOW_UPDATE from the server. */
2294 data->req.keepon |= KEEP_SEND_HOLD;
2295 CURL_TRC_CF(data, cf, "[%d] holding send as remote flow "
2296 "window is exhausted", stream->id);
2297 }
2298
2299 /* Whatever the cause, we need to return CURL_EAGAIN for this call.
2300 * We have unwritten state that needs us being invoked again and EAGAIN
2301 * is the only way to ensure that. */
2302 stream->upload_blocked_len = nwritten;
2303 CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) BLOCK: win %u/%zu "
2304 "blocked_len=%zu",
2305 stream->id, len,
2306 nghttp2_session_get_remote_window_size(ctx->h2), rwin,
2307 nwritten);
2308 *err = CURLE_AGAIN;
2309 nwritten = -1;
2310 goto out;
2311 }
2312 else if(should_close_session(ctx)) {
2313 /* nghttp2 thinks this session is done. If the stream has not been
2314 * closed, this is an error state for out transfer */
2315 if(stream->closed) {
2316 nwritten = http2_handle_stream_close(cf, data, stream, err);
2317 }
2318 else {
2319 CURL_TRC_CF(data, cf, "send: nothing to do in this session");
2320 *err = CURLE_HTTP2;
2321 nwritten = -1;
2322 }
2323 }
2324
2325 out:
2326 if(stream) {
2327 CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %zd, %d, "
2328 "upload_left=%" CURL_FORMAT_CURL_OFF_T ", "
2329 "h2 windows %d-%d (stream-conn), "
2330 "buffers %zu-%zu (stream-conn)",
2331 stream->id, len, nwritten, *err,
2332 stream->upload_left,
2333 nghttp2_session_get_stream_remote_window_size(
2334 ctx->h2, stream->id),
2335 nghttp2_session_get_remote_window_size(ctx->h2),
2336 Curl_bufq_len(&stream->sendbuf),
2337 Curl_bufq_len(&ctx->outbufq));
2338 }
2339 else {
2340 CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %zd, %d, "
2341 "connection-window=%d, nw_send_buffer(%zu)",
2342 len, nwritten, *err,
2343 nghttp2_session_get_remote_window_size(ctx->h2),
2344 Curl_bufq_len(&ctx->outbufq));
2345 }
2346 CF_DATA_RESTORE(cf, save);
2347 return nwritten;
2348 }
2349
cf_h2_get_select_socks(struct Curl_cfilter * cf,struct Curl_easy * data,curl_socket_t * sock)2350 static int cf_h2_get_select_socks(struct Curl_cfilter *cf,
2351 struct Curl_easy *data,
2352 curl_socket_t *sock)
2353 {
2354 struct cf_h2_ctx *ctx = cf->ctx;
2355 struct SingleRequest *k = &data->req;
2356 struct stream_ctx *stream = H2_STREAM_CTX(data);
2357 int bitmap = GETSOCK_BLANK;
2358 struct cf_call_data save;
2359
2360 CF_DATA_SAVE(save, cf, data);
2361 sock[0] = Curl_conn_cf_get_socket(cf, data);
2362
2363 if(!(k->keepon & (KEEP_RECV_PAUSE|KEEP_RECV_HOLD)))
2364 /* Unless paused - in an HTTP/2 connection we can basically always get a
2365 frame so we should always be ready for one */
2366 bitmap |= GETSOCK_READSOCK(0);
2367
2368 /* we're (still uploading OR the HTTP/2 layer wants to send data) AND
2369 there's a window to send data in */
2370 if((((k->keepon & KEEP_SENDBITS) == KEEP_SEND) ||
2371 nghttp2_session_want_write(ctx->h2)) &&
2372 (nghttp2_session_get_remote_window_size(ctx->h2) &&
2373 nghttp2_session_get_stream_remote_window_size(ctx->h2,
2374 stream->id)))
2375 bitmap |= GETSOCK_WRITESOCK(0);
2376
2377 CF_DATA_RESTORE(cf, save);
2378 return bitmap;
2379 }
2380
2381
cf_h2_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)2382 static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
2383 struct Curl_easy *data,
2384 bool blocking, bool *done)
2385 {
2386 struct cf_h2_ctx *ctx = cf->ctx;
2387 CURLcode result = CURLE_OK;
2388 struct cf_call_data save;
2389
2390 if(cf->connected) {
2391 *done = TRUE;
2392 return CURLE_OK;
2393 }
2394
2395 /* Connect the lower filters first */
2396 if(!cf->next->connected) {
2397 result = Curl_conn_cf_connect(cf->next, data, blocking, done);
2398 if(result || !*done)
2399 return result;
2400 }
2401
2402 *done = FALSE;
2403
2404 CF_DATA_SAVE(save, cf, data);
2405 if(!ctx->h2) {
2406 result = cf_h2_ctx_init(cf, data, FALSE);
2407 if(result)
2408 goto out;
2409 }
2410
2411 result = h2_progress_ingress(cf, data);
2412 if(result)
2413 goto out;
2414
2415 /* Send out our SETTINGS and ACKs and such. If that blocks, we
2416 * have it buffered and can count this filter as being connected */
2417 result = h2_progress_egress(cf, data);
2418 if(result == CURLE_AGAIN)
2419 result = CURLE_OK;
2420 else if(result)
2421 goto out;
2422
2423 *done = TRUE;
2424 cf->connected = TRUE;
2425 result = CURLE_OK;
2426
2427 out:
2428 CURL_TRC_CF(data, cf, "cf_connect() -> %d, %d, ", result, *done);
2429 CF_DATA_RESTORE(cf, save);
2430 return result;
2431 }
2432
cf_h2_close(struct Curl_cfilter * cf,struct Curl_easy * data)2433 static void cf_h2_close(struct Curl_cfilter *cf, struct Curl_easy *data)
2434 {
2435 struct cf_h2_ctx *ctx = cf->ctx;
2436
2437 if(ctx) {
2438 struct cf_call_data save;
2439
2440 CF_DATA_SAVE(save, cf, data);
2441 cf_h2_ctx_clear(ctx);
2442 CF_DATA_RESTORE(cf, save);
2443 }
2444 if(cf->next)
2445 cf->next->cft->do_close(cf->next, data);
2446 }
2447
cf_h2_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)2448 static void cf_h2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
2449 {
2450 struct cf_h2_ctx *ctx = cf->ctx;
2451
2452 (void)data;
2453 if(ctx) {
2454 cf_h2_ctx_free(ctx);
2455 cf->ctx = NULL;
2456 }
2457 }
2458
http2_data_pause(struct Curl_cfilter * cf,struct Curl_easy * data,bool pause)2459 static CURLcode http2_data_pause(struct Curl_cfilter *cf,
2460 struct Curl_easy *data,
2461 bool pause)
2462 {
2463 #ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
2464 struct cf_h2_ctx *ctx = cf->ctx;
2465 struct stream_ctx *stream = H2_STREAM_CTX(data);
2466
2467 DEBUGASSERT(data);
2468 if(ctx && ctx->h2 && stream) {
2469 uint32_t window = pause? 0 : stream->local_window_size;
2470
2471 int rv = nghttp2_session_set_local_window_size(ctx->h2,
2472 NGHTTP2_FLAG_NONE,
2473 stream->id,
2474 window);
2475 if(rv) {
2476 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
2477 nghttp2_strerror(rv), rv);
2478 return CURLE_HTTP2;
2479 }
2480
2481 if(!pause)
2482 drain_stream(cf, data, stream);
2483
2484 /* attempt to send the window update */
2485 (void)h2_progress_egress(cf, data);
2486
2487 if(!pause) {
2488 /* Unpausing a h2 transfer, requires it to be run again. The server
2489 * may send new DATA on us increasing the flow window, and it may
2490 * not. We may have already buffered and exhausted the new window
2491 * by operating on things in flight during the handling of other
2492 * transfers. */
2493 drain_stream(cf, data, stream);
2494 Curl_expire(data, 0, EXPIRE_RUN_NOW);
2495 }
2496 DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u",
2497 window, stream->id));
2498
2499 #ifdef DEBUGBUILD
2500 {
2501 /* read out the stream local window again */
2502 uint32_t window2 =
2503 nghttp2_session_get_stream_local_window_size(ctx->h2,
2504 stream->id);
2505 DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u",
2506 window2, stream->id));
2507 }
2508 #endif
2509 }
2510 #endif
2511 return CURLE_OK;
2512 }
2513
cf_h2_cntrl(struct Curl_cfilter * cf,struct Curl_easy * data,int event,int arg1,void * arg2)2514 static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf,
2515 struct Curl_easy *data,
2516 int event, int arg1, void *arg2)
2517 {
2518 CURLcode result = CURLE_OK;
2519 struct cf_call_data save;
2520
2521 (void)arg2;
2522
2523 CF_DATA_SAVE(save, cf, data);
2524 switch(event) {
2525 case CF_CTRL_DATA_SETUP:
2526 break;
2527 case CF_CTRL_DATA_PAUSE:
2528 result = http2_data_pause(cf, data, (arg1 != 0));
2529 break;
2530 case CF_CTRL_DATA_DONE_SEND: {
2531 result = http2_data_done_send(cf, data);
2532 break;
2533 }
2534 case CF_CTRL_DATA_DONE: {
2535 http2_data_done(cf, data, arg1 != 0);
2536 break;
2537 }
2538 default:
2539 break;
2540 }
2541 CF_DATA_RESTORE(cf, save);
2542 return result;
2543 }
2544
cf_h2_data_pending(struct Curl_cfilter * cf,const struct Curl_easy * data)2545 static bool cf_h2_data_pending(struct Curl_cfilter *cf,
2546 const struct Curl_easy *data)
2547 {
2548 struct cf_h2_ctx *ctx = cf->ctx;
2549 struct stream_ctx *stream = H2_STREAM_CTX(data);
2550
2551 if(ctx && (!Curl_bufq_is_empty(&ctx->inbufq)
2552 || (stream && !Curl_bufq_is_empty(&stream->sendbuf))
2553 || (stream && !Curl_bufq_is_empty(&stream->recvbuf))))
2554 return TRUE;
2555 return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
2556 }
2557
cf_h2_is_alive(struct Curl_cfilter * cf,struct Curl_easy * data,bool * input_pending)2558 static bool cf_h2_is_alive(struct Curl_cfilter *cf,
2559 struct Curl_easy *data,
2560 bool *input_pending)
2561 {
2562 struct cf_h2_ctx *ctx = cf->ctx;
2563 CURLcode result;
2564 struct cf_call_data save;
2565
2566 CF_DATA_SAVE(save, cf, data);
2567 result = (ctx && ctx->h2 && http2_connisalive(cf, data, input_pending));
2568 CURL_TRC_CF(data, cf, "conn alive -> %d, input_pending=%d",
2569 result, *input_pending);
2570 CF_DATA_RESTORE(cf, save);
2571 return result;
2572 }
2573
cf_h2_keep_alive(struct Curl_cfilter * cf,struct Curl_easy * data)2574 static CURLcode cf_h2_keep_alive(struct Curl_cfilter *cf,
2575 struct Curl_easy *data)
2576 {
2577 CURLcode result;
2578 struct cf_call_data save;
2579
2580 CF_DATA_SAVE(save, cf, data);
2581 result = http2_send_ping(cf, data);
2582 CF_DATA_RESTORE(cf, save);
2583 return result;
2584 }
2585
cf_h2_query(struct Curl_cfilter * cf,struct Curl_easy * data,int query,int * pres1,void * pres2)2586 static CURLcode cf_h2_query(struct Curl_cfilter *cf,
2587 struct Curl_easy *data,
2588 int query, int *pres1, void *pres2)
2589 {
2590 struct cf_h2_ctx *ctx = cf->ctx;
2591 struct cf_call_data save;
2592 size_t effective_max;
2593
2594 switch(query) {
2595 case CF_QUERY_MAX_CONCURRENT:
2596 DEBUGASSERT(pres1);
2597
2598 CF_DATA_SAVE(save, cf, data);
2599 if(nghttp2_session_check_request_allowed(ctx->h2) == 0) {
2600 /* the limit is what we have in use right now */
2601 effective_max = CONN_INUSE(cf->conn);
2602 }
2603 else {
2604 effective_max = ctx->max_concurrent_streams;
2605 }
2606 *pres1 = (effective_max > INT_MAX)? INT_MAX : (int)effective_max;
2607 CF_DATA_RESTORE(cf, save);
2608 return CURLE_OK;
2609 default:
2610 break;
2611 }
2612 return cf->next?
2613 cf->next->cft->query(cf->next, data, query, pres1, pres2) :
2614 CURLE_UNKNOWN_OPTION;
2615 }
2616
2617 struct Curl_cftype Curl_cft_nghttp2 = {
2618 "HTTP/2",
2619 CF_TYPE_MULTIPLEX,
2620 CURL_LOG_LVL_NONE,
2621 cf_h2_destroy,
2622 cf_h2_connect,
2623 cf_h2_close,
2624 Curl_cf_def_get_host,
2625 cf_h2_get_select_socks,
2626 cf_h2_data_pending,
2627 cf_h2_send,
2628 cf_h2_recv,
2629 cf_h2_cntrl,
2630 cf_h2_is_alive,
2631 cf_h2_keep_alive,
2632 cf_h2_query,
2633 };
2634
http2_cfilter_add(struct Curl_cfilter ** pcf,struct Curl_easy * data,struct connectdata * conn,int sockindex)2635 static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf,
2636 struct Curl_easy *data,
2637 struct connectdata *conn,
2638 int sockindex)
2639 {
2640 struct Curl_cfilter *cf = NULL;
2641 struct cf_h2_ctx *ctx;
2642 CURLcode result = CURLE_OUT_OF_MEMORY;
2643
2644 DEBUGASSERT(data->conn);
2645 ctx = calloc(sizeof(*ctx), 1);
2646 if(!ctx)
2647 goto out;
2648
2649 result = Curl_cf_create(&cf, &Curl_cft_nghttp2, ctx);
2650 if(result)
2651 goto out;
2652
2653 Curl_conn_cf_add(data, conn, sockindex, cf);
2654 result = CURLE_OK;
2655
2656 out:
2657 if(result)
2658 cf_h2_ctx_free(ctx);
2659 *pcf = result? NULL : cf;
2660 return result;
2661 }
2662
http2_cfilter_insert_after(struct Curl_cfilter * cf,struct Curl_easy * data)2663 static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf,
2664 struct Curl_easy *data)
2665 {
2666 struct Curl_cfilter *cf_h2 = NULL;
2667 struct cf_h2_ctx *ctx;
2668 CURLcode result = CURLE_OUT_OF_MEMORY;
2669
2670 (void)data;
2671 ctx = calloc(sizeof(*ctx), 1);
2672 if(!ctx)
2673 goto out;
2674
2675 result = Curl_cf_create(&cf_h2, &Curl_cft_nghttp2, ctx);
2676 if(result)
2677 goto out;
2678
2679 Curl_conn_cf_insert_after(cf, cf_h2);
2680 result = CURLE_OK;
2681
2682 out:
2683 if(result)
2684 cf_h2_ctx_free(ctx);
2685 return result;
2686 }
2687
Curl_cf_is_http2(struct Curl_cfilter * cf,const struct Curl_easy * data)2688 static bool Curl_cf_is_http2(struct Curl_cfilter *cf,
2689 const struct Curl_easy *data)
2690 {
2691 (void)data;
2692 for(; cf; cf = cf->next) {
2693 if(cf->cft == &Curl_cft_nghttp2)
2694 return TRUE;
2695 if(cf->cft->flags & CF_TYPE_IP_CONNECT)
2696 return FALSE;
2697 }
2698 return FALSE;
2699 }
2700
Curl_conn_is_http2(const struct Curl_easy * data,const struct connectdata * conn,int sockindex)2701 bool Curl_conn_is_http2(const struct Curl_easy *data,
2702 const struct connectdata *conn,
2703 int sockindex)
2704 {
2705 return conn? Curl_cf_is_http2(conn->cfilter[sockindex], data) : FALSE;
2706 }
2707
Curl_http2_may_switch(struct Curl_easy * data,struct connectdata * conn,int sockindex)2708 bool Curl_http2_may_switch(struct Curl_easy *data,
2709 struct connectdata *conn,
2710 int sockindex)
2711 {
2712 (void)sockindex;
2713 if(!Curl_conn_is_http2(data, conn, sockindex) &&
2714 data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) {
2715 #ifndef CURL_DISABLE_PROXY
2716 if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
2717 /* We don't support HTTP/2 proxies yet. Also it's debatable
2718 whether or not this setting should apply to HTTP/2 proxies. */
2719 infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
2720 return FALSE;
2721 }
2722 #endif
2723 return TRUE;
2724 }
2725 return FALSE;
2726 }
2727
Curl_http2_switch(struct Curl_easy * data,struct connectdata * conn,int sockindex)2728 CURLcode Curl_http2_switch(struct Curl_easy *data,
2729 struct connectdata *conn, int sockindex)
2730 {
2731 struct Curl_cfilter *cf;
2732 CURLcode result;
2733
2734 DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
2735 DEBUGF(infof(data, "switching to HTTP/2"));
2736
2737 result = http2_cfilter_add(&cf, data, conn, sockindex);
2738 if(result)
2739 return result;
2740
2741 result = cf_h2_ctx_init(cf, data, FALSE);
2742 if(result)
2743 return result;
2744
2745 conn->httpversion = 20; /* we know we're on HTTP/2 now */
2746 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2747 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
2748 Curl_multi_connchanged(data->multi);
2749
2750 if(cf->next) {
2751 bool done;
2752 return Curl_conn_cf_connect(cf, data, FALSE, &done);
2753 }
2754 return CURLE_OK;
2755 }
2756
Curl_http2_switch_at(struct Curl_cfilter * cf,struct Curl_easy * data)2757 CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data)
2758 {
2759 struct Curl_cfilter *cf_h2;
2760 CURLcode result;
2761
2762 DEBUGASSERT(!Curl_cf_is_http2(cf, data));
2763
2764 result = http2_cfilter_insert_after(cf, data);
2765 if(result)
2766 return result;
2767
2768 cf_h2 = cf->next;
2769 result = cf_h2_ctx_init(cf_h2, data, FALSE);
2770 if(result)
2771 return result;
2772
2773 cf->conn->httpversion = 20; /* we know we're on HTTP/2 now */
2774 cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2775 cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
2776 Curl_multi_connchanged(data->multi);
2777
2778 if(cf_h2->next) {
2779 bool done;
2780 return Curl_conn_cf_connect(cf_h2, data, FALSE, &done);
2781 }
2782 return CURLE_OK;
2783 }
2784
Curl_http2_upgrade(struct Curl_easy * data,struct connectdata * conn,int sockindex,const char * mem,size_t nread)2785 CURLcode Curl_http2_upgrade(struct Curl_easy *data,
2786 struct connectdata *conn, int sockindex,
2787 const char *mem, size_t nread)
2788 {
2789 struct Curl_cfilter *cf;
2790 struct cf_h2_ctx *ctx;
2791 CURLcode result;
2792
2793 DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
2794 DEBUGF(infof(data, "upgrading to HTTP/2"));
2795 DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED);
2796
2797 result = http2_cfilter_add(&cf, data, conn, sockindex);
2798 if(result)
2799 return result;
2800
2801 DEBUGASSERT(cf->cft == &Curl_cft_nghttp2);
2802 ctx = cf->ctx;
2803
2804 result = cf_h2_ctx_init(cf, data, TRUE);
2805 if(result)
2806 return result;
2807
2808 if(nread > 0) {
2809 /* Remaining data from the protocol switch reply is already using
2810 * the switched protocol, ie. HTTP/2. We add that to the network
2811 * inbufq. */
2812 ssize_t copied;
2813
2814 copied = Curl_bufq_write(&ctx->inbufq,
2815 (const unsigned char *)mem, nread, &result);
2816 if(copied < 0) {
2817 failf(data, "error on copying HTTP Upgrade response: %d", result);
2818 return CURLE_RECV_ERROR;
2819 }
2820 if((size_t)copied < nread) {
2821 failf(data, "connection buffer size could not take all data "
2822 "from HTTP Upgrade response header: copied=%zd, datalen=%zu",
2823 copied, nread);
2824 return CURLE_HTTP2;
2825 }
2826 infof(data, "Copied HTTP/2 data in stream buffer to connection buffer"
2827 " after upgrade: len=%zu", nread);
2828 }
2829
2830 conn->httpversion = 20; /* we know we're on HTTP/2 now */
2831 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2832 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
2833 Curl_multi_connchanged(data->multi);
2834
2835 if(cf->next) {
2836 bool done;
2837 return Curl_conn_cf_connect(cf, data, FALSE, &done);
2838 }
2839 return CURLE_OK;
2840 }
2841
2842 /* Only call this function for a transfer that already got an HTTP/2
2843 CURLE_HTTP2_STREAM error! */
Curl_h2_http_1_1_error(struct Curl_easy * data)2844 bool Curl_h2_http_1_1_error(struct Curl_easy *data)
2845 {
2846 struct stream_ctx *stream = H2_STREAM_CTX(data);
2847 return (stream && stream->error == NGHTTP2_HTTP_1_1_REQUIRED);
2848 }
2849
2850 #else /* !USE_NGHTTP2 */
2851
2852 /* Satisfy external references even if http2 is not compiled in. */
2853 #include <curl/curl.h>
2854
curl_pushheader_bynum(struct curl_pushheaders * h,size_t num)2855 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
2856 {
2857 (void) h;
2858 (void) num;
2859 return NULL;
2860 }
2861
curl_pushheader_byname(struct curl_pushheaders * h,const char * header)2862 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
2863 {
2864 (void) h;
2865 (void) header;
2866 return NULL;
2867 }
2868
2869 #endif /* USE_NGHTTP2 */
2870