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