mirror of
https://github.com/curl/curl.git
synced 2024-11-21 01:16:58 +08:00
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:
parent
077f12b0ae
commit
4ac6cc3ebd
@ -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)"
|
||||
|
132
lib/http2.c
132
lib/http2.c
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user