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