http2.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00009  *
00010  * This software is licensed as described in the file COPYING, which
00011  * you should have received as part of this distribution. The terms
00012  * are also available at https://curl.haxx.se/docs/copyright.html.
00013  *
00014  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00015  * copies of the Software, and permit persons to whom the Software is
00016  * furnished to do so, under the terms of the COPYING file.
00017  *
00018  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00019  * KIND, either express or implied.
00020  *
00021  ***************************************************************************/
00022 
00023 #include "curl_setup.h"
00024 
00025 #ifdef USE_NGHTTP2
00026 #include <nghttp2/nghttp2.h>
00027 #include "urldata.h"
00028 #include "http2.h"
00029 #include "http.h"
00030 #include "sendf.h"
00031 #include "curl_base64.h"
00032 #include "strcase.h"
00033 #include "multiif.h"
00034 #include "conncache.h"
00035 #include "url.h"
00036 #include "connect.h"
00037 #include "strtoofft.h"
00038 #include "strdup.h"
00039 /* The last 3 #include files should be in this order */
00040 #include "curl_printf.h"
00041 #include "curl_memory.h"
00042 #include "memdebug.h"
00043 
00044 #define MIN(x,y) ((x)<(y)?(x):(y))
00045 
00046 #if (NGHTTP2_VERSION_NUM < 0x010000)
00047 #error too old nghttp2 version, upgrade!
00048 #endif
00049 
00050 #if (NGHTTP2_VERSION_NUM > 0x010800)
00051 #define NGHTTP2_HAS_HTTP2_STRERROR 1
00052 #endif
00053 
00054 #if (NGHTTP2_VERSION_NUM >= 0x010900)
00055 /* nghttp2_session_callbacks_set_error_callback is present in nghttp2 1.9.0 or
00056    later */
00057 #define NGHTTP2_HAS_ERROR_CALLBACK 1
00058 #else
00059 #define nghttp2_session_callbacks_set_error_callback(x,y)
00060 #endif
00061 
00062 #if (NGHTTP2_VERSION_NUM >= 0x010c00)
00063 #define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1
00064 #endif
00065 
00066 #define HTTP2_HUGE_WINDOW_SIZE (1 << 30)
00067 
00068 /*
00069  * Curl_http2_init_state() is called when the easy handle is created and
00070  * allows for HTTP/2 specific init of state.
00071  */
00072 void Curl_http2_init_state(struct UrlState *state)
00073 {
00074   state->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
00075 }
00076 
00077 /*
00078  * Curl_http2_init_userset() is called when the easy handle is created and
00079  * allows for HTTP/2 specific user-set fields.
00080  */
00081 void Curl_http2_init_userset(struct UserDefined *set)
00082 {
00083   set->stream_weight = NGHTTP2_DEFAULT_WEIGHT;
00084 }
00085 
00086 static int http2_perform_getsock(const struct connectdata *conn,
00087                                  curl_socket_t *sock, /* points to
00088                                                          numsocks
00089                                                          number of
00090                                                          sockets */
00091                                  int numsocks)
00092 {
00093   const struct http_conn *c = &conn->proto.httpc;
00094   int bitmap = GETSOCK_BLANK;
00095   (void)numsocks;
00096 
00097   /* TODO We should check underlying socket state if it is SSL socket
00098      because of renegotiation. */
00099   sock[0] = conn->sock[FIRSTSOCKET];
00100 
00101   /* in a HTTP/2 connection we can basically always get a frame so we should
00102      always be ready for one */
00103   bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
00104 
00105   if(nghttp2_session_want_write(c->h2))
00106     bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
00107 
00108   return bitmap;
00109 }
00110 
00111 static int http2_getsock(struct connectdata *conn,
00112                          curl_socket_t *sock, /* points to numsocks
00113                                                  number of sockets */
00114                          int numsocks)
00115 {
00116   return http2_perform_getsock(conn, sock, numsocks);
00117 }
00118 
00119 static CURLcode http2_disconnect(struct connectdata *conn,
00120                                  bool dead_connection)
00121 {
00122   struct HTTP *http = conn->data->req.protop;
00123   struct http_conn *c = &conn->proto.httpc;
00124   (void)dead_connection;
00125 
00126   DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT starts now\n"));
00127 
00128   nghttp2_session_del(c->h2);
00129   Curl_safefree(c->inbuf);
00130 
00131   if(http) {
00132     Curl_add_buffer_free(http->header_recvbuf);
00133     http->header_recvbuf = NULL; /* clear the pointer */
00134     Curl_add_buffer_free(http->trailer_recvbuf);
00135     http->trailer_recvbuf = NULL; /* clear the pointer */
00136     for(; http->push_headers_used > 0; --http->push_headers_used) {
00137       free(http->push_headers[http->push_headers_used - 1]);
00138     }
00139     free(http->push_headers);
00140     http->push_headers = NULL;
00141   }
00142 
00143   DEBUGF(infof(conn->data, "HTTP/2 DISCONNECT done\n"));
00144 
00145   return CURLE_OK;
00146 }
00147 
00148 /* called from Curl_http_setup_conn */
00149 void Curl_http2_setup_req(struct Curl_easy *data)
00150 {
00151   struct HTTP *http = data->req.protop;
00152 
00153   http->nread_header_recvbuf = 0;
00154   http->bodystarted = FALSE;
00155   http->status_code = -1;
00156   http->pausedata = NULL;
00157   http->pauselen = 0;
00158   http->error_code = NGHTTP2_NO_ERROR;
00159   http->closed = FALSE;
00160   http->close_handled = FALSE;
00161   http->mem = data->state.buffer;
00162   http->len = BUFSIZE;
00163   http->memlen = 0;
00164 }
00165 
00166 /* called from Curl_http_setup_conn */
00167 void Curl_http2_setup_conn(struct connectdata *conn)
00168 {
00169   conn->proto.httpc.settings.max_concurrent_streams =
00170     DEFAULT_MAX_CONCURRENT_STREAMS;
00171 }
00172 
00173 /*
00174  * HTTP2 handler interface. This isn't added to the general list of protocols
00175  * but will be used at run-time when the protocol is dynamically switched from
00176  * HTTP to HTTP2.
00177  */
00178 const struct Curl_handler Curl_handler_http2 = {
00179   "HTTP",                               /* scheme */
00180   ZERO_NULL,                            /* setup_connection */
00181   Curl_http,                            /* do_it */
00182   Curl_http_done,                       /* done */
00183   ZERO_NULL,                            /* do_more */
00184   ZERO_NULL,                            /* connect_it */
00185   ZERO_NULL,                            /* connecting */
00186   ZERO_NULL,                            /* doing */
00187   http2_getsock,                        /* proto_getsock */
00188   http2_getsock,                        /* doing_getsock */
00189   ZERO_NULL,                            /* domore_getsock */
00190   http2_perform_getsock,                /* perform_getsock */
00191   http2_disconnect,                     /* disconnect */
00192   ZERO_NULL,                            /* readwrite */
00193   PORT_HTTP,                            /* defport */
00194   CURLPROTO_HTTP,                       /* protocol */
00195   PROTOPT_STREAM                        /* flags */
00196 };
00197 
00198 const struct Curl_handler Curl_handler_http2_ssl = {
00199   "HTTPS",                              /* scheme */
00200   ZERO_NULL,                            /* setup_connection */
00201   Curl_http,                            /* do_it */
00202   Curl_http_done,                       /* done */
00203   ZERO_NULL,                            /* do_more */
00204   ZERO_NULL,                            /* connect_it */
00205   ZERO_NULL,                            /* connecting */
00206   ZERO_NULL,                            /* doing */
00207   http2_getsock,                        /* proto_getsock */
00208   http2_getsock,                        /* doing_getsock */
00209   ZERO_NULL,                            /* domore_getsock */
00210   http2_perform_getsock,                /* perform_getsock */
00211   http2_disconnect,                     /* disconnect */
00212   ZERO_NULL,                            /* readwrite */
00213   PORT_HTTP,                            /* defport */
00214   CURLPROTO_HTTPS,                      /* protocol */
00215   PROTOPT_SSL | PROTOPT_STREAM          /* flags */
00216 };
00217 
00218 /*
00219  * Store nghttp2 version info in this buffer, Prefix with a space.  Return
00220  * total length written.
00221  */
00222 int Curl_http2_ver(char *p, size_t len)
00223 {
00224   nghttp2_info *h2 = nghttp2_version(0);
00225   return snprintf(p, len, " nghttp2/%s", h2->version_str);
00226 }
00227 
00228 /* HTTP/2 error code to name based on the Error Code Registry.
00229 https://tools.ietf.org/html/rfc7540#page-77
00230 nghttp2_error_code enums are identical.
00231 */
00232 const char *Curl_http2_strerror(uint32_t err)
00233 {
00234 #ifndef NGHTTP2_HAS_HTTP2_STRERROR
00235   const char *str[] = {
00236     "NO_ERROR",             /* 0x0 */
00237     "PROTOCOL_ERROR",       /* 0x1 */
00238     "INTERNAL_ERROR",       /* 0x2 */
00239     "FLOW_CONTROL_ERROR",   /* 0x3 */
00240     "SETTINGS_TIMEOUT",     /* 0x4 */
00241     "STREAM_CLOSED",        /* 0x5 */
00242     "FRAME_SIZE_ERROR",     /* 0x6 */
00243     "REFUSED_STREAM",       /* 0x7 */
00244     "CANCEL",               /* 0x8 */
00245     "COMPRESSION_ERROR",    /* 0x9 */
00246     "CONNECT_ERROR",        /* 0xA */
00247     "ENHANCE_YOUR_CALM",    /* 0xB */
00248     "INADEQUATE_SECURITY",  /* 0xC */
00249     "HTTP_1_1_REQUIRED"     /* 0xD */
00250   };
00251   return (err < sizeof str / sizeof str[0]) ? str[err] : "unknown";
00252 #else
00253   return nghttp2_http2_strerror(err);
00254 #endif
00255 }
00256 
00257 /*
00258  * The implementation of nghttp2_send_callback type. Here we write |data| with
00259  * size |length| to the network and return the number of bytes actually
00260  * written. See the documentation of nghttp2_send_callback for the details.
00261  */
00262 static ssize_t send_callback(nghttp2_session *h2,
00263                              const uint8_t *data, size_t length, int flags,
00264                              void *userp)
00265 {
00266   struct connectdata *conn = (struct connectdata *)userp;
00267   struct http_conn *c = &conn->proto.httpc;
00268   ssize_t written;
00269   CURLcode result = CURLE_OK;
00270 
00271   (void)h2;
00272   (void)flags;
00273 
00274   written = ((Curl_send*)c->send_underlying)(conn, FIRSTSOCKET,
00275                                              data, length, &result);
00276 
00277   if(result == CURLE_AGAIN) {
00278     return NGHTTP2_ERR_WOULDBLOCK;
00279   }
00280 
00281   if(written == -1) {
00282     failf(conn->data, "Failed sending HTTP2 data");
00283     return NGHTTP2_ERR_CALLBACK_FAILURE;
00284   }
00285 
00286   if(!written)
00287     return NGHTTP2_ERR_WOULDBLOCK;
00288 
00289   return written;
00290 }
00291 
00292 
00293 /* We pass a pointer to this struct in the push callback, but the contents of
00294    the struct are hidden from the user. */
00295 struct curl_pushheaders {
00296   struct Curl_easy *data;
00297   const nghttp2_push_promise *frame;
00298 };
00299 
00300 /*
00301  * push header access function. Only to be used from within the push callback
00302  */
00303 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
00304 {
00305   /* Verify that we got a good easy handle in the push header struct, mostly to
00306      detect rubbish input fast(er). */
00307   if(!h || !GOOD_EASY_HANDLE(h->data))
00308     return NULL;
00309   else {
00310     struct HTTP *stream = h->data->req.protop;
00311     if(num < stream->push_headers_used)
00312       return stream->push_headers[num];
00313   }
00314   return NULL;
00315 }
00316 
00317 /*
00318  * push header access function. Only to be used from within the push callback
00319  */
00320 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
00321 {
00322   /* Verify that we got a good easy handle in the push header struct,
00323      mostly to detect rubbish input fast(er). Also empty header name
00324      is just a rubbish too. We have to allow ":" at the beginning of
00325      the header, but header == ":" must be rejected. If we have ':' in
00326      the middle of header, it could be matched in middle of the value,
00327      this is because we do prefix match.*/
00328   if(!h || !GOOD_EASY_HANDLE(h->data) || !header || !header[0] ||
00329      !strcmp(header, ":") || strchr(header + 1, ':'))
00330     return NULL;
00331   else {
00332     struct HTTP *stream = h->data->req.protop;
00333     size_t len = strlen(header);
00334     size_t i;
00335     for(i=0; i<stream->push_headers_used; i++) {
00336       if(!strncmp(header, stream->push_headers[i], len)) {
00337         /* sub-match, make sure that it is followed by a colon */
00338         if(stream->push_headers[i][len] != ':')
00339           continue;
00340         return &stream->push_headers[i][len+1];
00341       }
00342     }
00343   }
00344   return NULL;
00345 }
00346 
00347 static struct Curl_easy *duphandle(struct Curl_easy *data)
00348 {
00349   struct Curl_easy *second = curl_easy_duphandle(data);
00350   if(second) {
00351     /* setup the request struct */
00352     struct HTTP *http = calloc(1, sizeof(struct HTTP));
00353     if(!http) {
00354       (void)Curl_close(second);
00355       second = NULL;
00356     }
00357     else {
00358       second->req.protop = http;
00359       http->header_recvbuf = Curl_add_buffer_init();
00360       if(!http->header_recvbuf) {
00361         free(http);
00362         (void)Curl_close(second);
00363         second = NULL;
00364       }
00365       else {
00366         Curl_http2_setup_req(second);
00367         second->state.stream_weight = data->state.stream_weight;
00368       }
00369     }
00370   }
00371   return second;
00372 }
00373 
00374 
00375 static int push_promise(struct Curl_easy *data,
00376                         struct connectdata *conn,
00377                         const nghttp2_push_promise *frame)
00378 {
00379   int rv;
00380   DEBUGF(infof(data, "PUSH_PROMISE received, stream %u!\n",
00381                frame->promised_stream_id));
00382   if(data->multi->push_cb) {
00383     struct HTTP *stream;
00384     struct HTTP *newstream;
00385     struct curl_pushheaders heads;
00386     CURLMcode rc;
00387     struct http_conn *httpc;
00388     size_t i;
00389     /* clone the parent */
00390     struct Curl_easy *newhandle = duphandle(data);
00391     if(!newhandle) {
00392       infof(data, "failed to duplicate handle\n");
00393       rv = 1; /* FAIL HARD */
00394       goto fail;
00395     }
00396 
00397     heads.data = data;
00398     heads.frame = frame;
00399     /* ask the application */
00400     DEBUGF(infof(data, "Got PUSH_PROMISE, ask application!\n"));
00401 
00402     stream = data->req.protop;
00403     if(!stream) {
00404       failf(data, "Internal NULL stream!\n");
00405       rv = 1;
00406       goto fail;
00407     }
00408 
00409     rv = data->multi->push_cb(data, newhandle,
00410                               stream->push_headers_used, &heads,
00411                               data->multi->push_userp);
00412 
00413     /* free the headers again */
00414     for(i=0; i<stream->push_headers_used; i++)
00415       free(stream->push_headers[i]);
00416     free(stream->push_headers);
00417     stream->push_headers = NULL;
00418 
00419     if(rv) {
00420       /* denied, kill off the new handle again */
00421       (void)Curl_close(newhandle);
00422       goto fail;
00423     }
00424 
00425     newstream = newhandle->req.protop;
00426     newstream->stream_id = frame->promised_stream_id;
00427     newhandle->req.maxdownload = -1;
00428     newhandle->req.size = -1;
00429 
00430     /* approved, add to the multi handle and immediately switch to PERFORM
00431        state with the given connection !*/
00432     rc = Curl_multi_add_perform(data->multi, newhandle, conn);
00433     if(rc) {
00434       infof(data, "failed to add handle to multi\n");
00435       Curl_close(newhandle);
00436       rv = 1;
00437       goto fail;
00438     }
00439 
00440     httpc = &conn->proto.httpc;
00441     nghttp2_session_set_stream_user_data(httpc->h2,
00442                                          frame->promised_stream_id, newhandle);
00443   }
00444   else {
00445     DEBUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n"));
00446     rv = 1;
00447   }
00448   fail:
00449   return rv;
00450 }
00451 
00452 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
00453                          void *userp)
00454 {
00455   struct connectdata *conn = (struct connectdata *)userp;
00456   struct http_conn *httpc = &conn->proto.httpc;
00457   struct Curl_easy *data_s = NULL;
00458   struct HTTP *stream = NULL;
00459   static int lastStream = -1;
00460   int rv;
00461   size_t left, ncopy;
00462   int32_t stream_id = frame->hd.stream_id;
00463 
00464   if(!stream_id) {
00465     /* stream ID zero is for connection-oriented stuff */
00466     if(frame->hd.type == NGHTTP2_SETTINGS) {
00467       uint32_t max_conn = httpc->settings.max_concurrent_streams;
00468       DEBUGF(infof(conn->data, "Got SETTINGS\n"));
00469       httpc->settings.max_concurrent_streams =
00470         nghttp2_session_get_remote_settings(
00471           session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
00472       httpc->settings.enable_push =
00473         nghttp2_session_get_remote_settings(
00474           session, NGHTTP2_SETTINGS_ENABLE_PUSH);
00475       DEBUGF(infof(conn->data, "MAX_CONCURRENT_STREAMS == %d\n",
00476                    httpc->settings.max_concurrent_streams));
00477       DEBUGF(infof(conn->data, "ENABLE_PUSH == %s\n",
00478                    httpc->settings.enable_push?"TRUE":"false"));
00479       if(max_conn != httpc->settings.max_concurrent_streams) {
00480         /* only signal change if the value actually changed */
00481         infof(conn->data,
00482               "Connection state changed (MAX_CONCURRENT_STREAMS updated)!\n");
00483         Curl_multi_connchanged(conn->data->multi);
00484       }
00485     }
00486     return 0;
00487   }
00488   data_s = nghttp2_session_get_stream_user_data(session, stream_id);
00489   if(lastStream != stream_id) {
00490     lastStream = stream_id;
00491   }
00492   if(!data_s) {
00493     DEBUGF(infof(conn->data,
00494                  "No Curl_easy associated with stream: %x\n",
00495                  stream_id));
00496     return 0;
00497   }
00498 
00499   stream = data_s->req.protop;
00500   if(!stream) {
00501     DEBUGF(infof(conn->data, "No proto pointer for stream: %x\n",
00502                  stream_id));
00503     return NGHTTP2_ERR_CALLBACK_FAILURE;
00504   }
00505 
00506   DEBUGF(infof(data_s, "on_frame_recv() header %x stream %x\n",
00507                frame->hd.type, stream_id));
00508 
00509   switch(frame->hd.type) {
00510   case NGHTTP2_DATA:
00511     /* If body started on this stream, then receiving DATA is illegal. */
00512     if(!stream->bodystarted) {
00513       rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
00514                                      stream_id, NGHTTP2_PROTOCOL_ERROR);
00515 
00516       if(nghttp2_is_fatal(rv)) {
00517         return NGHTTP2_ERR_CALLBACK_FAILURE;
00518       }
00519     }
00520     break;
00521   case NGHTTP2_HEADERS:
00522     if(stream->bodystarted) {
00523       /* Only valid HEADERS after body started is trailer HEADERS.  We
00524          buffer them in on_header callback. */
00525       break;
00526     }
00527 
00528     /* nghttp2 guarantees that :status is received, and we store it to
00529        stream->status_code */
00530     DEBUGASSERT(stream->status_code != -1);
00531 
00532     /* Only final status code signals the end of header */
00533     if(stream->status_code / 100 != 1) {
00534       stream->bodystarted = TRUE;
00535       stream->status_code = -1;
00536     }
00537 
00538     Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
00539 
00540     left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
00541     ncopy = MIN(stream->len, left);
00542 
00543     memcpy(&stream->mem[stream->memlen],
00544            stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
00545            ncopy);
00546     stream->nread_header_recvbuf += ncopy;
00547 
00548     DEBUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n",
00549                  ncopy, stream_id, stream->mem));
00550 
00551     stream->len -= ncopy;
00552     stream->memlen += ncopy;
00553 
00554     data_s->state.drain++;
00555     httpc->drain_total++;
00556     {
00557       /* get the pointer from userp again since it was re-assigned above */
00558       struct connectdata *conn_s = (struct connectdata *)userp;
00559 
00560       /* if we receive data for another handle, wake that up */
00561       if(conn_s->data != data_s)
00562         Curl_expire(data_s, 0);
00563     }
00564     break;
00565   case NGHTTP2_PUSH_PROMISE:
00566     rv = push_promise(data_s, conn, &frame->push_promise);
00567     if(rv) { /* deny! */
00568       rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
00569                                      frame->push_promise.promised_stream_id,
00570                                      NGHTTP2_CANCEL);
00571       if(nghttp2_is_fatal(rv)) {
00572         return rv;
00573       }
00574     }
00575     break;
00576   default:
00577     DEBUGF(infof(conn->data, "Got frame type %x for stream %u!\n",
00578                  frame->hd.type, stream_id));
00579     break;
00580   }
00581   return 0;
00582 }
00583 
00584 static int on_invalid_frame_recv(nghttp2_session *session,
00585                                  const nghttp2_frame *frame,
00586                                  int lib_error_code, void *userp)
00587 {
00588   struct Curl_easy *data_s = NULL;
00589   (void)userp;
00590 
00591   data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
00592   if(data_s) {
00593     DEBUGF(infof(data_s,
00594                  "on_invalid_frame_recv() was called, error=%d:%s\n",
00595                  lib_error_code, nghttp2_strerror(lib_error_code)));
00596   }
00597   return 0;
00598 }
00599 
00600 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
00601                               int32_t stream_id,
00602                               const uint8_t *data, size_t len, void *userp)
00603 {
00604   struct HTTP *stream;
00605   struct Curl_easy *data_s;
00606   size_t nread;
00607   struct connectdata *conn = (struct connectdata *)userp;
00608   (void)session;
00609   (void)flags;
00610   (void)data;
00611 
00612   DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
00613 
00614   /* get the stream from the hash based on Stream ID */
00615   data_s = nghttp2_session_get_stream_user_data(session, stream_id);
00616   if(!data_s)
00617     /* Receiving a Stream ID not in the hash should not happen, this is an
00618        internal error more than anything else! */
00619     return NGHTTP2_ERR_CALLBACK_FAILURE;
00620 
00621   stream = data_s->req.protop;
00622   if(!stream)
00623     return NGHTTP2_ERR_CALLBACK_FAILURE;
00624 
00625   nread = MIN(stream->len, len);
00626   memcpy(&stream->mem[stream->memlen], data, nread);
00627 
00628   stream->len -= nread;
00629   stream->memlen += nread;
00630 
00631   data_s->state.drain++;
00632   conn->proto.httpc.drain_total++;
00633 
00634   /* if we receive data for another handle, wake that up */
00635   if(conn->data != data_s)
00636     Curl_expire(data_s, 0);
00637 
00638   DEBUGF(infof(data_s, "%zu data received for stream %u "
00639                "(%zu left in buffer %p, total %zu)\n",
00640                nread, stream_id,
00641                stream->len, stream->mem,
00642                stream->memlen));
00643 
00644   if(nread < len) {
00645     stream->pausedata = data + nread;
00646     stream->pauselen = len - nread;
00647     DEBUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
00648                  ", stream %u\n",
00649                  len - nread, stream_id));
00650     data_s->easy_conn->proto.httpc.pause_stream_id = stream_id;
00651 
00652     return NGHTTP2_ERR_PAUSE;
00653   }
00654 
00655   /* pause execution of nghttp2 if we received data for another handle
00656      in order to process them first. */
00657   if(conn->data != data_s) {
00658     data_s->easy_conn->proto.httpc.pause_stream_id = stream_id;
00659 
00660     return NGHTTP2_ERR_PAUSE;
00661   }
00662 
00663   return 0;
00664 }
00665 
00666 static int before_frame_send(nghttp2_session *session,
00667                              const nghttp2_frame *frame,
00668                              void *userp)
00669 {
00670   struct Curl_easy *data_s;
00671   (void)userp;
00672 
00673   data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
00674   if(data_s) {
00675     DEBUGF(infof(data_s, "before_frame_send() was called\n"));
00676   }
00677 
00678   return 0;
00679 }
00680 static int on_frame_send(nghttp2_session *session,
00681                          const nghttp2_frame *frame,
00682                          void *userp)
00683 {
00684   struct Curl_easy *data_s;
00685   (void)userp;
00686 
00687   data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
00688   if(data_s) {
00689     DEBUGF(infof(data_s, "on_frame_send() was called, length = %zd\n",
00690                  frame->hd.length));
00691   }
00692   return 0;
00693 }
00694 static int on_frame_not_send(nghttp2_session *session,
00695                              const nghttp2_frame *frame,
00696                              int lib_error_code, void *userp)
00697 {
00698   struct Curl_easy *data_s;
00699   (void)userp;
00700 
00701   data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
00702   if(data_s) {
00703     DEBUGF(infof(data_s,
00704                  "on_frame_not_send() was called, lib_error_code = %d\n",
00705                  lib_error_code));
00706   }
00707   return 0;
00708 }
00709 static int on_stream_close(nghttp2_session *session, int32_t stream_id,
00710                            uint32_t error_code, void *userp)
00711 {
00712   struct Curl_easy *data_s;
00713   struct HTTP *stream;
00714   struct connectdata *conn = (struct connectdata *)userp;
00715   (void)session;
00716   (void)stream_id;
00717 
00718   if(stream_id) {
00719     /* get the stream from the hash based on Stream ID, stream ID zero is for
00720        connection-oriented stuff */
00721     data_s = nghttp2_session_get_stream_user_data(session, stream_id);
00722     if(!data_s) {
00723       /* We could get stream ID not in the hash.  For example, if we
00724          decided to reject stream (e.g., PUSH_PROMISE). */
00725       return 0;
00726     }
00727     DEBUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u\n",
00728                  Curl_http2_strerror(error_code), error_code, stream_id));
00729     stream = data_s->req.protop;
00730     if(!stream)
00731       return NGHTTP2_ERR_CALLBACK_FAILURE;
00732 
00733     stream->error_code = error_code;
00734     stream->closed = TRUE;
00735     data_s->state.drain++;
00736     conn->proto.httpc.drain_total++;
00737 
00738     /* remove the entry from the hash as the stream is now gone */
00739     nghttp2_session_set_stream_user_data(session, stream_id, 0);
00740     DEBUGF(infof(data_s, "Removed stream %u hash!\n", stream_id));
00741   }
00742   return 0;
00743 }
00744 
00745 static int on_begin_headers(nghttp2_session *session,
00746                             const nghttp2_frame *frame, void *userp)
00747 {
00748   struct HTTP *stream;
00749   struct Curl_easy *data_s = NULL;
00750   (void)userp;
00751 
00752   data_s = nghttp2_session_get_stream_user_data(session, frame->hd.stream_id);
00753   if(!data_s) {
00754     return 0;
00755   }
00756 
00757   DEBUGF(infof(data_s, "on_begin_headers() was called\n"));
00758 
00759   if(frame->hd.type != NGHTTP2_HEADERS) {
00760     return 0;
00761   }
00762 
00763   stream = data_s->req.protop;
00764   if(!stream || !stream->bodystarted) {
00765     return 0;
00766   }
00767 
00768   /* This is trailer HEADERS started.  Allocate buffer for them. */
00769   DEBUGF(infof(data_s, "trailer field started\n"));
00770 
00771   assert(stream->trailer_recvbuf == NULL);
00772 
00773   stream->trailer_recvbuf = Curl_add_buffer_init();
00774   if(!stream->trailer_recvbuf) {
00775     return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
00776   }
00777 
00778   return 0;
00779 }
00780 
00781 /* Decode HTTP status code.  Returns -1 if no valid status code was
00782    decoded. */
00783 static int decode_status_code(const uint8_t *value, size_t len)
00784 {
00785   int i;
00786   int res;
00787 
00788   if(len != 3) {
00789     return -1;
00790   }
00791 
00792   res = 0;
00793 
00794   for(i = 0; i < 3; ++i) {
00795     char c = value[i];
00796 
00797     if(c < '0' || c > '9') {
00798       return -1;
00799     }
00800 
00801     res *= 10;
00802     res += c - '0';
00803   }
00804 
00805   return res;
00806 }
00807 
00808 /* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
00809 static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
00810                      const uint8_t *name, size_t namelen,
00811                      const uint8_t *value, size_t valuelen,
00812                      uint8_t flags,
00813                      void *userp)
00814 {
00815   struct HTTP *stream;
00816   struct Curl_easy *data_s;
00817   int32_t stream_id = frame->hd.stream_id;
00818   struct connectdata *conn = (struct connectdata *)userp;
00819   (void)flags;
00820 
00821   DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
00822 
00823   /* get the stream from the hash based on Stream ID */
00824   data_s = nghttp2_session_get_stream_user_data(session, stream_id);
00825   if(!data_s)
00826     /* Receiving a Stream ID not in the hash should not happen, this is an
00827        internal error more than anything else! */
00828     return NGHTTP2_ERR_CALLBACK_FAILURE;
00829 
00830   stream = data_s->req.protop;
00831   if(!stream) {
00832     failf(data_s, "Internal NULL stream! 5\n");
00833     return NGHTTP2_ERR_CALLBACK_FAILURE;
00834   }
00835 
00836   /* Store received PUSH_PROMISE headers to be used when the subsequent
00837      PUSH_PROMISE callback comes */
00838   if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
00839     char *h;
00840 
00841     if(!stream->push_headers) {
00842       stream->push_headers_alloc = 10;
00843       stream->push_headers = malloc(stream->push_headers_alloc *
00844                                     sizeof(char *));
00845       stream->push_headers_used = 0;
00846     }
00847     else if(stream->push_headers_used ==
00848             stream->push_headers_alloc) {
00849       char **headp;
00850       stream->push_headers_alloc *= 2;
00851       headp = Curl_saferealloc(stream->push_headers,
00852                                stream->push_headers_alloc * sizeof(char *));
00853       if(!headp) {
00854         stream->push_headers = NULL;
00855         return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
00856       }
00857       stream->push_headers = headp;
00858     }
00859     h = aprintf("%s:%s", name, value);
00860     if(h)
00861       stream->push_headers[stream->push_headers_used++] = h;
00862     return 0;
00863   }
00864 
00865   if(stream->bodystarted) {
00866     /* This is trailer fields. */
00867     /* 3 is for ":" and "\r\n". */
00868     uint32_t n = (uint32_t)(namelen + valuelen + 3);
00869 
00870     DEBUGF(infof(data_s, "h2 trailer: %.*s: %.*s\n", namelen, name, valuelen,
00871                  value));
00872 
00873     Curl_add_buffer(stream->trailer_recvbuf, &n, sizeof(n));
00874     Curl_add_buffer(stream->trailer_recvbuf, name, namelen);
00875     Curl_add_buffer(stream->trailer_recvbuf, ": ", 2);
00876     Curl_add_buffer(stream->trailer_recvbuf, value, valuelen);
00877     Curl_add_buffer(stream->trailer_recvbuf, "\r\n\0", 3);
00878 
00879     return 0;
00880   }
00881 
00882   if(namelen == sizeof(":status") - 1 &&
00883      memcmp(":status", name, namelen) == 0) {
00884     /* nghttp2 guarantees :status is received first and only once, and
00885        value is 3 digits status code, and decode_status_code always
00886        succeeds. */
00887     stream->status_code = decode_status_code(value, valuelen);
00888     DEBUGASSERT(stream->status_code != -1);
00889 
00890     Curl_add_buffer(stream->header_recvbuf, "HTTP/2 ", 7);
00891     Curl_add_buffer(stream->header_recvbuf, value, valuelen);
00892     /* the space character after the status code is mandatory */
00893     Curl_add_buffer(stream->header_recvbuf, " \r\n", 3);
00894     /* if we receive data for another handle, wake that up */
00895     if(conn->data != data_s)
00896       Curl_expire(data_s, 0);
00897 
00898     DEBUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)\n",
00899                  stream->status_code, data_s));
00900     return 0;
00901   }
00902 
00903   /* nghttp2 guarantees that namelen > 0, and :status was already
00904      received, and this is not pseudo-header field . */
00905   /* convert to a HTTP1-style header */
00906   Curl_add_buffer(stream->header_recvbuf, name, namelen);
00907   Curl_add_buffer(stream->header_recvbuf, ": ", 2);
00908   Curl_add_buffer(stream->header_recvbuf, value, valuelen);
00909   Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
00910   /* if we receive data for another handle, wake that up */
00911   if(conn->data != data_s)
00912     Curl_expire(data_s, 0);
00913 
00914   DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
00915                value));
00916 
00917   return 0; /* 0 is successful */
00918 }
00919 
00920 static ssize_t data_source_read_callback(nghttp2_session *session,
00921                                          int32_t stream_id,
00922                                          uint8_t *buf, size_t length,
00923                                          uint32_t *data_flags,
00924                                          nghttp2_data_source *source,
00925                                          void *userp)
00926 {
00927   struct Curl_easy *data_s;
00928   struct HTTP *stream = NULL;
00929   size_t nread;
00930   (void)source;
00931   (void)userp;
00932 
00933   if(stream_id) {
00934     /* get the stream from the hash based on Stream ID, stream ID zero is for
00935        connection-oriented stuff */
00936     data_s = nghttp2_session_get_stream_user_data(session, stream_id);
00937     if(!data_s)
00938       /* Receiving a Stream ID not in the hash should not happen, this is an
00939          internal error more than anything else! */
00940       return NGHTTP2_ERR_CALLBACK_FAILURE;
00941 
00942     stream = data_s->req.protop;
00943     if(!stream)
00944       return NGHTTP2_ERR_CALLBACK_FAILURE;
00945   }
00946   else
00947     return NGHTTP2_ERR_INVALID_ARGUMENT;
00948 
00949   nread = MIN(stream->upload_len, length);
00950   if(nread > 0) {
00951     memcpy(buf, stream->upload_mem, nread);
00952     stream->upload_mem += nread;
00953     stream->upload_len -= nread;
00954     if(data_s->state.infilesize != -1)
00955       stream->upload_left -= nread;
00956   }
00957 
00958   if(stream->upload_left == 0)
00959     *data_flags = NGHTTP2_DATA_FLAG_EOF;
00960   else if(nread == 0)
00961     return NGHTTP2_ERR_DEFERRED;
00962 
00963   DEBUGF(infof(data_s, "data_source_read_callback: "
00964                "returns %zu bytes stream %u\n",
00965                nread, stream_id));
00966 
00967   return nread;
00968 }
00969 
00970 /*
00971  * The HTTP2 settings we send in the Upgrade request
00972  */
00973 static nghttp2_settings_entry settings[] = {
00974   { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100 },
00975   { NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE, HTTP2_HUGE_WINDOW_SIZE },
00976 };
00977 
00978 #define H2_BUFSIZE 32768
00979 
00980 #ifdef NGHTTP2_HAS_ERROR_CALLBACK
00981 static int error_callback(nghttp2_session *session,
00982                           const char *msg,
00983                           size_t len,
00984                           void *userp)
00985 {
00986   struct connectdata *conn = (struct connectdata *)userp;
00987   (void)session;
00988   infof(conn->data, "http2 error: %.*s\n", len, msg);
00989   return 0;
00990 }
00991 #endif
00992 
00993 void Curl_http2_done(struct connectdata *conn, bool premature)
00994 {
00995   struct Curl_easy *data = conn->data;
00996   struct HTTP *http = data->req.protop;
00997   struct http_conn *httpc = &conn->proto.httpc;
00998 
00999   if(http->header_recvbuf) {
01000     DEBUGF(infof(data, "free header_recvbuf!!\n"));
01001     Curl_add_buffer_free(http->header_recvbuf);
01002     http->header_recvbuf = NULL; /* clear the pointer */
01003     Curl_add_buffer_free(http->trailer_recvbuf);
01004     http->trailer_recvbuf = NULL; /* clear the pointer */
01005     if(http->push_headers) {
01006       /* if they weren't used and then freed before */
01007       for(; http->push_headers_used > 0; --http->push_headers_used) {
01008         free(http->push_headers[http->push_headers_used - 1]);
01009       }
01010       free(http->push_headers);
01011       http->push_headers = NULL;
01012     }
01013   }
01014 
01015   if(premature) {
01016     /* RST_STREAM */
01017     nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE, http->stream_id,
01018                               NGHTTP2_STREAM_CLOSED);
01019     if(http->stream_id == httpc->pause_stream_id) {
01020       infof(data, "stopped the pause stream!\n");
01021       httpc->pause_stream_id = 0;
01022     }
01023   }
01024   if(http->stream_id) {
01025     nghttp2_session_set_stream_user_data(httpc->h2, http->stream_id, 0);
01026     http->stream_id = 0;
01027   }
01028 }
01029 
01030 /*
01031  * Initialize nghttp2 for a Curl connection
01032  */
01033 CURLcode Curl_http2_init(struct connectdata *conn)
01034 {
01035   if(!conn->proto.httpc.h2) {
01036     int rc;
01037     nghttp2_session_callbacks *callbacks;
01038 
01039     conn->proto.httpc.inbuf = malloc(H2_BUFSIZE);
01040     if(conn->proto.httpc.inbuf == NULL)
01041       return CURLE_OUT_OF_MEMORY;
01042 
01043     rc = nghttp2_session_callbacks_new(&callbacks);
01044 
01045     if(rc) {
01046       failf(conn->data, "Couldn't initialize nghttp2 callbacks!");
01047       return CURLE_OUT_OF_MEMORY; /* most likely at least */
01048     }
01049 
01050     /* nghttp2_send_callback */
01051     nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
01052     /* nghttp2_on_frame_recv_callback */
01053     nghttp2_session_callbacks_set_on_frame_recv_callback
01054       (callbacks, on_frame_recv);
01055     /* nghttp2_on_invalid_frame_recv_callback */
01056     nghttp2_session_callbacks_set_on_invalid_frame_recv_callback
01057       (callbacks, on_invalid_frame_recv);
01058     /* nghttp2_on_data_chunk_recv_callback */
01059     nghttp2_session_callbacks_set_on_data_chunk_recv_callback
01060       (callbacks, on_data_chunk_recv);
01061     /* nghttp2_before_frame_send_callback */
01062     nghttp2_session_callbacks_set_before_frame_send_callback
01063       (callbacks, before_frame_send);
01064     /* nghttp2_on_frame_send_callback */
01065     nghttp2_session_callbacks_set_on_frame_send_callback
01066       (callbacks, on_frame_send);
01067     /* nghttp2_on_frame_not_send_callback */
01068     nghttp2_session_callbacks_set_on_frame_not_send_callback
01069       (callbacks, on_frame_not_send);
01070     /* nghttp2_on_stream_close_callback */
01071     nghttp2_session_callbacks_set_on_stream_close_callback
01072       (callbacks, on_stream_close);
01073     /* nghttp2_on_begin_headers_callback */
01074     nghttp2_session_callbacks_set_on_begin_headers_callback
01075       (callbacks, on_begin_headers);
01076     /* nghttp2_on_header_callback */
01077     nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
01078 
01079     nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
01080 
01081     /* The nghttp2 session is not yet setup, do it */
01082     rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
01083 
01084     nghttp2_session_callbacks_del(callbacks);
01085 
01086     if(rc) {
01087       failf(conn->data, "Couldn't initialize nghttp2!");
01088       return CURLE_OUT_OF_MEMORY; /* most likely at least */
01089     }
01090   }
01091   return CURLE_OK;
01092 }
01093 
01094 /*
01095  * Append headers to ask for a HTTP1.1 to HTTP2 upgrade.
01096  */
01097 CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req,
01098                                     struct connectdata *conn)
01099 {
01100   CURLcode result;
01101   ssize_t binlen;
01102   char *base64;
01103   size_t blen;
01104   struct SingleRequest *k = &conn->data->req;
01105   uint8_t *binsettings = conn->proto.httpc.binsettings;
01106 
01107   /* As long as we have a fixed set of settings, we don't have to dynamically
01108    * figure out the base64 strings since it'll always be the same. However,
01109    * the settings will likely not be fixed every time in the future.
01110    */
01111 
01112   /* this returns number of bytes it wrote */
01113   binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
01114                                          settings,
01115                                          sizeof(settings)/sizeof(settings[0]));
01116   if(!binlen) {
01117     failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
01118     return CURLE_FAILED_INIT;
01119   }
01120   conn->proto.httpc.binlen = binlen;
01121 
01122   result = Curl_base64url_encode(conn->data, (const char *)binsettings, binlen,
01123                                  &base64, &blen);
01124   if(result)
01125     return result;
01126 
01127   result = Curl_add_bufferf(req,
01128                             "Connection: Upgrade, HTTP2-Settings\r\n"
01129                             "Upgrade: %s\r\n"
01130                             "HTTP2-Settings: %s\r\n",
01131                             NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
01132   free(base64);
01133 
01134   k->upgr101 = UPGR101_REQUESTED;
01135 
01136   return result;
01137 }
01138 
01139 /*
01140  * Returns nonzero if current HTTP/2 session should be closed.
01141  */
01142 static int should_close_session(struct http_conn *httpc)
01143 {
01144   return httpc->drain_total == 0 && !nghttp2_session_want_read(httpc->h2) &&
01145     !nghttp2_session_want_write(httpc->h2);
01146 }
01147 
01148 static int h2_session_send(struct Curl_easy *data,
01149                            nghttp2_session *h2);
01150 
01151 /*
01152  * h2_process_pending_input() processes pending input left in
01153  * httpc->inbuf.  Then, call h2_session_send() to send pending data.
01154  * This function returns 0 if it succeeds, or -1 and error code will
01155  * be assigned to *err.
01156  */
01157 static int h2_process_pending_input(struct Curl_easy *data,
01158                                     struct http_conn *httpc,
01159                                     CURLcode *err)
01160 {
01161   ssize_t nread;
01162   char *inbuf;
01163   ssize_t rv;
01164 
01165   nread = httpc->inbuflen - httpc->nread_inbuf;
01166   inbuf = httpc->inbuf + httpc->nread_inbuf;
01167 
01168   rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
01169   if(rv < 0) {
01170     failf(data,
01171           "h2_process_pending_input: nghttp2_session_mem_recv() returned "
01172           "%d:%s\n", rv, nghttp2_strerror((int)rv));
01173     *err = CURLE_RECV_ERROR;
01174     return -1;
01175   }
01176 
01177   if(nread == rv) {
01178     DEBUGF(infof(data,
01179                  "h2_process_pending_input: All data in connection buffer "
01180                  "processed\n"));
01181     httpc->inbuflen = 0;
01182     httpc->nread_inbuf = 0;
01183   }
01184   else {
01185     httpc->nread_inbuf += rv;
01186     DEBUGF(infof(data,
01187                  "h2_process_pending_input: %zu bytes left in connection "
01188                  "buffer\n",
01189                  httpc->inbuflen - httpc->nread_inbuf));
01190   }
01191 
01192   rv = h2_session_send(data, httpc->h2);
01193   if(rv != 0) {
01194     *err = CURLE_SEND_ERROR;
01195     return -1;
01196   }
01197 
01198   if(should_close_session(httpc)) {
01199     DEBUGF(infof(data,
01200                  "h2_process_pending_input: nothing to do in this session\n"));
01201     *err = CURLE_HTTP2;
01202     return -1;
01203   }
01204 
01205   return 0;
01206 }
01207 
01208 /*
01209  * Called from transfer.c:done_sending when we stop uploading.
01210  */
01211 CURLcode Curl_http2_done_sending(struct connectdata *conn)
01212 {
01213   CURLcode result = CURLE_OK;
01214 
01215   if((conn->handler == &Curl_handler_http2_ssl) ||
01216      (conn->handler == &Curl_handler_http2)) {
01217     /* make sure this is only attempted for HTTP/2 transfers */
01218 
01219     struct HTTP *stream = conn->data->req.protop;
01220 
01221     if(stream->upload_left) {
01222       /* If the stream still thinks there's data left to upload. */
01223       struct http_conn *httpc = &conn->proto.httpc;
01224       nghttp2_session *h2 = httpc->h2;
01225 
01226       stream->upload_left = 0; /* DONE! */
01227 
01228       /* resume sending here to trigger the callback to get called again so
01229          that it can signal EOF to nghttp2 */
01230       (void)nghttp2_session_resume_data(h2, stream->stream_id);
01231 
01232       (void)h2_process_pending_input(conn->data, httpc, &result);
01233     }
01234   }
01235   return result;
01236 }
01237 
01238 
01239 static ssize_t http2_handle_stream_close(struct connectdata *conn,
01240                                          struct Curl_easy *data,
01241                                          struct HTTP *stream, CURLcode *err)
01242 {
01243   char *trailer_pos, *trailer_end;
01244   CURLcode result;
01245   struct http_conn *httpc = &conn->proto.httpc;
01246 
01247   if(httpc->pause_stream_id == stream->stream_id) {
01248     httpc->pause_stream_id = 0;
01249   }
01250 
01251   DEBUGASSERT(httpc->drain_total >= data->state.drain);
01252   httpc->drain_total -= data->state.drain;
01253   data->state.drain = 0;
01254 
01255   if(httpc->pause_stream_id == 0) {
01256     if(h2_process_pending_input(data, httpc, err) != 0) {
01257       return -1;
01258     }
01259   }
01260 
01261   DEBUGASSERT(data->state.drain == 0);
01262 
01263   /* Reset to FALSE to prevent infinite loop in readwrite_data function. */
01264   stream->closed = FALSE;
01265   if(stream->error_code != NGHTTP2_NO_ERROR) {
01266     failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %d)",
01267           stream->stream_id, Curl_http2_strerror(stream->error_code),
01268           stream->error_code);
01269     *err = CURLE_HTTP2_STREAM;
01270     return -1;
01271   }
01272 
01273   if(!stream->bodystarted) {
01274     failf(data, "HTTP/2 stream %u was closed cleanly, but before getting "
01275           " all response header fields, teated as error",
01276           stream->stream_id);
01277     *err = CURLE_HTTP2_STREAM;
01278     return -1;
01279   }
01280 
01281   if(stream->trailer_recvbuf && stream->trailer_recvbuf->buffer) {
01282     trailer_pos = stream->trailer_recvbuf->buffer;
01283     trailer_end = trailer_pos + stream->trailer_recvbuf->size_used;
01284 
01285     for(; trailer_pos < trailer_end;) {
01286       uint32_t n;
01287       memcpy(&n, trailer_pos, sizeof(n));
01288       trailer_pos += sizeof(n);
01289 
01290       result = Curl_client_write(conn, CLIENTWRITE_HEADER, trailer_pos, n);
01291       if(result) {
01292         *err = result;
01293         return -1;
01294       }
01295 
01296       trailer_pos += n + 1;
01297     }
01298   }
01299 
01300   stream->close_handled = TRUE;
01301 
01302   DEBUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close\n"));
01303   return 0;
01304 }
01305 
01306 /*
01307  * h2_pri_spec() fills in the pri_spec struct, used by nghttp2 to send weight
01308  * and dependency to the peer. It also stores the updated values in the state
01309  * struct.
01310  */
01311 
01312 static void h2_pri_spec(struct Curl_easy *data,
01313                         nghttp2_priority_spec *pri_spec)
01314 {
01315   struct HTTP *depstream = (data->set.stream_depends_on?
01316                             data->set.stream_depends_on->req.protop:NULL);
01317   int32_t depstream_id = depstream? depstream->stream_id:0;
01318   nghttp2_priority_spec_init(pri_spec, depstream_id, data->set.stream_weight,
01319                              data->set.stream_depends_e);
01320   data->state.stream_weight = data->set.stream_weight;
01321   data->state.stream_depends_e = data->set.stream_depends_e;
01322   data->state.stream_depends_on = data->set.stream_depends_on;
01323 }
01324 
01325 /*
01326  * h2_session_send() checks if there's been an update in the priority /
01327  * dependency settings and if so it submits a PRIORITY frame with the updated
01328  * info.
01329  */
01330 static int h2_session_send(struct Curl_easy *data,
01331                            nghttp2_session *h2)
01332 {
01333   struct HTTP *stream = data->req.protop;
01334   if((data->set.stream_weight != data->state.stream_weight) ||
01335      (data->set.stream_depends_e != data->state.stream_depends_e) ||
01336      (data->set.stream_depends_on != data->state.stream_depends_on) ) {
01337     /* send new weight and/or dependency */
01338     nghttp2_priority_spec pri_spec;
01339     int rv;
01340 
01341     h2_pri_spec(data, &pri_spec);
01342 
01343     DEBUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)\n",
01344                  stream->stream_id, data));
01345     rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
01346                                  &pri_spec);
01347     if(rv)
01348       return rv;
01349   }
01350 
01351   return nghttp2_session_send(h2);
01352 }
01353 
01354 static ssize_t http2_recv(struct connectdata *conn, int sockindex,
01355                           char *mem, size_t len, CURLcode *err)
01356 {
01357   CURLcode result = CURLE_OK;
01358   ssize_t rv;
01359   ssize_t nread;
01360   struct http_conn *httpc = &conn->proto.httpc;
01361   struct Curl_easy *data = conn->data;
01362   struct HTTP *stream = data->req.protop;
01363 
01364   (void)sockindex; /* we always do HTTP2 on sockindex 0 */
01365 
01366   if(should_close_session(httpc)) {
01367     DEBUGF(infof(data,
01368                  "http2_recv: nothing to do in this session\n"));
01369     *err = CURLE_HTTP2;
01370     return -1;
01371   }
01372 
01373   /* Nullify here because we call nghttp2_session_send() and they
01374      might refer to the old buffer. */
01375   stream->upload_mem = NULL;
01376   stream->upload_len = 0;
01377 
01378   /*
01379    * At this point 'stream' is just in the Curl_easy the connection
01380    * identifies as its owner at this time.
01381    */
01382 
01383   if(stream->bodystarted &&
01384      stream->nread_header_recvbuf < stream->header_recvbuf->size_used) {
01385     /* If there is body data pending for this stream to return, do that */
01386     size_t left =
01387       stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
01388     size_t ncopy = MIN(len, left);
01389     memcpy(mem, stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
01390            ncopy);
01391     stream->nread_header_recvbuf += ncopy;
01392 
01393     DEBUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf\n",
01394                  (int)ncopy));
01395     return ncopy;
01396   }
01397 
01398   DEBUGF(infof(data, "http2_recv: easy %p (stream %u)\n",
01399                data, stream->stream_id));
01400 
01401   if((data->state.drain) && stream->memlen) {
01402     DEBUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n",
01403                  stream->memlen, stream->stream_id,
01404                  stream->mem, mem));
01405     if(mem != stream->mem) {
01406       /* if we didn't get the same buffer this time, we must move the data to
01407          the beginning */
01408       memmove(mem, stream->mem, stream->memlen);
01409       stream->len = len - stream->memlen;
01410       stream->mem = mem;
01411     }
01412     if(httpc->pause_stream_id == stream->stream_id && !stream->pausedata) {
01413       /* We have paused nghttp2, but we have no pause data (see
01414          on_data_chunk_recv). */
01415       httpc->pause_stream_id = 0;
01416       if(h2_process_pending_input(data, httpc, &result) != 0) {
01417         *err = result;
01418         return -1;
01419       }
01420     }
01421   }
01422   else if(stream->pausedata) {
01423     DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
01424     nread = MIN(len, stream->pauselen);
01425     memcpy(mem, stream->pausedata, nread);
01426 
01427     stream->pausedata += nread;
01428     stream->pauselen -= nread;
01429 
01430     infof(data, "%zu data bytes written\n", nread);
01431     if(stream->pauselen == 0) {
01432       DEBUGF(infof(data, "Unpaused by stream %u\n", stream->stream_id));
01433       assert(httpc->pause_stream_id == stream->stream_id);
01434       httpc->pause_stream_id = 0;
01435 
01436       stream->pausedata = NULL;
01437       stream->pauselen = 0;
01438 
01439       /* When NGHTTP2_ERR_PAUSE is returned from
01440          data_source_read_callback, we might not process DATA frame
01441          fully.  Calling nghttp2_session_mem_recv() again will
01442          continue to process DATA frame, but if there is no incoming
01443          frames, then we have to call it again with 0-length data.
01444          Without this, on_stream_close callback will not be called,
01445          and stream could be hanged. */
01446       if(h2_process_pending_input(data, httpc, &result) != 0) {
01447         *err = result;
01448         return -1;
01449       }
01450     }
01451     DEBUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u\n",
01452                  nread, stream->stream_id));
01453     return nread;
01454   }
01455   else if(httpc->pause_stream_id) {
01456     /* If a stream paused nghttp2_session_mem_recv previously, and has
01457        not processed all data, it still refers to the buffer in
01458        nghttp2_session.  If we call nghttp2_session_mem_recv(), we may
01459        overwrite that buffer.  To avoid that situation, just return
01460        here with CURLE_AGAIN.  This could be busy loop since data in
01461        socket is not read.  But it seems that usually streams are
01462        notified with its drain property, and socket is read again
01463        quickly. */
01464     DEBUGF(infof(data, "stream %x is paused, pause id: %x\n",
01465                  stream->stream_id, httpc->pause_stream_id));
01466     *err = CURLE_AGAIN;
01467     return -1;
01468   }
01469   else {
01470     char *inbuf;
01471     /* remember where to store incoming data for this stream and how big the
01472        buffer is */
01473     stream->mem = mem;
01474     stream->len = len;
01475     stream->memlen = 0;
01476 
01477     if(httpc->inbuflen == 0) {
01478       nread = ((Curl_recv *)httpc->recv_underlying)(
01479           conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
01480 
01481       if(nread == -1) {
01482         if(result != CURLE_AGAIN)
01483           failf(data, "Failed receiving HTTP2 data");
01484         else if(stream->closed)
01485           /* received when the stream was already closed! */
01486           return http2_handle_stream_close(conn, data, stream, err);
01487 
01488         *err = result;
01489         return -1;
01490       }
01491 
01492       if(nread == 0) {
01493         failf(data, "Unexpected EOF");
01494         *err = CURLE_RECV_ERROR;
01495         return -1;
01496       }
01497 
01498       DEBUGF(infof(data, "nread=%zd\n", nread));
01499 
01500       httpc->inbuflen = nread;
01501       inbuf = httpc->inbuf;
01502     }
01503     else {
01504       nread = httpc->inbuflen - httpc->nread_inbuf;
01505       inbuf = httpc->inbuf + httpc->nread_inbuf;
01506 
01507       DEBUGF(infof(data, "Use data left in connection buffer, nread=%zd\n",
01508                    nread));
01509     }
01510     rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
01511 
01512     if(nghttp2_is_fatal((int)rv)) {
01513       failf(data, "nghttp2_session_mem_recv() returned %d:%s\n",
01514             rv, nghttp2_strerror((int)rv));
01515       *err = CURLE_RECV_ERROR;
01516       return 0;
01517     }
01518     DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", rv));
01519     if(nread == rv) {
01520       DEBUGF(infof(data, "All data in connection buffer processed\n"));
01521       httpc->inbuflen = 0;
01522       httpc->nread_inbuf = 0;
01523     }
01524     else {
01525       httpc->nread_inbuf += rv;
01526       DEBUGF(infof(data, "%zu bytes left in connection buffer\n",
01527                    httpc->inbuflen - httpc->nread_inbuf));
01528     }
01529     /* Always send pending frames in nghttp2 session, because
01530        nghttp2_session_mem_recv() may queue new frame */
01531     rv = h2_session_send(data, httpc->h2);
01532     if(rv != 0) {
01533       *err = CURLE_SEND_ERROR;
01534       return 0;
01535     }
01536 
01537     if(should_close_session(httpc)) {
01538       DEBUGF(infof(data, "http2_recv: nothing to do in this session\n"));
01539       *err = CURLE_HTTP2;
01540       return -1;
01541     }
01542   }
01543   if(stream->memlen) {
01544     ssize_t retlen = stream->memlen;
01545     DEBUGF(infof(data, "http2_recv: returns %zd for stream %u\n",
01546                  retlen, stream->stream_id));
01547     stream->memlen = 0;
01548 
01549     if(httpc->pause_stream_id == stream->stream_id) {
01550       /* data for this stream is returned now, but this stream caused a pause
01551          already so we need it called again asap */
01552       DEBUGF(infof(data, "Data returned for PAUSED stream %u\n",
01553                    stream->stream_id));
01554     }
01555     else if(!stream->closed) {
01556       DEBUGASSERT(httpc->drain_total >= data->state.drain);
01557       httpc->drain_total -= data->state.drain;
01558       data->state.drain = 0; /* this stream is hereby drained */
01559     }
01560 
01561     return retlen;
01562   }
01563   /* If stream is closed, return 0 to signal the http routine to close
01564      the connection */
01565   if(stream->closed) {
01566     return http2_handle_stream_close(conn, data, stream, err);
01567   }
01568   *err = CURLE_AGAIN;
01569   DEBUGF(infof(data, "http2_recv returns AGAIN for stream %u\n",
01570                stream->stream_id));
01571   return -1;
01572 }
01573 
01574 /* Index where :authority header field will appear in request header
01575    field list. */
01576 #define AUTHORITY_DST_IDX 3
01577 
01578 #define HEADER_OVERFLOW(x) \
01579   (x.namelen > (uint16_t)-1 || x.valuelen > (uint16_t)-1 - x.namelen)
01580 
01581 /*
01582  * Check header memory for the token "trailers".
01583  * Parse the tokens as separated by comma and surrounded by whitespace.
01584  * Returns TRUE if found or FALSE if not.
01585  */
01586 static bool contains_trailers(const char *p, size_t len)
01587 {
01588   const char *end = p + len;
01589   for(;;) {
01590     for(; p != end && (*p == ' ' || *p == '\t'); ++p)
01591       ;
01592     if(p == end || (size_t)(end - p) < sizeof("trailers") - 1)
01593       return FALSE;
01594     if(strncasecompare("trailers", p, sizeof("trailers") - 1)) {
01595       p += sizeof("trailers") - 1;
01596       for(; p != end && (*p == ' ' || *p == '\t'); ++p)
01597         ;
01598       if(p == end || *p == ',')
01599         return TRUE;
01600     }
01601     /* skip to next token */
01602     for(; p != end && *p != ','; ++p)
01603       ;
01604     if(p == end)
01605       return FALSE;
01606     ++p;
01607   }
01608 }
01609 
01610 typedef enum {
01611   /* Send header to server */
01612   HEADERINST_FORWARD,
01613   /* Don't send header to server */
01614   HEADERINST_IGNORE,
01615   /* Discard header, and replace it with "te: trailers" */
01616   HEADERINST_TE_TRAILERS
01617 } header_instruction;
01618 
01619 /* Decides how to treat given header field. */
01620 static header_instruction inspect_header(const char *name, size_t namelen,
01621                                          const char *value, size_t valuelen) {
01622   switch(namelen) {
01623   case 2:
01624     if(!strncasecompare("te", name, namelen))
01625       return HEADERINST_FORWARD;
01626 
01627     return contains_trailers(value, valuelen) ?
01628            HEADERINST_TE_TRAILERS : HEADERINST_IGNORE;
01629   case 7:
01630     return strncasecompare("upgrade", name, namelen) ?
01631            HEADERINST_IGNORE : HEADERINST_FORWARD;
01632   case 10:
01633     return (strncasecompare("connection", name, namelen) ||
01634             strncasecompare("keep-alive", name, namelen)) ?
01635            HEADERINST_IGNORE : HEADERINST_FORWARD;
01636   case 16:
01637     return strncasecompare("proxy-connection", name, namelen) ?
01638            HEADERINST_IGNORE : HEADERINST_FORWARD;
01639   case 17:
01640     return strncasecompare("transfer-encoding", name, namelen) ?
01641            HEADERINST_IGNORE : HEADERINST_FORWARD;
01642   default:
01643     return HEADERINST_FORWARD;
01644   }
01645 }
01646 
01647 static ssize_t http2_send(struct connectdata *conn, int sockindex,
01648                           const void *mem, size_t len, CURLcode *err)
01649 {
01650   /*
01651    * BIG TODO: Currently, we send request in this function, but this
01652    * function is also used to send request body. It would be nice to
01653    * add dedicated function for request.
01654    */
01655   int rv;
01656   struct http_conn *httpc = &conn->proto.httpc;
01657   struct HTTP *stream = conn->data->req.protop;
01658   nghttp2_nv *nva = NULL;
01659   size_t nheader;
01660   size_t i;
01661   size_t authority_idx;
01662   char *hdbuf = (char *)mem;
01663   char *end, *line_end;
01664   nghttp2_data_provider data_prd;
01665   int32_t stream_id;
01666   nghttp2_session *h2 = httpc->h2;
01667   nghttp2_priority_spec pri_spec;
01668 
01669   (void)sockindex;
01670 
01671   DEBUGF(infof(conn->data, "http2_send len=%zu\n", len));
01672 
01673   if(stream->stream_id != -1) {
01674     if(stream->close_handled) {
01675       infof(conn->data, "stream %d closed\n", stream->stream_id);
01676       *err = CURLE_HTTP2_STREAM;
01677       return -1;
01678     }
01679     else if(stream->closed) {
01680       return http2_handle_stream_close(conn, conn->data, stream, err);
01681     }
01682     /* If stream_id != -1, we have dispatched request HEADERS, and now
01683        are going to send or sending request body in DATA frame */
01684     stream->upload_mem = mem;
01685     stream->upload_len = len;
01686     nghttp2_session_resume_data(h2, stream->stream_id);
01687     rv = h2_session_send(conn->data, h2);
01688     if(nghttp2_is_fatal(rv)) {
01689       *err = CURLE_SEND_ERROR;
01690       return -1;
01691     }
01692     len -= stream->upload_len;
01693 
01694     /* Nullify here because we call nghttp2_session_send() and they
01695        might refer to the old buffer. */
01696     stream->upload_mem = NULL;
01697     stream->upload_len = 0;
01698 
01699     if(should_close_session(httpc)) {
01700       DEBUGF(infof(conn->data, "http2_send: nothing to do in this session\n"));
01701       *err = CURLE_HTTP2;
01702       return -1;
01703     }
01704 
01705     if(stream->upload_left) {
01706       /* we are sure that we have more data to send here.  Calling the
01707          following API will make nghttp2_session_want_write() return
01708          nonzero if remote window allows it, which then libcurl checks
01709          socket is writable or not.  See http2_perform_getsock(). */
01710       nghttp2_session_resume_data(h2, stream->stream_id);
01711     }
01712 
01713     DEBUGF(infof(conn->data, "http2_send returns %zu for stream %u\n", len,
01714                  stream->stream_id));
01715     return len;
01716   }
01717 
01718   /* Calculate number of headers contained in [mem, mem + len) */
01719   /* Here, we assume the curl http code generate *correct* HTTP header
01720      field block */
01721   nheader = 0;
01722   for(i = 1; i < len; ++i) {
01723     if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') {
01724       ++nheader;
01725       ++i;
01726     }
01727   }
01728   if(nheader < 2)
01729     goto fail;
01730 
01731   /* We counted additional 2 \r\n in the first and last line. We need 3
01732      new headers: :method, :path and :scheme. Therefore we need one
01733      more space. */
01734   nheader += 1;
01735   nva = malloc(sizeof(nghttp2_nv) * nheader);
01736   if(nva == NULL) {
01737     *err = CURLE_OUT_OF_MEMORY;
01738     return -1;
01739   }
01740 
01741   /* Extract :method, :path from request line */
01742   line_end = strstr(hdbuf, "\r\n");
01743 
01744   /* Method does not contain spaces */
01745   end = memchr(hdbuf, ' ', line_end - hdbuf);
01746   if(!end || end == hdbuf)
01747     goto fail;
01748   nva[0].name = (unsigned char *)":method";
01749   nva[0].namelen = strlen((char *)nva[0].name);
01750   nva[0].value = (unsigned char *)hdbuf;
01751   nva[0].valuelen = (size_t)(end - hdbuf);
01752   nva[0].flags = NGHTTP2_NV_FLAG_NONE;
01753   if(HEADER_OVERFLOW(nva[0])) {
01754     failf(conn->data, "Failed sending HTTP request: Header overflow");
01755     goto fail;
01756   }
01757 
01758   hdbuf = end + 1;
01759 
01760   /* Path may contain spaces so scan backwards */
01761   end = NULL;
01762   for(i = (size_t)(line_end - hdbuf); i; --i) {
01763     if(hdbuf[i - 1] == ' ') {
01764       end = &hdbuf[i - 1];
01765       break;
01766     }
01767   }
01768   if(!end || end == hdbuf)
01769     goto fail;
01770   nva[1].name = (unsigned char *)":path";
01771   nva[1].namelen = strlen((char *)nva[1].name);
01772   nva[1].value = (unsigned char *)hdbuf;
01773   nva[1].valuelen = (size_t)(end - hdbuf);
01774   nva[1].flags = NGHTTP2_NV_FLAG_NONE;
01775   if(HEADER_OVERFLOW(nva[1])) {
01776     failf(conn->data, "Failed sending HTTP request: Header overflow");
01777     goto fail;
01778   }
01779 
01780   hdbuf = end + 1;
01781 
01782   end = line_end;
01783   nva[2].name = (unsigned char *)":scheme";
01784   nva[2].namelen = strlen((char *)nva[2].name);
01785   if(conn->handler->flags & PROTOPT_SSL)
01786     nva[2].value = (unsigned char *)"https";
01787   else
01788     nva[2].value = (unsigned char *)"http";
01789   nva[2].valuelen = strlen((char *)nva[2].value);
01790   nva[2].flags = NGHTTP2_NV_FLAG_NONE;
01791   if(HEADER_OVERFLOW(nva[2])) {
01792     failf(conn->data, "Failed sending HTTP request: Header overflow");
01793     goto fail;
01794   }
01795 
01796   authority_idx = 0;
01797   i = 3;
01798   while(i < nheader) {
01799     size_t hlen;
01800 
01801     hdbuf = line_end + 2;
01802 
01803     line_end = strstr(hdbuf, "\r\n");
01804     if(line_end == hdbuf)
01805       goto fail;
01806 
01807     /* header continuation lines are not supported */
01808     if(*hdbuf == ' ' || *hdbuf == '\t')
01809       goto fail;
01810 
01811     for(end = hdbuf; end < line_end && *end != ':'; ++end)
01812       ;
01813     if(end == hdbuf || end == line_end)
01814       goto fail;
01815     hlen = end - hdbuf;
01816 
01817     if(hlen == 4 && strncasecompare("host", hdbuf, 4)) {
01818       authority_idx = i;
01819       nva[i].name = (unsigned char *)":authority";
01820       nva[i].namelen = strlen((char *)nva[i].name);
01821     }
01822     else {
01823       nva[i].name = (unsigned char *)hdbuf;
01824       nva[i].namelen = (size_t)(end - hdbuf);
01825     }
01826     hdbuf = end + 1;
01827     while(*hdbuf == ' ' || *hdbuf == '\t')
01828       ++hdbuf;
01829     end = line_end;
01830 
01831     switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf,
01832                           end - hdbuf)) {
01833     case HEADERINST_IGNORE:
01834       /* skip header fields prohibited by HTTP/2 specification. */
01835       --nheader;
01836       continue;
01837     case HEADERINST_TE_TRAILERS:
01838       nva[i].value = (uint8_t*)"trailers";
01839       nva[i].valuelen = sizeof("trailers") - 1;
01840       break;
01841     default:
01842       nva[i].value = (unsigned char *)hdbuf;
01843       nva[i].valuelen = (size_t)(end - hdbuf);
01844     }
01845 
01846     nva[i].flags = NGHTTP2_NV_FLAG_NONE;
01847     if(HEADER_OVERFLOW(nva[i])) {
01848       failf(conn->data, "Failed sending HTTP request: Header overflow");
01849       goto fail;
01850     }
01851     ++i;
01852   }
01853 
01854   /* :authority must come before non-pseudo header fields */
01855   if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) {
01856     nghttp2_nv authority = nva[authority_idx];
01857     for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
01858       nva[i] = nva[i - 1];
01859     }
01860     nva[i] = authority;
01861   }
01862 
01863   /* Warn stream may be rejected if cumulative length of headers is too large.
01864      It appears nghttp2 will not send a header frame larger than 64KB. */
01865   {
01866     size_t acc = 0;
01867     const size_t max_acc = 60000;  /* <64KB to account for some overhead */
01868 
01869     for(i = 0; i < nheader; ++i) {
01870       if(nva[i].namelen > max_acc - acc)
01871         break;
01872       acc += nva[i].namelen;
01873 
01874       if(nva[i].valuelen > max_acc - acc)
01875         break;
01876       acc += nva[i].valuelen;
01877 
01878       DEBUGF(infof(conn->data, "h2 header: %.*s:%.*s\n",
01879                    nva[i].namelen, nva[i].name,
01880                    nva[i].valuelen, nva[i].value));
01881     }
01882 
01883     if(i != nheader) {
01884       infof(conn->data, "http2_send: Warning: The cumulative length of all "
01885                         "headers exceeds %zu bytes and that could cause the "
01886                         "stream to be rejected.\n", max_acc);
01887     }
01888   }
01889 
01890   h2_pri_spec(conn->data, &pri_spec);
01891 
01892   switch(conn->data->set.httpreq) {
01893   case HTTPREQ_POST:
01894   case HTTPREQ_POST_FORM:
01895   case HTTPREQ_PUT:
01896     if(conn->data->state.infilesize != -1)
01897       stream->upload_left = conn->data->state.infilesize;
01898     else
01899       /* data sending without specifying the data amount up front */
01900       stream->upload_left = -1; /* unknown, but not zero */
01901 
01902     data_prd.read_callback = data_source_read_callback;
01903     data_prd.source.ptr = NULL;
01904     stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
01905                                        &data_prd, conn->data);
01906     break;
01907   default:
01908     stream_id = nghttp2_submit_request(h2, &pri_spec, nva, nheader,
01909                                        NULL, conn->data);
01910   }
01911 
01912   Curl_safefree(nva);
01913 
01914   if(stream_id < 0) {
01915     DEBUGF(infof(conn->data, "http2_send() send error\n"));
01916     *err = CURLE_SEND_ERROR;
01917     return -1;
01918   }
01919 
01920   infof(conn->data, "Using Stream ID: %x (easy handle %p)\n",
01921         stream_id, conn->data);
01922   stream->stream_id = stream_id;
01923 
01924   /* this does not call h2_session_send() since there can not have been any
01925    * priority upodate since the nghttp2_submit_request() call above */
01926   rv = nghttp2_session_send(h2);
01927 
01928   if(rv != 0) {
01929     *err = CURLE_SEND_ERROR;
01930     return -1;
01931   }
01932 
01933   if(should_close_session(httpc)) {
01934     DEBUGF(infof(conn->data, "http2_send: nothing to do in this session\n"));
01935     *err = CURLE_HTTP2;
01936     return -1;
01937   }
01938 
01939   if(stream->stream_id != -1) {
01940     /* If whole HEADERS frame was sent off to the underlying socket,
01941        the nghttp2 library calls data_source_read_callback. But only
01942        it found that no data available, so it deferred the DATA
01943        transmission. Which means that nghttp2_session_want_write()
01944        returns 0 on http2_perform_getsock(), which results that no
01945        writable socket check is performed. To workaround this, we
01946        issue nghttp2_session_resume_data() here to bring back DATA
01947        transmission from deferred state. */
01948     nghttp2_session_resume_data(h2, stream->stream_id);
01949   }
01950 
01951   return len;
01952 
01953 fail:
01954   free(nva);
01955   *err = CURLE_SEND_ERROR;
01956   return -1;
01957 }
01958 
01959 CURLcode Curl_http2_setup(struct connectdata *conn)
01960 {
01961   CURLcode result;
01962   struct http_conn *httpc = &conn->proto.httpc;
01963   struct HTTP *stream = conn->data->req.protop;
01964 
01965   stream->stream_id = -1;
01966 
01967   if(!stream->header_recvbuf)
01968     stream->header_recvbuf = Curl_add_buffer_init();
01969 
01970   if((conn->handler == &Curl_handler_http2_ssl) ||
01971      (conn->handler == &Curl_handler_http2))
01972     return CURLE_OK; /* already done */
01973 
01974   if(conn->handler->flags & PROTOPT_SSL)
01975     conn->handler = &Curl_handler_http2_ssl;
01976   else
01977     conn->handler = &Curl_handler_http2;
01978 
01979   result = Curl_http2_init(conn);
01980   if(result)
01981     return result;
01982 
01983   infof(conn->data, "Using HTTP2, server supports multi-use\n");
01984   stream->upload_left = 0;
01985   stream->upload_mem = NULL;
01986   stream->upload_len = 0;
01987 
01988   httpc->inbuflen = 0;
01989   httpc->nread_inbuf = 0;
01990 
01991   httpc->pause_stream_id = 0;
01992   httpc->drain_total = 0;
01993 
01994   conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
01995   conn->httpversion = 20;
01996   conn->bundle->multiuse = BUNDLE_MULTIPLEX;
01997 
01998   infof(conn->data, "Connection state changed (HTTP/2 confirmed)\n");
01999   Curl_multi_connchanged(conn->data->multi);
02000 
02001   return CURLE_OK;
02002 }
02003 
02004 CURLcode Curl_http2_switched(struct connectdata *conn,
02005                              const char *mem, size_t nread)
02006 {
02007   CURLcode result;
02008   struct http_conn *httpc = &conn->proto.httpc;
02009   int rv;
02010   ssize_t nproc;
02011   struct Curl_easy *data = conn->data;
02012   struct HTTP *stream = conn->data->req.protop;
02013 
02014   result = Curl_http2_setup(conn);
02015   if(result)
02016     return result;
02017 
02018   httpc->recv_underlying = (recving)conn->recv[FIRSTSOCKET];
02019   httpc->send_underlying = (sending)conn->send[FIRSTSOCKET];
02020   conn->recv[FIRSTSOCKET] = http2_recv;
02021   conn->send[FIRSTSOCKET] = http2_send;
02022 
02023   if(conn->data->req.upgr101 == UPGR101_RECEIVED) {
02024     /* stream 1 is opened implicitly on upgrade */
02025     stream->stream_id = 1;
02026     /* queue SETTINGS frame (again) */
02027     rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings,
02028                                  httpc->binlen, NULL);
02029     if(rv != 0) {
02030       failf(data, "nghttp2_session_upgrade() failed: %s(%d)",
02031             nghttp2_strerror(rv), rv);
02032       return CURLE_HTTP2;
02033     }
02034 
02035     nghttp2_session_set_stream_user_data(httpc->h2,
02036                                          stream->stream_id,
02037                                          conn->data);
02038   }
02039   else {
02040     /* stream ID is unknown at this point */
02041     stream->stream_id = -1;
02042     rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE, settings,
02043                                  sizeof(settings) / sizeof(settings[0]));
02044     if(rv != 0) {
02045       failf(data, "nghttp2_submit_settings() failed: %s(%d)",
02046             nghttp2_strerror(rv), rv);
02047       return CURLE_HTTP2;
02048     }
02049   }
02050 
02051 #ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
02052   rv = nghttp2_session_set_local_window_size(httpc->h2, NGHTTP2_FLAG_NONE, 0,
02053                                              HTTP2_HUGE_WINDOW_SIZE);
02054   if(rv != 0) {
02055     failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
02056           nghttp2_strerror(rv), rv);
02057     return CURLE_HTTP2;
02058   }
02059 #endif
02060 
02061   /* we are going to copy mem to httpc->inbuf.  This is required since
02062      mem is part of buffer pointed by stream->mem, and callbacks
02063      called by nghttp2_session_mem_recv() will write stream specific
02064      data into stream->mem, overwriting data already there. */
02065   if(H2_BUFSIZE < nread) {
02066     failf(data, "connection buffer size is too small to store data following "
02067                 "HTTP Upgrade response header: buflen=%zu, datalen=%zu",
02068           H2_BUFSIZE, nread);
02069     return CURLE_HTTP2;
02070   }
02071 
02072   infof(conn->data, "Copying HTTP/2 data in stream buffer to connection buffer"
02073                     " after upgrade: len=%zu\n",
02074         nread);
02075 
02076   if(nread)
02077     memcpy(httpc->inbuf, mem, nread);
02078   httpc->inbuflen = nread;
02079 
02080   nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf,
02081                                    httpc->inbuflen);
02082 
02083   if(nghttp2_is_fatal((int)nproc)) {
02084     failf(data, "nghttp2_session_mem_recv() failed: %s(%d)",
02085           nghttp2_strerror((int)nproc), (int)nproc);
02086     return CURLE_HTTP2;
02087   }
02088 
02089   DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", nproc));
02090 
02091   if((ssize_t)nread == nproc) {
02092     httpc->inbuflen = 0;
02093     httpc->nread_inbuf = 0;
02094   }
02095   else {
02096     httpc->nread_inbuf += nproc;
02097   }
02098 
02099   /* Try to send some frames since we may read SETTINGS already. */
02100   rv = h2_session_send(data, httpc->h2);
02101 
02102   if(rv != 0) {
02103     failf(data, "nghttp2_session_send() failed: %s(%d)",
02104           nghttp2_strerror(rv), rv);
02105     return CURLE_HTTP2;
02106   }
02107 
02108   if(should_close_session(httpc)) {
02109     DEBUGF(infof(data,
02110                  "nghttp2_session_send(): nothing to do in this session\n"));
02111     return CURLE_HTTP2;
02112   }
02113 
02114   return CURLE_OK;
02115 }
02116 
02117 void Curl_http2_add_child(struct Curl_easy *parent, struct Curl_easy *child,
02118                           bool exclusive)
02119 {
02120   struct Curl_http2_dep **tail;
02121   struct Curl_http2_dep *dep = calloc(1, sizeof(struct Curl_http2_dep));
02122   dep->data = child;
02123 
02124   if(parent->set.stream_dependents && exclusive) {
02125     struct Curl_http2_dep *node = parent->set.stream_dependents;
02126     while(node) {
02127       node->data->set.stream_depends_on = child;
02128       node = node->next;
02129     }
02130 
02131     tail = &child->set.stream_dependents;
02132     while(*tail)
02133       tail = &(*tail)->next;
02134 
02135     DEBUGASSERT(!*tail);
02136     *tail = parent->set.stream_dependents;
02137     parent->set.stream_dependents = 0;
02138   }
02139 
02140   tail = &parent->set.stream_dependents;
02141   while(*tail) {
02142     (*tail)->data->set.stream_depends_e = FALSE;
02143     tail = &(*tail)->next;
02144   }
02145 
02146   DEBUGASSERT(!*tail);
02147   *tail = dep;
02148 
02149   child->set.stream_depends_on = parent;
02150   child->set.stream_depends_e = exclusive;
02151 }
02152 
02153 void Curl_http2_remove_child(struct Curl_easy *parent, struct Curl_easy *child)
02154 {
02155   struct Curl_http2_dep *last = 0;
02156   struct Curl_http2_dep *data = parent->set.stream_dependents;
02157   DEBUGASSERT(child->set.stream_depends_on == parent);
02158 
02159   while(data && data->data != child) {
02160     last = data;
02161     data = data->next;
02162   }
02163 
02164   DEBUGASSERT(data);
02165 
02166   if(data) {
02167     if(last) {
02168       last->next = data->next;
02169     }
02170     else {
02171       parent->set.stream_dependents = data->next;
02172     }
02173     free(data);
02174   }
02175 
02176   child->set.stream_depends_on = 0;
02177   child->set.stream_depends_e = FALSE;
02178 }
02179 
02180 void Curl_http2_cleanup_dependencies(struct Curl_easy *data)
02181 {
02182   while(data->set.stream_dependents) {
02183     struct Curl_easy *tmp = data->set.stream_dependents->data;
02184     Curl_http2_remove_child(data, tmp);
02185     if(data->set.stream_depends_on)
02186       Curl_http2_add_child(data->set.stream_depends_on, tmp, FALSE);
02187   }
02188 
02189   if(data->set.stream_depends_on)
02190     Curl_http2_remove_child(data->set.stream_depends_on, data);
02191 }
02192 
02193 #else /* !USE_NGHTTP2 */
02194 
02195 /* Satisfy external references even if http2 is not compiled in. */
02196 
02197 #define CURL_DISABLE_TYPECHECK
02198 #include <curl/curl.h>
02199 
02200 char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
02201 {
02202   (void) h;
02203   (void) num;
02204   return NULL;
02205 }
02206 
02207 char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
02208 {
02209   (void) h;
02210   (void) header;
02211   return NULL;
02212 }
02213 
02214 #endif /* USE_NGHTTP2 */


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:04