aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2015-06-01 14:20:57 +0200
committerDaniel Stenberg <daniel@haxx.se>2015-06-24 23:44:42 +0200
commitfeea9263e9066768323a759ee178c144fccf5998 (patch)
treed17fc4c72cba65e48645abc5e762e0698d2a8602
parentea7134ac874a66107e54ff93657ac565cf2ec4aa (diff)
http2: setup the new pushed stream properly
-rw-r--r--lib/http.c1
-rw-r--r--lib/http.h1
-rw-r--r--lib/http2.c140
-rw-r--r--lib/http2.h2
-rw-r--r--lib/multi.c15
-rw-r--r--lib/multiif.h6
6 files changed, 127 insertions, 38 deletions
diff --git a/lib/http.c b/lib/http.c
index e06c798e9..d307eabd5 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -164,6 +164,7 @@ CURLcode Curl_http_setup_conn(struct connectdata *conn)
conn->data->req.protop = http;
Curl_http2_setup_conn(conn);
+ Curl_http2_setup_req(conn->data);
return CURLE_OK;
}
diff --git a/lib/http.h b/lib/http.h
index 415be39e1..80ec68303 100644
--- a/lib/http.h
+++ b/lib/http.h
@@ -176,6 +176,7 @@ struct HTTP {
const uint8_t *upload_mem; /* points to a buffer to read from */
size_t upload_len; /* size of the buffer 'upload_mem' points to */
curl_off_t upload_left; /* number of bytes left to upload */
+ Curl_send_buffer *push_recvbuf; /* store incoming push headers */
#endif
};
diff --git a/lib/http2.c b/lib/http2.c
index ae8afa480..8f5b6930b 100644
--- a/lib/http2.c
+++ b/lib/http2.c
@@ -95,12 +95,9 @@ static CURLcode http2_disconnect(struct connectdata *conn,
}
/* called from Curl_http_setup_conn */
-void Curl_http2_setup_conn(struct connectdata *conn)
+void Curl_http2_setup_req(struct SessionHandle *data)
{
- struct HTTP *http = conn->data->req.protop;
-
- conn->proto.httpc.settings.max_concurrent_streams =
- DEFAULT_MAX_CONCURRENT_STREAMS;
+ struct HTTP *http = data->req.protop;
http->nread_header_recvbuf = 0;
http->bodystarted = FALSE;
@@ -109,13 +106,18 @@ void Curl_http2_setup_conn(struct connectdata *conn)
http->pauselen = 0;
http->error_code = NGHTTP2_NO_ERROR;
http->closed = FALSE;
-
- /* where to store incoming data for this stream and how big the buffer is */
- http->mem = conn->data->state.buffer;
+ http->mem = data->state.buffer;
http->len = BUFSIZE;
http->memlen = 0;
}
+/* called from Curl_http_setup_conn */
+void Curl_http2_setup_conn(struct connectdata *conn)
+{
+ conn->proto.httpc.settings.max_concurrent_streams =
+ DEFAULT_MAX_CONCURRENT_STREAMS;
+}
+
/*
* HTTP2 handler interface. This isn't added to the general list of protocols
* but will be used at run-time when the protocol is dynamically switched from
@@ -228,46 +230,98 @@ struct curl_headerpair *curl_pushheader_bynum(struct curl_pushheaders *h,
return NULL;
}
+static CURL *duphandle(struct SessionHandle *data)
+{
+ struct SessionHandle *second = curl_easy_duphandle(data);
+ if(second) {
+ /* setup the request struct */
+ struct HTTP *http = calloc(1, sizeof(struct HTTP));
+ if(!http) {
+ (void)Curl_close(second);
+ second = NULL;
+ }
+ else {
+ second->req.protop = http;
+ http->header_recvbuf = Curl_add_buffer_init();
+ if(!http->header_recvbuf) {
+ free(http);
+ (void)Curl_close(second);
+ second = NULL;
+ }
+ else
+ Curl_http2_setup_req(second);
+ }
+ }
+ return second;
+}
+
+
static int push_promise(struct SessionHandle *data,
+ struct connectdata *conn,
const nghttp2_push_promise *frame)
{
int rv;
+ DEBUGF(infof(data, "PUSH_PROMISE received, stream %u!\n",
+ frame->promised_stream_id));
if(data->multi->push_cb) {
+ struct HTTP *stream;
+ struct curl_pushheaders heads;
+ CURLMcode rc;
+ struct http_conn *httpc;
/* clone the parent */
- CURL *newhandle = curl_easy_duphandle(data);
+ CURL *newhandle = duphandle(data);
if(!newhandle) {
infof(data, "failed to duplicate handle\n");
rv = 1; /* FAIL HARD */
+ goto fail;
}
- else {
- struct curl_pushheaders heads;
- heads.data = data;
- heads.frame = frame;
- /* ask the application */
- DEBUGF(infof(data, "Got PUSH_PROMISE, ask application!\n"));
- rv = data->multi->push_cb(data, newhandle,
- frame->nvlen, &heads,
- data->multi->push_userp);
- if(rv)
- /* denied, kill off the new handle again */
- (void)Curl_close(newhandle);
- else {
- /* approved, add to the multi handle */
- CURLMcode rc = curl_multi_add_handle(data->multi, newhandle);
- if(rc) {
- infof(data, "failed to add handle to multi\n");
- Curl_close(newhandle);
- rv = 1;
- }
- else
- rv = 0;
- }
+
+ heads.data = data;
+ heads.frame = frame;
+ /* ask the application */
+ DEBUGF(infof(data, "Got PUSH_PROMISE, ask application!\n"));
+
+ stream = data->req.protop;
+
+#ifdef CURLDEBUG
+ fprintf(stderr, "PUSHHDR %s\n", stream->push_recvbuf->buffer);
+#endif
+
+ rv = data->multi->push_cb(data, newhandle,
+ frame->nvlen, &heads,
+ data->multi->push_userp);
+ if(rv) {
+ /* denied, kill off the new handle again */
+ (void)Curl_close(newhandle);
+ goto fail;
+ }
+
+ /* approved, add to the multi handle and immediately switch to PERFORM
+ state with the given connection !*/
+ rc = Curl_multi_add_perform(data->multi, newhandle, conn);
+ if(rc) {
+ infof(data, "failed to add handle to multi\n");
+ Curl_close(newhandle);
+ rv = 1;
+ goto fail;
}
+
+ httpc = &conn->proto.httpc;
+ /* put the newhandle in the hash with the stream id as key */
+ if(!Curl_hash_add(&httpc->streamsh,
+ (size_t *)&frame->promised_stream_id,
+ sizeof(frame->promised_stream_id), newhandle)) {
+ failf(conn->data, "Couldn't add stream to hash!");
+ rv = 1;
+ }
+ else
+ rv = 0;
}
else {
DEBUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n"));
rv = 1;
}
+ fail:
return rv;
}
@@ -358,7 +412,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
Curl_expire(data_s, 1);
break;
case NGHTTP2_PUSH_PROMISE:
- rv = push_promise(data_s, &frame->push_promise);
+ rv = push_promise(data_s, conn, &frame->push_promise);
if(rv) { /* deny! */
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
frame->push_promise.promised_stream_id,
@@ -591,11 +645,6 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
(void)frame;
(void)flags;
- /* Ignore PUSH_PROMISE for now */
- if(frame->hd.type != NGHTTP2_HEADERS) {
- return 0;
- }
-
DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
/* get the stream from the hash based on Stream ID */
@@ -615,6 +664,21 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
consequence is handled in on_frame_recv(). */
return 0;
+ /* Store received PUSH_PROMISE headers to be used when the subsequent
+ PUSH_PROMISE callback comes */
+ if(frame->hd.type == NGHTTP2_PUSH_PROMISE) {
+ fprintf(stderr, "*** PUSH_PROMISE headers on stream %u for %u\n",
+ stream_id,
+ frame->push_promise.promised_stream_id);
+ if(!stream->push_recvbuf)
+ stream->push_recvbuf = Curl_add_buffer_init();
+ Curl_add_buffer(stream->push_recvbuf, name, namelen);
+ Curl_add_buffer(stream->push_recvbuf, ":", 1);
+ Curl_add_buffer(stream->push_recvbuf, value, valuelen);
+ Curl_add_buffer(stream->push_recvbuf, "\r\n", 2);
+ return 0;
+ }
+
if(namelen == sizeof(":status") - 1 &&
memcmp(":status", name, namelen) == 0) {
/* nghttp2 guarantees :status is received first and only once, and
diff --git a/lib/http2.h b/lib/http2.h
index 1614736d3..bb7ad9c4c 100644
--- a/lib/http2.h
+++ b/lib/http2.h
@@ -46,6 +46,7 @@ CURLcode Curl_http2_switched(struct connectdata *conn,
const char *data, size_t nread);
/* called from Curl_http_setup_conn */
void Curl_http2_setup_conn(struct connectdata *conn);
+void Curl_http2_setup_req(struct SessionHandle *data);
#else /* USE_NGHTTP2 */
#define Curl_http2_init(x) CURLE_UNSUPPORTED_PROTOCOL
#define Curl_http2_send_request(x) CURLE_UNSUPPORTED_PROTOCOL
@@ -53,6 +54,7 @@ void Curl_http2_setup_conn(struct connectdata *conn);
#define Curl_http2_setup(x) CURLE_UNSUPPORTED_PROTOCOL
#define Curl_http2_switched(x,y,z) CURLE_UNSUPPORTED_PROTOCOL
#define Curl_http2_setup_conn(x)
+#define Curl_http2_setup_req(x)
#endif
#endif /* HEADER_CURL_HTTP2_H */
diff --git a/lib/multi.c b/lib/multi.c
index 33c03f299..a17af5a21 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -950,6 +950,21 @@ static bool multi_ischanged(struct Curl_multi *multi, bool clear)
return retval;
}
+CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
+ struct SessionHandle *data,
+ struct connectdata *conn)
+{
+ CURLMcode rc;
+
+ rc = curl_multi_add_handle(multi, data);
+ if(!rc) {
+ /* take this handle to the perform state right away */
+ multistate(data, CURLM_STATE_PERFORM);
+ data->easy_conn = conn;
+ }
+ return rc;
+}
+
static CURLMcode multi_runsingle(struct Curl_multi *multi,
struct timeval now,
struct SessionHandle *data)
diff --git a/lib/multiif.h b/lib/multiif.h
index 5052f65ae..e6323adf5 100644
--- a/lib/multiif.h
+++ b/lib/multiif.h
@@ -88,4 +88,10 @@ void Curl_multi_connchanged(struct Curl_multi *multi);
void Curl_multi_closed(struct connectdata *conn, curl_socket_t s);
+/*
+ * Add a handle and move it into PERFORM state at once. For pushed streams.
+ */
+CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
+ struct SessionHandle *data,
+ struct connectdata *conn);
#endif /* HEADER_CURL_MULTIIF_H */