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