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