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