CURLcode Curl_http2_switched(struct connectdata *conn, const char *mem, size_t nread) { CURLcode result; struct http_conn *httpc = &conn->proto.httpc; int rv; struct SessionHandle *data = conn->data; struct HTTP *stream = conn->data->req.protop; result = Curl_http2_setup(conn); if(result) return result; httpc->recv_underlying = (recving)conn->recv[FIRSTSOCKET]; httpc->send_underlying = (sending)conn->send[FIRSTSOCKET]; conn->recv[FIRSTSOCKET] = http2_recv; conn->send[FIRSTSOCKET] = http2_send; if(conn->data->req.upgr101 == UPGR101_RECEIVED) { /* stream 1 is opened implicitly on upgrade */ stream->stream_id = 1; /* queue SETTINGS frame (again) */ rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings, httpc->binlen, NULL); if(rv != 0) { failf(data, "nghttp2_session_upgrade() failed: %s(%d)", nghttp2_strerror(rv), rv); return CURLE_HTTP2; } } else { /* stream ID is unknown at this point */ stream->stream_id = -1; rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE, NULL, 0); if(rv != 0) { failf(data, "nghttp2_submit_settings() failed: %s(%d)", nghttp2_strerror(rv), rv); return CURLE_HTTP2; } } rv = (int)nghttp2_session_mem_recv(httpc->h2, (const uint8_t*)mem, nread); if(rv != (int)nread) { failf(data, "nghttp2_session_mem_recv() failed: %s(%d)", nghttp2_strerror(rv), rv); return CURLE_HTTP2; } /* Try to send some frames since we may read SETTINGS already. */ rv = nghttp2_session_send(httpc->h2); if(rv != 0) { failf(data, "nghttp2_session_send() failed: %s(%d)", nghttp2_strerror(rv), rv); return CURLE_HTTP2; } return CURLE_OK; }
/* * Append headers to ask for a HTTP1.1 to HTTP2 upgrade. */ CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req, struct connectdata *conn) { CURLcode result; ssize_t binlen; char *base64; size_t blen; struct SingleRequest *k = &conn->data->req; uint8_t *binsettings = conn->proto.httpc.binsettings; result = Curl_http2_init(conn); if(result) return result; result = Curl_http2_setup(conn); if(result) return result; /* As long as we have a fixed set of settings, we don't have to dynamically * figure out the base64 strings since it'll always be the same. However, * the settings will likely not be fixed every time in the future. */ /* this returns number of bytes it wrote */ binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN, settings, sizeof(settings)/sizeof(settings[0])); if(!binlen) { failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload"); return CURLE_FAILED_INIT; } conn->proto.httpc.binlen = binlen; result = Curl_base64url_encode(conn->data, (const char *)binsettings, binlen, &base64, &blen); if(result) return result; result = Curl_add_bufferf(req, "Connection: Upgrade, HTTP2-Settings\r\n" "Upgrade: %s\r\n" "HTTP2-Settings: %s\r\n", NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64); Curl_safefree(base64); k->upgr101 = UPGR101_REQUESTED; return result; }
CURLcode Curl_http2_switched(struct connectdata *conn, const char *mem, size_t nread) { CURLcode result; struct http_conn *httpc = &conn->proto.httpc; int rv; ssize_t nproc; struct SessionHandle *data = conn->data; struct HTTP *stream = conn->data->req.protop; result = Curl_http2_setup(conn); if(result) return result; httpc->recv_underlying = (recving)conn->recv[FIRSTSOCKET]; httpc->send_underlying = (sending)conn->send[FIRSTSOCKET]; conn->recv[FIRSTSOCKET] = http2_recv; conn->send[FIRSTSOCKET] = http2_send; if(conn->data->req.upgr101 == UPGR101_RECEIVED) { /* stream 1 is opened implicitly on upgrade */ stream->stream_id = 1; /* queue SETTINGS frame (again) */ rv = nghttp2_session_upgrade(httpc->h2, httpc->binsettings, httpc->binlen, NULL); if(rv != 0) { failf(data, "nghttp2_session_upgrade() failed: %s(%d)", nghttp2_strerror(rv), rv); return CURLE_HTTP2; } nghttp2_session_set_stream_user_data(httpc->h2, stream->stream_id, conn->data); } else { /* stream ID is unknown at this point */ stream->stream_id = -1; rv = nghttp2_submit_settings(httpc->h2, NGHTTP2_FLAG_NONE, NULL, 0); if(rv != 0) { failf(data, "nghttp2_submit_settings() failed: %s(%d)", nghttp2_strerror(rv), rv); return CURLE_HTTP2; } } /* we are going to copy mem to httpc->inbuf. This is required since mem is part of buffer pointed by stream->mem, and callbacks called by nghttp2_session_mem_recv() will write stream specific data into stream->mem, overwriting data already there. */ if(H2_BUFSIZE < nread) { failf(data, "connection buffer size is too small to store data following " "HTTP Upgrade response header: buflen=%zu, datalen=%zu", H2_BUFSIZE, nread); return CURLE_HTTP2; } infof(conn->data, "Copying HTTP/2 data in stream buffer to connection buffer" " after upgrade: len=%zu\n", nread); memcpy(httpc->inbuf, mem, nread); httpc->inbuflen = nread; nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf, httpc->inbuflen); if(nghttp2_is_fatal((int)nproc)) { failf(data, "nghttp2_session_mem_recv() failed: %s(%d)", nghttp2_strerror((int)nproc), (int)nproc); return CURLE_HTTP2; } DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", nproc)); if((ssize_t)nread == nproc) { httpc->inbuflen = 0; httpc->nread_inbuf = 0; } else { httpc->nread_inbuf += nproc; } /* Try to send some frames since we may read SETTINGS already. */ rv = nghttp2_session_send(httpc->h2); if(rv != 0) { failf(data, "nghttp2_session_send() failed: %s(%d)", nghttp2_strerror(rv), rv); return CURLE_HTTP2; } return CURLE_OK; }