Require nghttp2 v1.0.0

This commit requires nghttp2 v1.0.0 to compile, and migrate to v1.0.0,
and utilize recent version of nghttp2 to simplify the code,

First we use nghttp2_option_set_no_recv_client_magic function to
detect nghttp2 v1.0.0.  That function only exists since v1.0.0.

Since nghttp2 v0.7.5, nghttp2 ensures header field ordering, and
validates received header field.  If it found error, RST_STREAM with
PROTOCOL_ERROR is issued.  Since we require v1.0.0, we can utilize
this feature to simplify libcurl code.  This commit does this.

Migration from 0.7 series are done based on nghttp2 migration
document.  For libcurl, we removed the code sending first 24 bytes
client magic.  It is now done by nghttp2 library.
on_invalid_frame_recv callback signature changed, and is updated
accordingly.
This commit is contained in:
Tatsuhiro Tsujikawa 2015-05-16 18:03:47 +09:00 committed by Daniel Stenberg
parent 077f12b0ae
commit 4ac6cc3ebd
2 changed files with 32 additions and 104 deletions

View File

@ -2841,7 +2841,9 @@ if test X"$want_h2" != Xno; then
CPPFLAGS="$CPPFLAGS $CPP_H2"
LIBS="$LIB_H2 $LIBS"
AC_CHECK_LIB(nghttp2, nghttp2_session_callbacks_set_send_callback,
# use nghttp2_option_set_no_recv_client_magic to require nghttp2
# >= 1.0.0
AC_CHECK_LIB(nghttp2, nghttp2_option_set_no_recv_client_magic,
[
AC_CHECK_HEADERS(nghttp2/nghttp2.h,
curl_h2_msg="enabled (nghttp2)"

View File

@ -238,37 +238,21 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
break;
if(stream->bodystarted) {
/* Only valid HEADERS after body started is trailer header,
which is not fully supported in this code. If HEADERS is not
trailer, then it is a PROTOCOL_ERROR. */
if((frame->hd.flags & NGHTTP2_FLAG_END_STREAM) == 0) {
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
stream_id, NGHTTP2_PROTOCOL_ERROR);
if(nghttp2_is_fatal(rv)) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
}
/* Only valid HEADERS after body started is trailer HEADERS. We
ignores trailer HEADERS for now. nghttp2 guarantees that it
has END_STREAM flag set. */
break;
}
if(stream->status_code == -1) {
/* No :status header field means PROTOCOL_ERROR. */
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
stream_id, NGHTTP2_PROTOCOL_ERROR);
if(nghttp2_is_fatal(rv)) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
break;
}
/* nghttp2 guarantees that :status is received, and we store it to
stream->status_code */
DEBUGASSERT(stream->status_code != -1);
/* Only final status code signals the end of header */
if(stream->status_code / 100 != 1)
if(stream->status_code / 100 != 1) {
stream->bodystarted = TRUE;
stream->status_code = -1;
stream->status_code = -1;
}
Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
@ -330,14 +314,14 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
static int on_invalid_frame_recv(nghttp2_session *session,
const nghttp2_frame *frame,
uint32_t error_code, void *userp)
int lib_error_code, void *userp)
{
struct connectdata *conn = (struct connectdata *)userp;
(void)session;
(void)frame;
DEBUGF(infof(conn->data,
"on_invalid_frame_recv() was called, error_code = %d\n",
error_code));
"on_invalid_frame_recv() was called, error=%d:%s\n",
lib_error_code, nghttp2_strerror(lib_error_code)));
return 0;
}
@ -503,8 +487,6 @@ static int decode_status_code(const uint8_t *value, size_t len)
return res;
}
static const char STATUS[] = ":status";
/* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
const uint8_t *name, size_t namelen,
@ -515,9 +497,6 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
struct connectdata *conn = (struct connectdata *)userp;
struct HTTP *stream;
struct SessionHandle *data_s;
int rv;
int goodname;
int goodheader;
int32_t stream_id = frame->hd.stream_id;
(void)session;
@ -548,40 +527,13 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
consequence is handled in on_frame_recv(). */
return 0;
goodname = nghttp2_check_header_name(name, namelen);
goodheader = nghttp2_check_header_value(value, valuelen);
if(!goodname || !goodheader) {
infof(data_s, "Detected bad incoming header %s%s, reset stream!\n",
goodname?"":"name",
goodheader?"":"value");
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
stream_id, NGHTTP2_PROTOCOL_ERROR);
if(nghttp2_is_fatal(rv)) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
if(namelen == sizeof(":status") - 1 &&
memcmp(STATUS, name, namelen) == 0) {
/* :status must appear exactly once. */
if(stream->status_code != -1 ||
(stream->status_code = decode_status_code(value, valuelen)) == -1) {
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
stream_id, NGHTTP2_PROTOCOL_ERROR);
if(nghttp2_is_fatal(rv)) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
memcmp(":status", name, namelen) == 0) {
/* nghttp2 guarantees :status is received first and only once, and
value is 3 digits status code, and decode_status_code always
succeeds. */
stream->status_code = decode_status_code(value, valuelen);
DEBUGASSERT(stream->status_code != -1);
Curl_add_buffer(stream->header_recvbuf, "HTTP/2.0 ", 9);
Curl_add_buffer(stream->header_recvbuf, value, valuelen);
@ -593,31 +545,19 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
stream->status_code));
return 0;
}
else {
/* Here we are sure that namelen > 0 because of
nghttp2_check_header_name(). Pseudo header other than :status
is illegal. */
if(stream->status_code == -1 || name[0] == ':') {
rv = nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE,
stream_id, NGHTTP2_PROTOCOL_ERROR);
if(nghttp2_is_fatal(rv)) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
}
/* nghttp2 guarantees that namelen > 0, and :status was already
received, and this is not pseudo-header field . */
/* convert to a HTTP1-style header */
Curl_add_buffer(stream->header_recvbuf, name, namelen);
Curl_add_buffer(stream->header_recvbuf, ":", 1);
Curl_add_buffer(stream->header_recvbuf, value, valuelen);
Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
data_s->state.drain++;
Curl_expire(data_s, 1);
/* convert to a HTTP1-style header */
Curl_add_buffer(stream->header_recvbuf, name, namelen);
Curl_add_buffer(stream->header_recvbuf, ":", 1);
Curl_add_buffer(stream->header_recvbuf, value, valuelen);
Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
data_s->state.drain++;
Curl_expire(data_s, 1);
DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n",
namelen, name, valuelen, value));
}
DEBUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
value));
return 0; /* 0 is successful */
}
@ -1257,20 +1197,6 @@ CURLcode Curl_http2_switched(struct connectdata *conn,
conn->recv[FIRSTSOCKET] = http2_recv;
conn->send[FIRSTSOCKET] = http2_send;
rv = (int) ((Curl_send*)httpc->send_underlying)
(conn, FIRSTSOCKET,
NGHTTP2_CLIENT_CONNECTION_PREFACE,
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN,
&result);
if(result)
/* TODO: This may get CURLE_AGAIN */
return result;
if(rv != 24) {
failf(data, "Only sent partial HTTP2 packet");
return CURLE_SEND_ERROR;
}
if(conn->data->req.upgr101 == UPGR101_RECEIVED) {
/* stream 1 is opened implicitly on upgrade */
stream->stream_id = 1;