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