source: azure_iot_hub/trunk/curl-7.57.0/lib/http2.c@ 389

Last change on this file since 389 was 389, checked in by coas-nagasima, 5 years ago

ビルドが通るよう更新

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 69.1 KB
Line 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2017, 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.haxx.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 ***************************************************************************/
22
23#include "curl_setup.h"
24
25#ifdef USE_NGHTTP2
26#include <nghttp2/nghttp2.h>
27#include "urldata.h"
28#include "http2.h"
29#include "http.h"
30#include "sendf.h"
31#include "select.h"
32#include "curl_base64.h"
33#include "strcase.h"
34#include "multiif.h"
35#include "url.h"
36#include "connect.h"
37#include "strtoofft.h"
38#include "strdup.h"
39/* The last 3 #include files should be in this order */
40#include "curl_printf.h"
41#include "curl_memory.h"
42#include "memdebug.h"
43
44#define MIN(x,y) ((x)<(y)?(x):(y))
45
46#if (NGHTTP2_VERSION_NUM < 0x010000)
47#error too old nghttp2 version, upgrade!
48#endif
49
50#if (NGHTTP2_VERSION_NUM > 0x010800)
51#define NGHTTP2_HAS_HTTP2_STRERROR 1
52#endif
53
54#if (NGHTTP2_VERSION_NUM >= 0x010900)
55/* nghttp2_session_callbacks_set_error_callback is present in nghttp2 1.9.0 or
56 later */
57#define NGHTTP2_HAS_ERROR_CALLBACK 1
58#else
59#define nghttp2_session_callbacks_set_error_callback(x,y)
60#endif
61
62#if (NGHTTP2_VERSION_NUM >= 0x010c00)
63#define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1
64#endif
65
66#define HTTP2_HUGE_WINDOW_SIZE (1 << 30)
67
68/*
69 * Curl_http2_init_state() is called when the easy handle is created and
70 * allows for HTTP/2 specific init of state.
71 */
72void Curl_http2_init_state(struct UrlState *state)
73{
74 state->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
75}
76
77/*
78 * Curl_http2_init_userset() is called when the easy handle is created and
79 * allows for HTTP/2 specific user-set fields.
80 */
81void Curl_http2_init_userset(struct UserDefined *set)
82{
83 set->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
84}
85
86static int http2_perform_getsock(const struct connectdata *conn,
87 curl_socket_t *sock, /* points to
88 numsocks
89 number of
90 sockets */
91 int numsocks)
92{
93 const struct http_conn *c = &conn->proto.httpc;
94 int bitmap = GETSOCK_BLANK;
95 (void)numsocks;
96
97 /* TODO We should check underlying socket state if it is SSL socket
98 because of renegotiation. */
99 sock[0] = conn->sock[FIRSTSOCKET];
100
101 /* in a HTTP/2 connection we can basically always get a frame so we should
102 always be ready for one */
103 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
104
105 if(nghttp2_session_want_write(c->h2))
106 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
107
108 return bitmap;
109}
110
111static int http2_getsock(struct connectdata *conn,
112 curl_socket_t *sock, /* points to numsocks
113 number of sockets */
114 int numsocks)
115{
116 return http2_perform_getsock(conn, sock, numsocks);
117}
118
119/*
120 * http2_stream_free() free HTTP2 stream related data
121 */
122static void http2_stream_free(struct HTTP *http)
123{
124 if(http) {
125 Curl_add_buffer_free(http->header_recvbuf);
126 http->header_recvbuf = NULL; /* clear the pointer */
127 Curl_add_buffer_free(http->trailer_recvbuf);
128 http->trailer_recvbuf = NULL; /* clear the pointer */
129 for(; http->push_headers_used > 0; --http->push_headers_used) {
130 free(http->push_headers[http->push_headers_used - 1]);
131 }
132 free(http->push_headers);
133 http->push_headers = NULL;
134 }
135}
136
137static CURLcode http2_disconnect(struct connectdata *conn,
138 bool dead_connection)
139{
140 struct http_conn *c = &conn->proto.httpc;
141 (void)dead_connection;
142
143 DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT starts now\n"));
144
145 nghttp2_session_del(c->h2);
146 Curl_safefree(c->inbuf);
147 http2_stream_free(conn->data->req.protop);
148
149 DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT done\n"));
150
151 return CURLE_OK;
152}
153
154/*
155 * The server may send us data at any point (e.g. PING frames). Therefore,
156 * we cannot assume that an HTTP/2 socket is dead just because it is readable.
157 *
158 * Instead, if it is readable, run Curl_connalive() to peek at the socket
159 * and distinguish between closed and data.
160 */
161static bool http2_connisdead(struct connectdata *check)
162{
163 int sval;
164 bool ret_val = TRUE;
165
166 sval = SOCKET_READABLE(check->sock[FIRSTSOCKET], 0);
167 if(sval == 0) {
168 /* timeout */
169 ret_val = FALSE;
170 }
171 else if(sval & CURL_CSELECT_ERR) {
172 /* socket is in an error state */
173 ret_val = TRUE;
174 }
175 else if(sval & CURL_CSELECT_IN) {
176 /* readable with no error. could still be closed */
177 ret_val = !Curl_connalive(check);
178 }
179
180 return ret_val;
181}
182
183
184static unsigned int http2_conncheck(struct connectdata *check,
185 unsigned int checks_to_perform)
186{
187 unsigned int ret_val = CONNRESULT_NONE;
188
189 if(checks_to_perform & CONNCHECK_ISDEAD) {
190 if(http2_connisdead(check))
191 ret_val |= CONNRESULT_DEAD;
192 }
193
194 return ret_val;
195}
196
197/* called from Curl_http_setup_conn */
198void Curl_http2_setup_req(struct Curl_easy *data)
199{
200 struct HTTP *http = data->req.protop;
201
202 http->nread_header_recvbuf = 0;
203 http->bodystarted = FALSE;
204 http->status_code = -1;
205 http->pausedata = NULL;
206 http->pauselen = 0;
207 http->error_code = NGHTTP2_NO_ERROR;
208 http->closed = FALSE;
209 http->close_handled = FALSE;
210 http->mem = data->state.buffer;
211 http->len = data->set.buffer_size;
212 http->memlen = 0;
213}
214
215/* called from Curl_http_setup_conn */
216void Curl_http2_setup_conn(struct connectdata *conn)
217{
218 conn->proto.httpc.settings.max_concurrent_streams =
219 DEFAULT_MAX_CONCURRENT_STREAMS;
220}
221
222/*
223 * HTTP2 handler interface. This isn't added to the general list of protocols
224 * but will be used at run-time when the protocol is dynamically switched from
225 * HTTP to HTTP2.
226 */
227static const struct Curl_handler Curl_handler_http2 = {
228 "HTTP", /* scheme */
229 ZERO_NULL, /* setup_connection */
230 Curl_http, /* do_it */
231 Curl_http_done, /* done */
232 ZERO_NULL, /* do_more */
233 ZERO_NULL, /* connect_it */
234 ZERO_NULL, /* connecting */
235 ZERO_NULL, /* doing */
236 http2_getsock, /* proto_getsock */
237 http2_getsock, /* doing_getsock */
238 ZERO_NULL, /* domore_getsock */
239 http2_perform_getsock, /* perform_getsock */
240 http2_disconnect, /* disconnect */
241 ZERO_NULL, /* readwrite */
242 http2_conncheck, /* connection_check */
243 PORT_HTTP, /* defport */
244 CURLPROTO_HTTP, /* protocol */
245 PROTOPT_STREAM /* flags */
246};
247
248static const struct Curl_handler Curl_handler_http2_ssl = {
249 "HTTPS", /* scheme */
250 ZERO_NULL, /* setup_connection */
251 Curl_http, /* do_it */
252 Curl_http_done, /* done */
253 ZERO_NULL, /* do_more */
254 ZERO_NULL, /* connect_it */
255 ZERO_NULL, /* connecting */
256 ZERO_NULL, /* doing */
257 http2_getsock, /* proto_getsock */
258 http2_getsock, /* doing_getsock */
259 ZERO_NULL, /* domore_getsock */
260 http2_perform_getsock, /* perform_getsock */
261 http2_disconnect, /* disconnect */
262 ZERO_NULL, /* readwrite */
263 http2_conncheck, /* connection_check */
264 PORT_HTTP, /* defport */
265 CURLPROTO_HTTPS, /* protocol */
266 PROTOPT_SSL | PROTOPT_STREAM /* flags */
267};
268
269/*
270 * Store nghttp2 version info in this buffer, Prefix with a space. Return
271 * total length written.
272 */
273int Curl_http2_ver(char *p, size_t len)
274{
275 nghttp2_info *h2 = nghttp2_version(0);
276 return snprintf(p, len, " nghttp2/%s", h2->version_str);
277}
278
279/* HTTP/2 error code to name based on the Error Code Registry.
280https://tools.ietf.org/html/rfc7540#page-77
281nghttp2_error_code enums are identical.
282*/
283const char *Curl_http2_strerror(uint32_t err)
284{
285#ifndef NGHTTP2_HAS_HTTP2_STRERROR
286 const char *str[] = {
287 "NO_ERROR", /* 0x0 */
288 "PROTOCOL_ERROR", /* 0x1 */
289 "INTERNAL_ERROR", /* 0x2 */
290 "FLOW_CONTROL_ERROR", /* 0x3 */
291 "SETTINGS_TIMEOUT", /* 0x4 */
292 "STREAM_CLOSED", /* 0x5 */
293 "FRAME_SIZE_ERROR", /* 0x6 */
294 "REFUSED_STREAM", /* 0x7 */
295 "CANCEL", /* 0x8 */
296 "COMPRESSION_ERROR", /* 0x9 */
297 "CONNECT_ERROR", /* 0xA */
298 "ENHANCE_YOUR_CALM", /* 0xB */
299 "INADEQUATE_SECURITY", /* 0xC */
300 "HTTP_1_1_REQUIRED" /* 0xD */
301 };
302 return (err < sizeof str / sizeof str[0]) ? str[err] : "unknown";
303#else
304 return nghttp2_http2_strerror(err);
305#endif
306}
307
308/*
309 * The implementation of nghttp2_send_callback type. Here we write |data| with
310 * size |length| to the network and return the number of bytes actually
311 * written. See the documentation of nghttp2_send_callback for the details.
312 */
313static ssize_t send_callback(nghttp2_session *h2,
314 const uint8_t *data, size_t length, int flags,
315 void *userp)
316{
317 struct connectdata *conn = (struct connectdata *)userp;
318 struct http_conn *c = &conn->proto.httpc;
319 ssize_t written;
320 CURLcode result = CURLE_OK;
321
322 (void)h2;
323 (void)flags;
324
325 written = ((Curl_send*)c->send_underlying)(conn, FIRSTSOCKET,
326 data, length, &result);
327
328 if(result == CURLE_AGAIN) {
329 return NGHTTP2_ERR_WOULDBLOCK;
330 }
331
332 if(written == -1) {
333 failf(conn->data, "Failed sending HTTP2 data");
334 return NGHTTP2_ERR_CALLBACK_FAILURE;
335 }
336
337 if(!written)
338 return NGHTTP2_ERR_WOULDBLOCK;
339
340 return written;
341}
342
343
344/* We pass a pointer to this struct in the push callback, but the contents of
345 the struct are hidden from the user. */
346struct curl_pushheaders {
347 struct Curl_easy *data;
348 const nghttp2_push_promise *frame;
349};
350
351/*
352 * push header access function. Only to be used from within the push callback
353 */
354char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
355{
356 /* Verify that we got a good easy handle in the push header struct, mostly to
357 detect rubbish input fast(er). */
358 if(!h || !GOOD_EASY_HANDLE(h->data))
359 return NULL;
360 else {
361 struct HTTP *stream = h->data->req.protop;
362 if(num < stream->push_headers_used)
363 return stream->push_headers[num];
364 }
365 return NULL;
366}
367
368/*
369 * push header access function. Only to be used from within the push callback
370 */
371char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
372{
373 /* Verify that we got a good easy handle in the push header struct,
374 mostly to detect rubbish input fast(er). Also empty header name
375 is just a rubbish too. We have to allow ":" at the beginning of
376 the header, but header == ":" must be rejected. If we have ':' in
377 the middle of header, it could be matched in middle of the value,
378 this is because we do prefix match.*/
379 if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] ||
380 !strcmp(header, ":") || strchr(header + 1, ':'))
381 return NULL;
382 else {
383 struct HTTP *stream = h->data->req.protop;
384 size_t len = strlen(header);
385 size_t i;
386 for(i = 0; i<stream->push_headers_used; i++) {
387 if(!strncmp(header, stream->push_headers[i], len)) {
388 /* sub-match, make sure that it is followed by a colon */
389 if(stream->push_headers[i][len] != ':')
390 continue;
391 return &stream->push_headers[i][len + 1];
392 }
393 }
394 }
395 return NULL;
396}
397
398static struct Curl_easy *duphandle(struct Curl_easy *data)
399{
400 struct Curl_easy *second = curl_easy_duphandle(data);
401 if(second) {
402 /* setup the request struct */
403 struct HTTP *http = calloc(1, sizeof(struct HTTP));
404 if(!http) {
405 (void)Curl_close(second);
406 second = NULL;
407 }
408 else {
409 second->req.protop = http;
410 http->header_recvbuf = Curl_add_buffer_init();
411 if(!http->header_recvbuf) {
412 free(http);
413 (void)Curl_close(second);
414 second = NULL;
415 }
416 else {
417 Curl_http2_setup_req(second);
418 second->state.stream_weight = data->state.stream_weight;
419 }
420 }
421 }
422 return second;
423}
424
425
426static int push_promise(struct Curl_easy *data,
427 struct connectdata *conn,
428 const nghttp2_push_promise *frame)
429{
430 int rv;
431 DEBUGF(infof(data, "PUSH_PROMISE received, stream %u!\n",
432 frame->promised_stream_id));
433 if(data->multi->push_cb) {
434 struct HTTP *stream;
435 struct HTTP *newstream;
436 struct curl_pushheaders heads;
437 CURLMcode rc;
438 struct http_conn *httpc;
439 size_t i;
440 /* clone the parent */
441 struct Curl_easy *newhandle = duphandle(data);
442 if(!newhandle) {
443 infof(data, "failed to duplicate handle\n");
444 rv = 1; /* FAIL HARD */
445 goto fail;
446 }
447
448 heads.data = data;
449 heads.frame = frame;
450 /* ask the application */
451 DEBUGF(infof(data, "Got PUSH_PROMISE, ask application!\n"));
452
453 stream = data->req.protop;
454 if(!stream) {
455 failf(data, "Internal NULL stream!\n");
456 (void)Curl_close(newhandle);
457 rv = 1;
458 goto fail;
459 }
460
461 rv = data->multi->push_cb(data, newhandle,
462 stream->push_headers_used, &heads,
463 data->multi->push_userp);
464
465 /* free the headers again */
466 for(i = 0; i<stream->push_headers_used; i++)
467 free(stream->push_headers[i]);
468 free(stream->push_headers);
469 stream->push_headers = NULL;
470 stream->push_headers_used = 0;
471
472 if(rv) {
473 /* denied, kill off the new handle again */
474 http2_stream_free(newhandle->req.protop);
475 (void)Curl_close(newhandle);
476 goto fail;
477 }
478
479 newstream = newhandle->req.protop;
480 newstream->stream_id = frame->promised_stream_id;
481 newhandle->req.maxdownload = -1;
482 newhandle->req.size = -1;
483
484 /* approved, add to the multi handle and immediately switch to PERFORM
485 state with the given connection !*/
486 rc = Curl_multi_add_perform(data->multi, newhandle, conn);
487 if(rc) {
488 infof(data, "failed to add handle to multi\n");
489 http2_stream_free(newhandle->req.protop);
490 Curl_close(newhandle);
491 rv = 1;
492 goto fail;
493 }
494
495 httpc = &conn->proto.httpc;
496 nghttp2_session_set_stream_user_data(httpc->h2,
497 frame->promised_stream_id, newhandle);
498 }
499 else {
500 DEBUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n"));
501 rv = 1;
502 }
503 fail:
504 return rv;
505}
506
507static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
508 void *userp)
509{
510 struct connectdata *conn = (struct connectdata *)userp;
511 struct http_conn *httpc = &conn->proto.httpc;
512 struct Curl_easy *data_s = NULL;
513 struct HTTP *stream = NULL;
514 static int lastStream = -1;
515 int rv;
516 size_t left, ncopy;
517 int32_t stream_id = frame->hd.stream_id;
518
519 if(!stream_id) {
520 /* stream ID zero is for connection-oriented stuff */
521 if(frame->hd.type == NGHTTP2_SETTINGS) {
522 uint32_t max_conn = httpc->settings.max_concurrent_streams;
523 DEBUGF(infof(conn->data, "Got SETTINGS\n"));
524 httpc->settings.max_concurrent_streams =
525 nghttp2_session_get_remote_settings(
526 session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
527 httpc->settings.enable_push =
528 nghttp2_session_get_remote_settings(
529 session, NGHTTP2_SETTINGS_ENABLE_PUSH);
530 DEBUGF(infof(conn->data, "MAX_CONCURRENT_STREAMS == %d\n",
531 httpc->settings.max_concurrent_streams));
532 DEBUGF(infof(conn->data, "ENABLE_PUSH == %s\n",
533 httpc->settings.enable_push?"TRUE":"false"));
534 if(max_conn != httpc->settings.max_concurrent_streams) {
535 /* only signal change if the value actually changed */
536 infof(conn->data,
537 "Connection state changed (MAX_CONCURRENT_STREAMS updated)!\n");
538 Curl_multi_connchanged(conn->data->multi);
539 }
540 }
541 return 0;
542 }
543 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
544 if(lastStream != stream_id) {
545 lastStream = stream_id;
546 }
547 if(!data_s) {
548 DEBUGF(infof(conn->data,
549 "No Curl_easy associated with stream: %x\n",
550 stream_id));
551 return 0;
552 }
553
554 stream = data_s->req.protop;
555 if(!stream) {
556 DEBUGF(infof(conn->data, "No proto pointer for stream: %x\n",
557 stream_id));
558 return NGHTTP2_ERR_CALLBACK_FAILURE;
559 }
560
561 DEBUGF(infof(data_s, "on_frame_recv() header %x stream %x\n",
562 frame->hd.type, stream_id));
563
564 switch(frame->hd.type) {
565 case NGHTTP2_DATA:
566 /* If body started on this stream, then receiving DATA is illegal. */
567 if(!stream->bodystarted) {
568 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
569 stream_id, NGHTTP2_PROTOCOL_ERROR);
570
571 if(nghttp2_is_fatal(rv)) {
572 return NGHTTP2_ERR_CALLBACK_FAILURE;
573 }
574 }
575 break;
576 case NGHTTP2_HEADERS:
577 if(stream->bodystarted) {
578 /* Only valid HEADERS after body started is trailer HEADERS. We
579 buffer them in on_header callback. */
580 break;
581 }
582
583 /* nghttp2 guarantees that :status is received, and we store it to
584 stream->status_code */
585 DEBUGASSERT(stream->status_code != -1);
586
587 /* Only final status code signals the end of header */
588 if(stream->status_code / 100 != 1) {
589 stream->bodystarted = TRUE;
590 stream->status_code = -1;
591 }
592
593 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
594
595 left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
596 ncopy = MIN(stream->len, left);
597
598 memcpy(&stream->mem[stream->memlen],
599 stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
600 ncopy);
601 stream->nread_header_recvbuf += ncopy;
602
603 DEBUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n",
604 ncopy, stream_id, stream->mem));
605
606 stream->len -= ncopy;
607 stream->memlen += ncopy;
608
609 data_s->state.drain++;
610 httpc->drain_total++;
611 {
612 /* get the pointer from userp again since it was re-assigned above */
613 struct connectdata *conn_s = (struct connectdata *)userp;
614
615 /* if we receive data for another handle, wake that up */
616 if(conn_s->data != data_s)
617 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
618 }
619 break;
620 case NGHTTP2_PUSH_PROMISE:
621 rv = push_promise(data_s, conn, &frame->push_promise);
622 if(rv) { /* deny! */
623 rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
624 frame->push_promise.promised_stream_id,
625 NGHTTP2_CANCEL);
626 if(nghttp2_is_fatal(rv)) {
627 return rv;
628 }
629 }
630 break;
631 default:
632 DEBUGF(infof(conn->data, "Got frame type %x for stream %u!\n",
633 frame->hd.type, stream_id));
634 break;
635 }
636 return 0;
637}
638
639static int on_invalid_frame_recv(nghttp2_session *session,
640 const nghttp2_frame *frame,
641 int lib_error_code, void *userp)
642{
643 struct Curl_easy *data_s = NULL;
644 (void)userp;
645#if !defined(DEBUGBUILD) || defined(CURL_DISABLE_VERBOSE_STRINGS)
646 (void)lib_error_code;
647#endif
648
649 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
650 if(data_s) {
651 DEBUGF(infof(data_s,
652 "on_invalid_frame_recv() was called, error=%d:%s\n",
653 lib_error_code, nghttp2_strerror(lib_error_code)));
654 }
655 return 0;
656}
657
658static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
659 int32_t stream_id,
660 const uint8_t *data, size_t len, void *userp)
661{
662 struct HTTP *stream;
663 struct Curl_easy *data_s;
664 size_t nread;
665 struct connectdata *conn = (struct connectdata *)userp;
666 (void)session;
667 (void)flags;
668 (void)data;
669
670 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
671
672 /* get the stream from the hash based on Stream ID */
673 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
674 if(!data_s)
675 /* Receiving a Stream ID not in the hash should not happen, this is an
676 internal error more than anything else! */
677 return NGHTTP2_ERR_CALLBACK_FAILURE;
678
679 stream = data_s->req.protop;
680 if(!stream)
681 return NGHTTP2_ERR_CALLBACK_FAILURE;
682
683 nread = MIN(stream->len, len);
684 memcpy(&stream->mem[stream->memlen], data, nread);
685
686 stream->len -= nread;
687 stream->memlen += nread;
688
689 data_s->state.drain++;
690 conn->proto.httpc.drain_total++;
691
692 /* if we receive data for another handle, wake that up */
693 if(conn->data != data_s)
694 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
695
696 DEBUGF(infof(data_s, "%zu data received for stream %u "
697 "(%zu left in buffer %p, total %zu)\n",
698 nread, stream_id,
699 stream->len, stream->mem,
700 stream->memlen));
701
702 if(nread < len) {
703 stream->pausedata = data + nread;
704 stream->pauselen = len - nread;
705 DEBUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
706 ", stream %u\n",
707 len - nread, stream_id));
708 data_s->easy_conn->proto.httpc.pause_stream_id = stream_id;
709
710 return NGHTTP2_ERR_PAUSE;
711 }
712
713 /* pause execution of nghttp2 if we received data for another handle
714 in order to process them first. */
715 if(conn->data != data_s) {
716 data_s->easy_conn->proto.httpc.pause_stream_id = stream_id;
717
718 return NGHTTP2_ERR_PAUSE;
719 }
720
721 return 0;
722}
723
724static int before_frame_send(nghttp2_session *session,
725 const nghttp2_frame *frame,
726 void *userp)
727{
728 struct Curl_easy *data_s;
729 (void)userp;
730
731 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
732 if(data_s) {
733 DEBUGF(infof(data_s, "before_frame_send() was called\n"));
734 }
735
736 return 0;
737}
738static int on_frame_send(nghttp2_session *session,
739 const nghttp2_frame *frame,
740 void *userp)
741{
742 struct Curl_easy *data_s;
743 (void)userp;
744
745 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
746 if(data_s) {
747 DEBUGF(infof(data_s, "on_frame_send() was called, length = %zd\n",
748 frame->hd.length));
749 }
750 return 0;
751}
752static int on_frame_not_send(nghttp2_session *session,
753 const nghttp2_frame *frame,
754 int lib_error_code, void *userp)
755{
756 struct Curl_easy *data_s;
757 (void)userp;
758#if !defined(DEBUGBUILD) || defined(CURL_DISABLE_VERBOSE_STRINGS)
759 (void)lib_error_code;
760#endif
761
762 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
763 if(data_s) {
764 DEBUGF(infof(data_s,
765 "on_frame_not_send() was called, lib_error_code = %d\n",
766 lib_error_code));
767 }
768 return 0;
769}
770static int on_stream_close(nghttp2_session *session, int32_t stream_id,
771 uint32_t error_code, void *userp)
772{
773 struct Curl_easy *data_s;
774 struct HTTP *stream;
775 struct connectdata *conn = (struct connectdata *)userp;
776 (void)session;
777 (void)stream_id;
778
779 if(stream_id) {
780 /* get the stream from the hash based on Stream ID, stream ID zero is for
781 connection-oriented stuff */
782 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
783 if(!data_s) {
784 /* We could get stream ID not in the hash. For example, if we
785 decided to reject stream (e.g., PUSH_PROMISE). */
786 return 0;
787 }
788 DEBUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u\n",
789 Curl_http2_strerror(error_code), error_code, stream_id));
790 stream = data_s->req.protop;
791 if(!stream)
792 return NGHTTP2_ERR_CALLBACK_FAILURE;
793
794 stream->error_code = error_code;
795 stream->closed = TRUE;
796 data_s->state.drain++;
797 conn->proto.httpc.drain_total++;
798
799 /* remove the entry from the hash as the stream is now gone */
800 nghttp2_session_set_stream_user_data(session, stream_id, 0);
801 DEBUGF(infof(data_s, "Removed stream %u hash!\n", stream_id));
802 }
803 return 0;
804}
805
806static int on_begin_headers(nghttp2_session *session,
807 const nghttp2_frame *frame, void *userp)
808{
809 struct HTTP *stream;
810 struct Curl_easy *data_s = NULL;
811 (void)userp;
812
813 data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
814 if(!data_s) {
815 return 0;
816 }
817
818 DEBUGF(infof(data_s, "on_begin_headers() was called\n"));
819
820 if(frame->hd.type != NGHTTP2_HEADERS) {
821 return 0;
822 }
823
824 stream = data_s->req.protop;
825 if(!stream || !stream->bodystarted) {
826 return 0;
827 }
828
829 /* This is trailer HEADERS started. Allocate buffer for them. */
830 DEBUGF(infof(data_s, "trailer field started\n"));
831
832 DEBUGASSERT(stream->trailer_recvbuf == NULL);
833
834 stream->trailer_recvbuf = Curl_add_buffer_init();
835 if(!stream->trailer_recvbuf) {
836 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
837 }
838
839 return 0;
840}
841
842/* Decode HTTP status code. Returns -1 if no valid status code was
843 decoded. */
844static int decode_status_code(const uint8_t *value, size_t len)
845{
846 int i;
847 int res;
848
849 if(len != 3) {
850 return -1;
851 }
852
853 res = 0;
854
855 for(i = 0; i < 3; ++i) {
856 char c = value[i];
857
858 if(c < '0' || c > '9') {
859 return -1;
860 }
861
862 res *= 10;
863 res += c - '0';
864 }
865
866 return res;
867}
868
869/* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
870static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
871 const uint8_t *name, size_t namelen,
872 const uint8_t *value, size_t valuelen,
873 uint8_t flags,
874 void *userp)
875{
876 struct HTTP *stream;
877 struct Curl_easy *data_s;
878 int32_t stream_id = frame->hd.stream_id;
879 struct connectdata *conn = (struct connectdata *)userp;
880 (void)flags;
881
882 DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
883
884 /* get the stream from the hash based on Stream ID */
885 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
886 if(!data_s)
887 /* Receiving a Stream ID not in the hash should not happen, this is an
888 internal error more than anything else! */
889 return NGHTTP2_ERR_CALLBACK_FAILURE;
890
891 stream = data_s->req.protop;
892 if(!stream) {
893 failf(data_s, "Internal NULL stream! 5\n");
894 return NGHTTP2_ERR_CALLBACK_FAILURE;
895 }
896
897 /* Store received PUSH_PROMISE headers to be used when the subsequent
898 PUSH_PROMISE callback comes */
899 if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
900 char *h;
901
902 if(!stream->push_headers) {
903 stream->push_headers_alloc = 10;
904 stream->push_headers = malloc(stream->push_headers_alloc *
905 sizeof(char *));
906 stream->push_headers_used = 0;
907 }
908 else if(stream->push_headers_used ==
909 stream->push_headers_alloc) {
910 char **headp;
911 stream->push_headers_alloc *= 2;
912 headp = Curl_saferealloc(stream->push_headers,
913 stream->push_headers_alloc * sizeof(char *));
914 if(!headp) {
915 stream->push_headers = NULL;
916 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
917 }
918 stream->push_headers = headp;
919 }
920 h = aprintf("%s:%s", name, value);
921 if(h)
922 stream->push_headers[stream->push_headers_used++] = h;
923 return 0;
924 }
925
926 if(stream->bodystarted) {
927 /* This is trailer fields. */
928 /* 3 is for ":" and "\r\n". */
929 uint32_t n = (uint32_t)(namelen + valuelen + 3);
930
931 DEBUGF(infof(data_s, "h2 trailer: %.*s: %.*s\n", namelen, name, valuelen,
932 value));
933
934 Curl_add_buffer(stream->trailer_recvbuf, &n, sizeof(n));
935 Curl_add_buffer(stream->trailer_recvbuf, name, namelen);
936 Curl_add_buffer(stream->trailer_recvbuf, ": ", 2);
937 Curl_add_buffer(stream->trailer_recvbuf, value, valuelen);
938 Curl_add_buffer(stream->trailer_recvbuf, "\r\n\0", 3);
939
940 return 0;
941 }
942
943 if(namelen == sizeof(":status") - 1 &&
944 memcmp(":status", name, namelen) == 0) {
945 /* nghttp2 guarantees :status is received first and only once, and
946 value is 3 digits status code, and decode_status_code always
947 succeeds. */
948 stream->status_code = decode_status_code(value, valuelen);
949 DEBUGASSERT(stream->status_code != -1);
950
951 Curl_add_buffer(stream->header_recvbuf, "HTTP/2 ", 7);
952 Curl_add_buffer(stream->header_recvbuf, value, valuelen);
953 /* the space character after the status code is mandatory */
954 Curl_add_buffer(stream->header_recvbuf, " \r\n", 3);
955 /* if we receive data for another handle, wake that up */
956 if(conn->data != data_s)
957 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
958
959 DEBUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)\n",
960 stream->status_code, data_s));
961 return 0;
962 }
963
964 /* nghttp2 guarantees that namelen > 0, and :status was already
965 received, and this is not pseudo-header field . */
966 /* convert to a HTTP1-style header */
967 Curl_add_buffer(stream->header_recvbuf, name, namelen);
968 Curl_add_buffer(stream->header_recvbuf, ": ", 2);
969 Curl_add_buffer(stream->header_recvbuf, value, valuelen);
970 Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
971 /* if we receive data for another handle, wake that up */
972 if(conn->data != data_s)
973 Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
974
975 DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
976 value));
977
978 return 0; /* 0 is successful */
979}
980
981static ssize_t data_source_read_callback(nghttp2_session *session,
982 int32_t stream_id,
983 uint8_t *buf, size_t length,
984 uint32_t *data_flags,
985 nghttp2_data_source *source,
986 void *userp)
987{
988 struct Curl_easy *data_s;
989 struct HTTP *stream = NULL;
990 size_t nread;
991 (void)source;
992 (void)userp;
993
994 if(stream_id) {
995 /* get the stream from the hash based on Stream ID, stream ID zero is for
996 connection-oriented stuff */
997 data_s = nghttp2_session_get_stream_user_data(session, stream_id);
998 if(!data_s)
999 /* Receiving a Stream ID not in the hash should not happen, this is an
1000 internal error more than anything else! */
1001 return NGHTTP2_ERR_CALLBACK_FAILURE;
1002
1003 stream = data_s->req.protop;
1004 if(!stream)
1005 return NGHTTP2_ERR_CALLBACK_FAILURE;
1006 }
1007 else
1008 return NGHTTP2_ERR_INVALID_ARGUMENT;
1009
1010 nread = MIN(stream->upload_len, length);
1011 if(nread > 0) {
1012 memcpy(buf, stream->upload_mem, nread);
1013 stream->upload_mem += nread;
1014 stream->upload_len -= nread;
1015 if(data_s->state.infilesize != -1)
1016 stream->upload_left -= nread;
1017 }
1018
1019 if(stream->upload_left == 0)
1020 *data_flags = NGHTTP2_DATA_FLAG_EOF;
1021 else if(nread == 0)
1022 return NGHTTP2_ERR_DEFERRED;
1023
1024 DEBUGF(infof(data_s, "data_source_read_callback: "
1025 "returns %zu bytes stream %u\n",
1026 nread, stream_id));
1027
1028 return nread;
1029}
1030
1031#define H2_BUFSIZE 32768
1032
1033#ifdef NGHTTP2_HAS_ERROR_CALLBACK
1034static int error_callback(nghttp2_session *session,
1035 const char *msg,
1036 size_t len,
1037 void *userp)
1038{
1039 struct connectdata *conn = (struct connectdata *)userp;
1040 (void)session;
1041 infof(conn->data, "http2 error: %.*s\n", len, msg);
1042 return 0;
1043}
1044#endif
1045
1046static void populate_settings(struct connectdata *conn,
1047 struct http_conn *httpc)
1048{
1049 nghttp2_settings_entry *iv = httpc->local_settings;
1050
1051 iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
1052 iv[0].value = 100;
1053
1054 iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
1055 iv[1].value = HTTP2_HUGE_WINDOW_SIZE;
1056
1057 iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
1058 iv[2].value = conn->data->multi->push_cb != NULL;
1059
1060 httpc->local_settings_num = 3;
1061}
1062
1063void Curl_http2_done(struct connectdata *conn, bool premature)
1064{
1065 struct Curl_easy *data = conn->data;
1066 struct HTTP *http = data->req.protop;
1067 struct http_conn *httpc = &conn->proto.httpc;
1068
1069 if(http->header_recvbuf) {
1070 DEBUGF(infof(data, "free header_recvbuf!!\n"));
1071 Curl_add_buffer_free(http->header_recvbuf);
1072 http->header_recvbuf = NULL; /* clear the pointer */
1073 Curl_add_buffer_free(http->trailer_recvbuf);
1074 http->trailer_recvbuf = NULL; /* clear the pointer */
1075 if(http->push_headers) {
1076 /* if they weren't used and then freed before */
1077 for(; http->push_headers_used > 0; --http->push_headers_used) {
1078 free(http->push_headers[http->push_headers_used - 1]);
1079 }
1080 free(http->push_headers);
1081 http->push_headers = NULL;
1082 }
1083 }
1084
1085 if(premature) {
1086 /* RST_STREAM */
1087 nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE, http->stream_id,
1088 NGHTTP2_STREAM_CLOSED);
1089 if(http->stream_id == httpc->pause_stream_id) {
1090 infof(data, "stopped the pause stream!\n");
1091 httpc->pause_stream_id = 0;
1092 }
1093 }
1094 if(http->stream_id) {
1095 nghttp2_session_set_stream_user_data(httpc->h2, http->stream_id, 0);
1096 http->stream_id = 0;
1097 }
1098}
1099
1100/*
1101 * Initialize nghttp2 for a Curl connection
1102 */
1103CURLcode Curl_http2_init(struct connectdata *conn)
1104{
1105 if(!conn->proto.httpc.h2) {
1106 int rc;
1107 nghttp2_session_callbacks *callbacks;
1108
1109 conn->proto.httpc.inbuf = malloc(H2_BUFSIZE);
1110 if(conn->proto.httpc.inbuf == NULL)
1111 return CURLE_OUT_OF_MEMORY;
1112
1113 rc = nghttp2_session_callbacks_new(&callbacks);
1114
1115 if(rc) {
1116 failf(conn->data, "Couldn't initialize nghttp2 callbacks!");
1117 return CURLE_OUT_OF_MEMORY; /* most likely at least */
1118 }
1119
1120 /* nghttp2_send_callback */
1121 nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
1122 /* nghttp2_on_frame_recv_callback */
1123 nghttp2_session_callbacks_set_on_frame_recv_callback
1124 (callbacks, on_frame_recv);
1125 /* nghttp2_on_invalid_frame_recv_callback */
1126 nghttp2_session_callbacks_set_on_invalid_frame_recv_callback
1127 (callbacks, on_invalid_frame_recv);
1128 /* nghttp2_on_data_chunk_recv_callback */
1129 nghttp2_session_callbacks_set_on_data_chunk_recv_callback
1130 (callbacks, on_data_chunk_recv);
1131 /* nghttp2_before_frame_send_callback */
1132 nghttp2_session_callbacks_set_before_frame_send_callback
1133 (callbacks, before_frame_send);
1134 /* nghttp2_on_frame_send_callback */
1135 nghttp2_session_callbacks_set_on_frame_send_callback
1136 (callbacks, on_frame_send);
1137 /* nghttp2_on_frame_not_send_callback */
1138 nghttp2_session_callbacks_set_on_frame_not_send_callback
1139 (callbacks, on_frame_not_send);
1140 /* nghttp2_on_stream_close_callback */
1141 nghttp2_session_callbacks_set_on_stream_close_callback
1142 (callbacks, on_stream_close);
1143 /* nghttp2_on_begin_headers_callback */
1144 nghttp2_session_callbacks_set_on_begin_headers_callback
1145 (callbacks, on_begin_headers);
1146 /* nghttp2_on_header_callback */
1147 nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
1148
1149 nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
1150
1151 /* The nghttp2 session is not yet setup, do it */
1152 rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
1153
1154 nghttp2_session_callbacks_del(callbacks);
1155
1156 if(rc) {
1157 failf(conn->data, "Couldn't initialize nghttp2!");
1158 return CURLE_OUT_OF_MEMORY; /* most likely at least */
1159 }
1160 }
1161 return CURLE_OK;
1162}
1163
1164/*
1165 * Append headers to ask for a HTTP1.1 to HTTP2 upgrade.
1166 */
1167CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req,
1168 struct connectdata *conn)
1169{
1170 CURLcode result;
1171 ssize_t binlen;
1172 char *base64;
1173 size_t blen;
1174 struct SingleRequest *k = &conn->data->req;
1175 uint8_t *binsettings = conn->proto.httpc.binsettings;
1176 struct http_conn *httpc = &conn->proto.httpc;
1177
1178 populate_settings(conn, httpc);
1179
1180 /* this returns number of bytes it wrote */
1181 binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
1182 httpc->local_settings,
1183 httpc->local_settings_num);
1184 if(!binlen) {
1185 failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
1186 Curl_add_buffer_free(req);
1187 return CURLE_FAILED_INIT;
1188 }
1189 conn->proto.httpc.binlen = binlen;
1190
1191 result = Curl_base64url_encode(conn->data, (const char *)binsettings, binlen,
1192 &base64, &blen);
1193 if(result) {
1194 Curl_add_buffer_free(req);
1195 return result;
1196 }
1197
1198 result = Curl_add_bufferf(req,
1199 "Connection: Upgrade, HTTP2-Settings\r\n"
1200 "Upgrade: %s\r\n"
1201 "HTTP2-Settings: %s\r\n",
1202 NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
1203 free(base64);
1204
1205 k->upgr101 = UPGR101_REQUESTED;
1206
1207 return result;
1208}
1209
1210/*
1211 * Returns nonzero if current HTTP/2 session should be closed.
1212 */
1213static int should_close_session(struct http_conn *httpc)
1214{
1215 return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) &&
1216 !nghttp2_session_want_write(httpc->h2);
1217}
1218
1219static int h2_session_send(struct Curl_easy *data,
1220 nghttp2_session *h2);
1221
1222/*
1223 * h2_process_pending_input() processes pending input left in
1224 * httpc->inbuf. Then, call h2_session_send() to send pending data.
1225 * This function returns 0 if it succeeds, or -1 and error code will
1226 * be assigned to *err.
1227 */
1228static int h2_process_pending_input(struct Curl_easy *data,
1229 struct http_conn *httpc,
1230 CURLcode *err)
1231{
1232 ssize_t nread;
1233 char *inbuf;
1234 ssize_t rv;
1235
1236 nread = httpc->inbuflen - httpc->nread_inbuf;
1237 inbuf = httpc->inbuf + httpc->nread_inbuf;
1238
1239 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
1240 if(rv < 0) {
1241 failf(data,
1242 "h2_process_pending_input: nghttp2_session_mem_recv() returned "
1243 "%d:%s\n", rv, nghttp2_strerror((int)rv));
1244 *err = CURLE_RECV_ERROR;
1245 return -1;
1246 }
1247
1248 if(nread == rv) {
1249 DEBUGF(infof(data,
1250 "h2_process_pending_input: All data in connection buffer "
1251 "processed\n"));
1252 httpc->inbuflen = 0;
1253 httpc->nread_inbuf = 0;
1254 }
1255 else {
1256 httpc->nread_inbuf += rv;
1257 DEBUGF(infof(data,
1258 "h2_process_pending_input: %zu bytes left in connection "
1259 "buffer\n",
1260 httpc->inbuflen - httpc->nread_inbuf));
1261 }
1262
1263 rv = h2_session_send(data, httpc->h2);
1264 if(rv != 0) {
1265 *err = CURLE_SEND_ERROR;
1266 return -1;
1267 }
1268
1269 if(should_close_session(httpc)) {
1270 DEBUGF(infof(data,
1271 "h2_process_pending_input: nothing to do in this session\n"));
1272 *err = CURLE_HTTP2;
1273 return -1;
1274 }
1275
1276 return 0;
1277}
1278
1279/*
1280 * Called from transfer.c:done_sending when we stop uploading.
1281 */
1282CURLcode Curl_http2_done_sending(struct connectdata *conn)
1283{
1284 CURLcode result = CURLE_OK;
1285
1286 if((conn->handler == &Curl_handler_http2_ssl) ||
1287 (conn->handler == &Curl_handler_http2)) {
1288 /* make sure this is only attempted for HTTP/2 transfers */
1289
1290 struct HTTP *stream = conn->data->req.protop;
1291
1292 if(stream->upload_left) {
1293 /* If the stream still thinks there's data left to upload. */
1294 struct http_conn *httpc = &conn->proto.httpc;
1295 nghttp2_session *h2 = httpc->h2;
1296
1297 stream->upload_left = 0; /* DONE! */
1298
1299 /* resume sending here to trigger the callback to get called again so
1300 that it can signal EOF to nghttp2 */
1301 (void)nghttp2_session_resume_data(h2, stream->stream_id);
1302
1303 (void)h2_process_pending_input(conn->data, httpc, &result);
1304 }
1305 }
1306 return result;
1307}
1308
1309
1310static ssize_t http2_handle_stream_close(struct connectdata *conn,
1311 struct Curl_easy *data,
1312 struct HTTP *stream, CURLcode *err)
1313{
1314 char *trailer_pos, *trailer_end;
1315 CURLcode result;
1316 struct http_conn *httpc = &conn->proto.httpc;
1317
1318 if(httpc->pause_stream_id == stream->stream_id) {
1319 httpc->pause_stream_id = 0;
1320 }
1321
1322 DEBUGASSERT(httpc->drain_total >= data->state.drain);
1323 httpc->drain_total -= data->state.drain;
1324 data->state.drain = 0;
1325
1326 if(httpc->pause_stream_id == 0) {
1327 if(h2_process_pending_input(data, httpc, err) != 0) {
1328 return -1;
1329 }
1330 }
1331
1332 DEBUGASSERT(data->state.drain == 0);
1333
1334 /* Reset to FALSE to prevent infinite loop in readwrite_data function. */
1335 stream->closed = FALSE;
1336 if(stream->error_code != NGHTTP2_NO_ERROR) {
1337 failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %d)",
1338 stream->stream_id, Curl_http2_strerror(stream->error_code),
1339 stream->error_code);
1340 *err = CURLE_HTTP2_STREAM;
1341 return -1;
1342 }
1343
1344 if(!stream->bodystarted) {
1345 failf(data, "HTTP/2 stream %u was closed cleanly, but before getting "
1346 " all response header fields, teated as error",
1347 stream->stream_id);
1348 *err = CURLE_HTTP2_STREAM;
1349 return -1;
1350 }
1351
1352 if(stream->trailer_recvbuf && stream->trailer_recvbuf->buffer) {
1353 trailer_pos = stream->trailer_recvbuf->buffer;
1354 trailer_end = trailer_pos + stream->trailer_recvbuf->size_used;
1355
1356 for(; trailer_pos < trailer_end;) {
1357 uint32_t n;
1358 memcpy(&n, trailer_pos, sizeof(n));
1359 trailer_pos += sizeof(n);
1360
1361 result = Curl_client_write(conn, CLIENTWRITE_HEADER, trailer_pos, n);
1362 if(result) {
1363 *err = result;
1364 return -1;
1365 }
1366
1367 trailer_pos += n + 1;
1368 }
1369 }
1370
1371 stream->close_handled = TRUE;
1372
1373 DEBUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close\n"));
1374 return 0;
1375}
1376
1377/*
1378 * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
1379 * and dependency to the peer. It also stores the updated values in the state
1380 * struct.
1381 */
1382
1383static void h2_pri_spec(struct Curl_easy *data,
1384 nghttp2_priority_spec *pri_spec)
1385{
1386 struct HTTP *depstream = (data->set.stream_depends_on?
1387 data->set.stream_depends_on->req.protop:NULL);
1388 int32_t depstream_id = depstream? depstream->stream_id:0;
1389 nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_weight,
1390 data->set.stream_depends_e);
1391 data->state.stream_weight = data->set.stream_weight;
1392 data->state.stream_depends_e = data->set.stream_depends_e;
1393 data->state.stream_depends_on = data->set.stream_depends_on;
1394}
1395
1396/*
1397 * h2_session_send() checks if there's been an update in the priority /
1398 * dependency settings and if so it submits a PRIORITY frame with the updated
1399 * info.
1400 */
1401static int h2_session_send(struct Curl_easy *data,
1402 nghttp2_session *h2)
1403{
1404 struct HTTP *stream = data->req.protop;
1405 if((data->set.stream_weight != data->state.stream_weight) ||
1406 (data->set.stream_depends_e != data->state.stream_depends_e) ||
1407 (data->set.stream_depends_on != data->state.stream_depends_on) ) {
1408 /* send new weight and/or dependency */
1409 nghttp2_priority_spec pri_spec;
1410 int rv;
1411
1412 h2_pri_spec(data, &pri_spec);
1413
1414 DEBUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)\n",
1415 stream->stream_id, data));
1416 rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
1417 &pri_spec);
1418 if(rv)
1419 return rv;
1420 }
1421
1422 return nghttp2_session_send(h2);
1423}
1424
1425static ssize_t http2_recv(struct connectdata *conn, int sockindex,
1426 char *mem, size_t len, CURLcode *err)
1427{
1428 CURLcode result = CURLE_OK;
1429 ssize_t rv;
1430 ssize_t nread;
1431 struct http_conn *httpc = &conn->proto.httpc;
1432 struct Curl_easy *data = conn->data;
1433 struct HTTP *stream = data->req.protop;
1434
1435 (void)sockindex; /* we always do HTTP2 on sockindex 0 */
1436
1437 if(should_close_session(httpc)) {
1438 DEBUGF(infof(data,
1439 "http2_recv: nothing to do in this session\n"));
1440 *err = CURLE_HTTP2;
1441 return -1;
1442 }
1443
1444 /* Nullify here because we call nghttp2_session_send() and they
1445 might refer to the old buffer. */
1446 stream->upload_mem = NULL;
1447 stream->upload_len = 0;
1448
1449 /*
1450 * At this point 'stream' is just in the Curl_easy the connection
1451 * identifies as its owner at this time.
1452 */
1453
1454 if(stream->bodystarted &&
1455 stream->nread_header_recvbuf < stream->header_recvbuf->size_used) {
1456 /* If there is body data pending for this stream to return, do that */
1457 size_t left =
1458 stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
1459 size_t ncopy = MIN(len, left);
1460 memcpy(mem, stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
1461 ncopy);
1462 stream->nread_header_recvbuf += ncopy;
1463
1464 DEBUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf\n",
1465 (int)ncopy));
1466 return ncopy;
1467 }
1468
1469 DEBUGF(infof(data, "http2_recv: easy %p (stream %u)\n",
1470 data, stream->stream_id));
1471
1472 if((data->state.drain) && stream->memlen) {
1473 DEBUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n",
1474 stream->memlen, stream->stream_id,
1475 stream->mem, mem));
1476 if(mem != stream->mem) {
1477 /* if we didn't get the same buffer this time, we must move the data to
1478 the beginning */
1479 memmove(mem, stream->mem, stream->memlen);
1480 stream->len = len - stream->memlen;
1481 stream->mem = mem;
1482 }
1483 if(httpc->pause_stream_id == stream->stream_id && !stream->pausedata) {
1484 /* We have paused nghttp2, but we have no pause data (see
1485 on_data_chunk_recv). */
1486 httpc->pause_stream_id = 0;
1487 if(h2_process_pending_input(data, httpc, &result) != 0) {
1488 *err = result;
1489 return -1;
1490 }
1491 }
1492 }
1493 else if(stream->pausedata) {
1494 DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
1495 nread = MIN(len, stream->pauselen);
1496 memcpy(mem, stream->pausedata, nread);
1497
1498 stream->pausedata += nread;
1499 stream->pauselen -= nread;
1500
1501 infof(data, "%zu data bytes written\n", nread);
1502 if(stream->pauselen == 0) {
1503 DEBUGF(infof(data, "Unpaused by stream %u\n", stream->stream_id));
1504 DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
1505 httpc->pause_stream_id = 0;
1506
1507 stream->pausedata = NULL;
1508 stream->pauselen = 0;
1509
1510 /* When NGHTTP2_ERR_PAUSE is returned from
1511 data_source_read_callback, we might not process DATA frame
1512 fully. Calling nghttp2_session_mem_recv() again will
1513 continue to process DATA frame, but if there is no incoming
1514 frames, then we have to call it again with 0-length data.
1515 Without this, on_stream_close callback will not be called,
1516 and stream could be hanged. */
1517 if(h2_process_pending_input(data, httpc, &result) != 0) {
1518 *err = result;
1519 return -1;
1520 }
1521 }
1522 DEBUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u\n",
1523 nread, stream->stream_id));
1524 return nread;
1525 }
1526 else if(httpc->pause_stream_id) {
1527 /* If a stream paused nghttp2_session_mem_recv previously, and has
1528 not processed all data, it still refers to the buffer in
1529 nghttp2_session. If we call nghttp2_session_mem_recv(), we may
1530 overwrite that buffer. To avoid that situation, just return
1531 here with CURLE_AGAIN. This could be busy loop since data in
1532 socket is not read. But it seems that usually streams are
1533 notified with its drain property, and socket is read again
1534 quickly. */
1535 DEBUGF(infof(data, "stream %x is paused, pause id: %x\n",
1536 stream->stream_id, httpc->pause_stream_id));
1537 *err = CURLE_AGAIN;
1538 return -1;
1539 }
1540 else {
1541 char *inbuf;
1542 /* remember where to store incoming data for this stream and how big the
1543 buffer is */
1544 stream->mem = mem;
1545 stream->len = len;
1546 stream->memlen = 0;
1547
1548 if(httpc->inbuflen == 0) {
1549 nread = ((Curl_recv *)httpc->recv_underlying)(
1550 conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
1551
1552 if(nread == -1) {
1553 if(result != CURLE_AGAIN)
1554 failf(data, "Failed receiving HTTP2 data");
1555 else if(stream->closed)
1556 /* received when the stream was already closed! */
1557 return http2_handle_stream_close(conn, data, stream, err);
1558
1559 *err = result;
1560 return -1;
1561 }
1562
1563 if(nread == 0) {
1564 failf(data, "Unexpected EOF");
1565 *err = CURLE_RECV_ERROR;
1566 return -1;
1567 }
1568
1569 DEBUGF(infof(data, "nread=%zd\n", nread));
1570
1571 httpc->inbuflen = nread;
1572 inbuf = httpc->inbuf;
1573 }
1574 else {
1575 nread = httpc->inbuflen - httpc->nread_inbuf;
1576 inbuf = httpc->inbuf + httpc->nread_inbuf;
1577
1578 DEBUGF(infof(data, "Use data left in connection buffer, nread=%zd\n",
1579 nread));
1580 }
1581 rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
1582
1583 if(nghttp2_is_fatal((int)rv)) {
1584 failf(data, "nghttp2_session_mem_recv() returned %d:%s\n",
1585 rv, nghttp2_strerror((int)rv));
1586 *err = CURLE_RECV_ERROR;
1587 return -1;
1588 }
1589 DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", rv));
1590 if(nread == rv) {
1591 DEBUGF(infof(data, "All data in connection buffer processed\n"));
1592 httpc->inbuflen = 0;
1593 httpc->nread_inbuf = 0;
1594 }
1595 else {
1596 httpc->nread_inbuf += rv;
1597 DEBUGF(infof(data, "%zu bytes left in connection buffer\n",
1598 httpc->inbuflen - httpc->nread_inbuf));
1599 }
1600 /* Always send pending frames in nghttp2 session, because
1601 nghttp2_session_mem_recv() may queue new frame */
1602 rv = h2_session_send(data, httpc->h2);
1603 if(rv != 0) {
1604 *err = CURLE_SEND_ERROR;
1605 return -1;
1606 }
1607
1608 if(should_close_session(httpc)) {
1609 DEBUGF(infof(data, "http2_recv: nothing to do in this session\n"));
1610 *err = CURLE_HTTP2;
1611 return -1;
1612 }
1613 }
1614 if(stream->memlen) {
1615 ssize_t retlen = stream->memlen;
1616 DEBUGF(infof(data, "http2_recv: returns %zd for stream %u\n",
1617 retlen, stream->stream_id));
1618 stream->memlen = 0;
1619
1620 if(httpc->pause_stream_id == stream->stream_id) {
1621 /* data for this stream is returned now, but this stream caused a pause
1622 already so we need it called again asap */
1623 DEBUGF(infof(data, "Data returned for PAUSED stream %u\n",
1624 stream->stream_id));
1625 }
1626 else if(!stream->closed) {
1627 DEBUGASSERT(httpc->drain_total >= data->state.drain);
1628 httpc->drain_total -= data->state.drain;
1629 data->state.drain = 0; /* this stream is hereby drained */
1630 }
1631
1632 return retlen;
1633 }
1634 /* If stream is closed, return 0 to signal the http routine to close
1635 the connection */
1636 if(stream->closed) {
1637 return http2_handle_stream_close(conn, data, stream, err);
1638 }
1639 *err = CURLE_AGAIN;
1640 DEBUGF(infof(data, "http2_recv returns AGAIN for stream %u\n",
1641 stream->stream_id));
1642 return -1;
1643}
1644
1645/* Index where :authority header field will appear in request header
1646 field list. */
1647#define AUTHORITY_DST_IDX 3
1648
1649#define HEADER_OVERFLOW(x) \
1650 (x.namelen > (uint16_t)-1 || x.valuelen > (uint16_t)-1 - x.namelen)
1651
1652/*
1653 * Check header memory for the token "trailers".
1654 * Parse the tokens as separated by comma and surrounded by whitespace.
1655 * Returns TRUE if found or FALSE if not.
1656 */
1657static bool contains_trailers(const char *p, size_t len)
1658{
1659 const char *end = p + len;
1660 for(;;) {
1661 for(; p != end && (*p == ' ' || *p == '\t'); ++p)
1662 ;
1663 if(p == end || (size_t)(end - p) < sizeof("trailers") - 1)
1664 return FALSE;
1665 if(strncasecompare("trailers", p, sizeof("trailers") - 1)) {
1666 p += sizeof("trailers") - 1;
1667 for(; p != end && (*p == ' ' || *p == '\t'); ++p)
1668 ;
1669 if(p == end || *p == ',')
1670 return TRUE;
1671 }
1672 /* skip to next token */
1673 for(; p != end && *p != ','; ++p)
1674 ;
1675 if(p == end)
1676 return FALSE;
1677 ++p;
1678 }
1679}
1680
1681typedef enum {
1682 /* Send header to server */
1683 HEADERINST_FORWARD,
1684 /* Don't send header to server */
1685 HEADERINST_IGNORE,
1686 /* Discard header, and replace it with "te: trailers" */
1687 HEADERINST_TE_TRAILERS
1688} header_instruction;
1689
1690/* Decides how to treat given header field. */
1691static header_instruction inspect_header(const char *name, size_t namelen,
1692 const char *value, size_t valuelen) {
1693 switch(namelen) {
1694 case 2:
1695 if(!strncasecompare("te", name, namelen))
1696 return HEADERINST_FORWARD;
1697
1698 return contains_trailers(value, valuelen) ?
1699 HEADERINST_TE_TRAILERS : HEADERINST_IGNORE;
1700 case 7:
1701 return strncasecompare("upgrade", name, namelen) ?
1702 HEADERINST_IGNORE : HEADERINST_FORWARD;
1703 case 10:
1704 return (strncasecompare("connection", name, namelen) ||
1705 strncasecompare("keep-alive", name, namelen)) ?
1706 HEADERINST_IGNORE : HEADERINST_FORWARD;
1707 case 16:
1708 return strncasecompare("proxy-connection", name, namelen) ?
1709 HEADERINST_IGNORE : HEADERINST_FORWARD;
1710 case 17:
1711 return strncasecompare("transfer-encoding", name, namelen) ?
1712 HEADERINST_IGNORE : HEADERINST_FORWARD;
1713 default:
1714 return HEADERINST_FORWARD;
1715 }
1716}
1717
1718static ssize_t http2_send(struct connectdata *conn, int sockindex,
1719 const void *mem, size_t len, CURLcode *err)
1720{
1721 /*
1722 * BIG TODO: Currently, we send request in this function, but this
1723 * function is also used to send request body. It would be nice to
1724 * add dedicated function for request.
1725 */
1726 int rv;
1727 struct http_conn *httpc = &conn->proto.httpc;
1728 struct HTTP *stream = conn->data->req.protop;
1729 nghttp2_nv *nva = NULL;
1730 size_t nheader;
1731 size_t i;
1732 size_t authority_idx;
1733 char *hdbuf = (char *)mem;
1734 char *end, *line_end;
1735 nghttp2_data_provider data_prd;
1736 int32_t stream_id;
1737 nghttp2_session *h2 = httpc->h2;
1738 nghttp2_priority_spec pri_spec;
1739
1740 (void)sockindex;
1741
1742 DEBUGF(infof(conn->data, "http2_send len=%zu\n", len));
1743
1744 if(stream->stream_id != -1) {
1745 if(stream->close_handled) {
1746 infof(conn->data, "stream %d closed\n", stream->stream_id);
1747 *err = CURLE_HTTP2_STREAM;
1748 return -1;
1749 }
1750 else if(stream->closed) {
1751 return http2_handle_stream_close(conn, conn->data, stream, err);
1752 }
1753 /* If stream_id != -1, we have dispatched request HEADERS, and now
1754 are going to send or sending request body in DATA frame */
1755 stream->upload_mem = mem;
1756 stream->upload_len = len;
1757 nghttp2_session_resume_data(h2, stream->stream_id);
1758 rv = h2_session_send(conn->data, h2);
1759 if(nghttp2_is_fatal(rv)) {
1760 *err = CURLE_SEND_ERROR;
1761 return -1;
1762 }
1763 len -= stream->upload_len;
1764
1765 /* Nullify here because we call nghttp2_session_send() and they
1766 might refer to the old buffer. */
1767 stream->upload_mem = NULL;
1768 stream->upload_len = 0;
1769
1770 if(should_close_session(httpc)) {
1771 DEBUGF(infof(conn->data, "http2_send: nothing to do in this session\n"));
1772 *err = CURLE_HTTP2;
1773 return -1;
1774 }
1775
1776 if(stream->upload_left) {
1777 /* we are sure that we have more data to send here. Calling the
1778 following API will make nghttp2_session_want_write() return
1779 nonzero if remote window allows it, which then libcurl checks
1780 socket is writable or not. See http2_perform_getsock(). */
1781 nghttp2_session_resume_data(h2, stream->stream_id);
1782 }
1783
1784 DEBUGF(infof(conn->data, "http2_send returns %zu for stream %u\n", len,
1785 stream->stream_id));
1786 return len;
1787 }
1788
1789 /* Calculate number of headers contained in [mem, mem + len) */
1790 /* Here, we assume the curl http code generate *correct* HTTP header
1791 field block */
1792 nheader = 0;
1793 for(i = 1; i < len; ++i) {
1794 if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') {
1795 ++nheader;
1796 ++i;
1797 }
1798 }
1799 if(nheader < 2)
1800 goto fail;
1801
1802 /* We counted additional 2 \r\n in the first and last line. We need 3
1803 new headers: :method, :path and :scheme. Therefore we need one
1804 more space. */
1805 nheader += 1;
1806 nva = malloc(sizeof(nghttp2_nv) * nheader);
1807 if(nva == NULL) {
1808 *err = CURLE_OUT_OF_MEMORY;
1809 return -1;
1810 }
1811
1812 /* Extract :method, :path from request line */
1813 line_end = strstr(hdbuf, "\r\n");
1814
1815 /* Method does not contain spaces */
1816 end = memchr(hdbuf, ' ', line_end - hdbuf);
1817 if(!end || end == hdbuf)
1818 goto fail;
1819 nva[0].name = (unsigned char *)":method";
1820 nva[0].namelen = strlen((char *)nva[0].name);
1821 nva[0].value = (unsigned char *)hdbuf;
1822 nva[0].valuelen = (size_t)(end - hdbuf);
1823 nva[0].flags = NGHTTP2_NV_FLAG_NONE;
1824 if(HEADER_OVERFLOW(nva[0])) {
1825 failf(conn->data, "Failed sending HTTP request: Header overflow");
1826 goto fail;
1827 }
1828
1829 hdbuf = end + 1;
1830
1831 /* Path may contain spaces so scan backwards */
1832 end = NULL;
1833 for(i = (size_t)(line_end - hdbuf); i; --i) {
1834 if(hdbuf[i - 1] == ' ') {
1835 end = &hdbuf[i - 1];
1836 break;
1837 }
1838 }
1839 if(!end || end == hdbuf)
1840 goto fail;
1841 nva[1].name = (unsigned char *)":path";
1842 nva[1].namelen = strlen((char *)nva[1].name);
1843 nva[1].value = (unsigned char *)hdbuf;
1844 nva[1].valuelen = (size_t)(end - hdbuf);
1845 nva[1].flags = NGHTTP2_NV_FLAG_NONE;
1846 if(HEADER_OVERFLOW(nva[1])) {
1847 failf(conn->data, "Failed sending HTTP request: Header overflow");
1848 goto fail;
1849 }
1850
1851 nva[2].name = (unsigned char *)":scheme";
1852 nva[2].namelen = strlen((char *)nva[2].name);
1853 if(conn->handler->flags & PROTOPT_SSL)
1854 nva[2].value = (unsigned char *)"https";
1855 else
1856 nva[2].value = (unsigned char *)"http";
1857 nva[2].valuelen = strlen((char *)nva[2].value);
1858 nva[2].flags = NGHTTP2_NV_FLAG_NONE;
1859 if(HEADER_OVERFLOW(nva[2])) {
1860 failf(conn->data, "Failed sending HTTP request: Header overflow");
1861 goto fail;
1862 }
1863
1864 authority_idx = 0;
1865 i = 3;
1866 while(i < nheader) {
1867 size_t hlen;
1868
1869 hdbuf = line_end + 2;
1870
1871 line_end = strstr(hdbuf, "\r\n");
1872 if(line_end == hdbuf)
1873 goto fail;
1874
1875 /* header continuation lines are not supported */
1876 if(*hdbuf == ' ' || *hdbuf == '\t')
1877 goto fail;
1878
1879 for(end = hdbuf; end < line_end && *end != ':'; ++end)
1880 ;
1881 if(end == hdbuf || end == line_end)
1882 goto fail;
1883 hlen = end - hdbuf;
1884
1885 if(hlen == 4 && strncasecompare("host", hdbuf, 4)) {
1886 authority_idx = i;
1887 nva[i].name = (unsigned char *)":authority";
1888 nva[i].namelen = strlen((char *)nva[i].name);
1889 }
1890 else {
1891 nva[i].name = (unsigned char *)hdbuf;
1892 nva[i].namelen = (size_t)(end - hdbuf);
1893 }
1894 hdbuf = end + 1;
1895 while(*hdbuf == ' ' || *hdbuf == '\t')
1896 ++hdbuf;
1897 end = line_end;
1898
1899 switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf,
1900 end - hdbuf)) {
1901 case HEADERINST_IGNORE:
1902 /* skip header fields prohibited by HTTP/2 specification. */
1903 --nheader;
1904 continue;
1905 case HEADERINST_TE_TRAILERS:
1906 nva[i].value = (uint8_t*)"trailers";
1907 nva[i].valuelen = sizeof("trailers") - 1;
1908 break;
1909 default:
1910 nva[i].value = (unsigned char *)hdbuf;
1911 nva[i].valuelen = (size_t)(end - hdbuf);
1912 }
1913
1914 nva[i].flags = NGHTTP2_NV_FLAG_NONE;
1915 if(HEADER_OVERFLOW(nva[i])) {
1916 failf(conn->data, "Failed sending HTTP request: Header overflow");
1917 goto fail;
1918 }
1919 ++i;
1920 }
1921
1922 /* :authority must come before non-pseudo header fields */
1923 if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) {
1924 nghttp2_nv authority = nva[authority_idx];
1925 for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
1926 nva[i] = nva[i - 1];
1927 }
1928 nva[i] = authority;
1929 }
1930
1931 /* Warn stream may be rejected if cumulative length of headers is too large.
1932 It appears nghttp2 will not send a header frame larger than 64KB. */
1933#define MAX_ACC 60000 /* <64KB to account for some overhead */
1934 {
1935 size_t acc = 0;
1936
1937 for(i = 0; i < nheader; ++i) {
1938 acc += nva[i].namelen + nva[i].valuelen;
1939
1940 DEBUGF(infof(conn->data, "h2 header: %.*s:%.*s\n",
1941 nva[i].namelen, nva[i].name,
1942 nva[i].valuelen, nva[i].value));
1943 }
1944
1945 if(acc > MAX_ACC) {
1946 infof(conn->data, "http2_send: Warning: The cumulative length of all "
1947 "headers exceeds %zu bytes and that could cause the "
1948 "stream to be rejected.\n", MAX_ACC);
1949 }
1950 }
1951
1952 h2_pri_spec(conn->data, &pri_spec);
1953
1954 switch(conn->data->set.httpreq) {
1955 case HTTPREQ_POST:
1956 case HTTPREQ_POST_FORM:
1957 case HTTPREQ_POST_MIME:
1958 case HTTPREQ_PUT:
1959 if(conn->data->state.infilesize != -1)
1960 stream->upload_left = conn->data->state.infilesize;
1961 else
1962 /* data sending without specifying the data amount up front */
1963 stream->upload_left = -1; /* unknown, but not zero */
1964
1965 data_prd.read_callback = data_source_read_callback;
1966 data_prd.source.ptr = NULL;
1967 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
1968 &data_prd, conn->data);
1969 break;
1970 default:
1971 stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
1972 NULL, conn->data);
1973 }
1974
1975 Curl_safefree(nva);
1976
1977 if(stream_id < 0) {
1978 DEBUGF(infof(conn->data, "http2_send() send error\n"));
1979 *err = CURLE_SEND_ERROR;
1980 return -1;
1981 }
1982
1983 infof(conn->data, "Using Stream ID: %x (easy handle %p)\n",
1984 stream_id, conn->data);
1985 stream->stream_id = stream_id;
1986
1987 /* this does not call h2_session_send() since there can not have been any
1988 * priority upodate since the nghttp2_submit_request() call above */
1989 rv = nghttp2_session_send(h2);
1990
1991 if(rv != 0) {
1992 *err = CURLE_SEND_ERROR;
1993 return -1;
1994 }
1995
1996 if(should_close_session(httpc)) {
1997 DEBUGF(infof(conn->data, "http2_send: nothing to do in this session\n"));
1998 *err = CURLE_HTTP2;
1999 return -1;
2000 }
2001
2002 if(stream->stream_id != -1) {
2003 /* If whole HEADERS frame was sent off to the underlying socket,
2004 the nghttp2 library calls data_source_read_callback. But only
2005 it found that no data available, so it deferred the DATA
2006 transmission. Which means that nghttp2_session_want_write()
2007 returns 0 on http2_perform_getsock(), which results that no
2008 writable socket check is performed. To workaround this, we
2009 issue nghttp2_session_resume_data() here to bring back DATA
2010 transmission from deferred state. */
2011 nghttp2_session_resume_data(h2, stream->stream_id);
2012 }
2013
2014 return len;
2015
2016fail:
2017 free(nva);
2018 *err = CURLE_SEND_ERROR;
2019 return -1;
2020}
2021
2022CURLcode Curl_http2_setup(struct connectdata *conn)
2023{
2024 CURLcode result;
2025 struct http_conn *httpc = &conn->proto.httpc;
2026 struct HTTP *stream = conn->data->req.protop;
2027
2028 stream->stream_id = -1;
2029
2030 if(!stream->header_recvbuf)
2031 stream->header_recvbuf = Curl_add_buffer_init();
2032
2033 if((conn->handler == &Curl_handler_http2_ssl) ||
2034 (conn->handler == &Curl_handler_http2))
2035 return CURLE_OK; /* already done */
2036
2037 if(conn->handler->flags & PROTOPT_SSL)
2038 conn->handler = &Curl_handler_http2_ssl;
2039 else
2040 conn->handler = &Curl_handler_http2;
2041
2042 result = Curl_http2_init(conn);
2043 if(result)
2044 return result;
2045
2046 infof(conn->data, "Using HTTP2, server supports multi-use\n");
2047 stream->upload_left = 0;
2048 stream->upload_mem = NULL;
2049 stream->upload_len = 0;
2050
2051 httpc->inbuflen = 0;
2052 httpc->nread_inbuf = 0;
2053
2054 httpc->pause_stream_id = 0;
2055 httpc->drain_total = 0;
2056
2057 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
2058 conn->httpversion = 20;
2059 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
2060
2061 infof(conn->data, "Connection state changed (HTTP/2 confirmed)\n");
2062 Curl_multi_connchanged(conn->data->multi);
2063
2064 return CURLE_OK;
2065}
2066
2067CURLcode Curl_http2_switched(struct connectdata *conn,
2068 const char *mem, size_t nread)
2069{
2070 CURLcode result;
2071 struct http_conn *httpc = &conn->proto.httpc;
2072 int rv;
2073 ssize_t nproc;
2074 struct Curl_easy *data = conn->data;
2075 struct HTTP *stream = conn->data->req.protop;
2076
2077 result = Curl_http2_setup(conn);
2078 if(result)
2079 return result;
2080
2081 httpc->recv_underlying = (recving)conn->recv[FIRSTSOCKET];
2082 httpc->send_underlying = (sending)conn->send[FIRSTSOCKET];
2083 conn->recv[FIRSTSOCKET] = http2_recv;
2084 conn->send[FIRSTSOCKET] = http2_send;
2085
2086 if(conn->data->req.upgr101 == UPGR101_RECEIVED) {
2087 /* stream 1 is opened implicitly on upgrade */
2088 stream->stream_id = 1;
2089 /* queue SETTINGS frame (again) */
2090 rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings,
2091 httpc->binlen, NULL);
2092 if(rv != 0) {
2093 failf(data, "nghttp2_session_upgrade() failed: %s(%d)",
2094 nghttp2_strerror(rv), rv);
2095 return CURLE_HTTP2;
2096 }
2097
2098 nghttp2_session_set_stream_user_data(httpc->h2,
2099 stream->stream_id,
2100 conn->data);
2101 }
2102 else {
2103 populate_settings(conn, httpc);
2104
2105 /* stream ID is unknown at this point */
2106 stream->stream_id = -1;
2107 rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE,
2108 httpc->local_settings,
2109 httpc->local_settings_num);
2110 if(rv != 0) {
2111 failf(data, "nghttp2_submit_settings() failed: %s(%d)",
2112 nghttp2_strerror(rv), rv);
2113 return CURLE_HTTP2;
2114 }
2115 }
2116
2117#ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
2118 rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, 0,
2119 HTTP2_HUGE_WINDOW_SIZE);
2120 if(rv != 0) {
2121 failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
2122 nghttp2_strerror(rv), rv);
2123 return CURLE_HTTP2;
2124 }
2125#endif
2126
2127 /* we are going to copy mem to httpc->inbuf. This is required since
2128 mem is part of buffer pointed by stream->mem, and callbacks
2129 called by nghttp2_session_mem_recv() will write stream specific
2130 data into stream->mem, overwriting data already there. */
2131 if(H2_BUFSIZE < nread) {
2132 failf(data, "connection buffer size is too small to store data following "
2133 "HTTP Upgrade response header: buflen=%zu, datalen=%zu",
2134 H2_BUFSIZE, nread);
2135 return CURLE_HTTP2;
2136 }
2137
2138 infof(conn->data, "Copying HTTP/2 data in stream buffer to connection buffer"
2139 " after upgrade: len=%zu\n",
2140 nread);
2141
2142 if(nread)
2143 memcpy(httpc->inbuf, mem, nread);
2144 httpc->inbuflen = nread;
2145
2146 nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf,
2147 httpc->inbuflen);
2148
2149 if(nghttp2_is_fatal((int)nproc)) {
2150 failf(data, "nghttp2_session_mem_recv() failed: %s(%d)",
2151 nghttp2_strerror((int)nproc), (int)nproc);
2152 return CURLE_HTTP2;
2153 }
2154
2155 DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", nproc));
2156
2157 if((ssize_t)nread == nproc) {
2158 httpc->inbuflen = 0;
2159 httpc->nread_inbuf = 0;
2160 }
2161 else {
2162 httpc->nread_inbuf += nproc;
2163 }
2164
2165 /* Try to send some frames since we may read SETTINGS already. */
2166 rv = h2_session_send(data, httpc->h2);
2167
2168 if(rv != 0) {
2169 failf(data, "nghttp2_session_send() failed: %s(%d)",
2170 nghttp2_strerror(rv), rv);
2171 return CURLE_HTTP2;
2172 }
2173
2174 if(should_close_session(httpc)) {
2175 DEBUGF(infof(data,
2176 "nghttp2_session_send(): nothing to do in this session\n"));
2177 return CURLE_HTTP2;
2178 }
2179
2180 return CURLE_OK;
2181}
2182
2183CURLcode Curl_http2_add_child(struct Curl_easy *parent,
2184 struct Curl_easy *child,
2185 bool exclusive)
2186{
2187 if(parent) {
2188 struct Curl_http2_dep **tail;
2189 struct Curl_http2_dep *dep = calloc(1, sizeof(struct Curl_http2_dep));
2190 if(!dep)
2191 return CURLE_OUT_OF_MEMORY;
2192 dep->data = child;
2193
2194 if(parent->set.stream_dependents && exclusive) {
2195 struct Curl_http2_dep *node = parent->set.stream_dependents;
2196 while(node) {
2197 node->data->set.stream_depends_on = child;
2198 node = node->next;
2199 }
2200
2201 tail = &child->set.stream_dependents;
2202 while(*tail)
2203 tail = &(*tail)->next;
2204
2205 DEBUGASSERT(!*tail);
2206 *tail = parent->set.stream_dependents;
2207 parent->set.stream_dependents = 0;
2208 }
2209
2210 tail = &parent->set.stream_dependents;
2211 while(*tail) {
2212 (*tail)->data->set.stream_depends_e = FALSE;
2213 tail = &(*tail)->next;
2214 }
2215
2216 DEBUGASSERT(!*tail);
2217 *tail = dep;
2218 }
2219
2220 child->set.stream_depends_on = parent;
2221 child->set.stream_depends_e = exclusive;
2222 return CURLE_OK;
2223}
2224
2225void Curl_http2_remove_child(struct Curl_easy *parent, struct Curl_easy *child)
2226{
2227 struct Curl_http2_dep *last = 0;
2228 struct Curl_http2_dep *data = parent->set.stream_dependents;
2229 DEBUGASSERT(child->set.stream_depends_on == parent);
2230
2231 while(data && data->data != child) {
2232 last = data;
2233 data = data->next;
2234 }
2235
2236 DEBUGASSERT(data);
2237
2238 if(data) {
2239 if(last) {
2240 last->next = data->next;
2241 }
2242 else {
2243 parent->set.stream_dependents = data->next;
2244 }
2245 free(data);
2246 }
2247
2248 child->set.stream_depends_on = 0;
2249 child->set.stream_depends_e = FALSE;
2250}
2251
2252void Curl_http2_cleanup_dependencies(struct Curl_easy *data)
2253{
2254 while(data->set.stream_dependents) {
2255 struct Curl_easy *tmp = data->set.stream_dependents->data;
2256 Curl_http2_remove_child(data, tmp);
2257 if(data->set.stream_depends_on)
2258 Curl_http2_add_child(data->set.stream_depends_on, tmp, FALSE);
2259 }
2260
2261 if(data->set.stream_depends_on)
2262 Curl_http2_remove_child(data->set.stream_depends_on, data);
2263}
2264
2265#else /* !USE_NGHTTP2 */
2266
2267/* Satisfy external references even if http2 is not compiled in. */
2268
2269#define CURL_DISABLE_TYPECHECK
2270#include <curl/curl.h>
2271
2272char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
2273{
2274 (void) h;
2275 (void) num;
2276 return NULL;
2277}
2278
2279char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
2280{
2281 (void) h;
2282 (void) header;
2283 return NULL;
2284}
2285
2286#endif /* USE_NGHTTP2 */
Note: See TracBrowser for help on using the repository browser.