http2: mark the connection for close on GOAWAY

... don't consider it an error!

Assisted-by: Jay Satiro
Reported-by: Łukasz Domeradzki
Fixes #2365
Closes #2375
This commit is contained in:
Daniel Stenberg 2018-03-10 23:48:43 +01:00
Родитель 7fe68c39b3
Коммит 8b498a875c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 5CC908FDB71E12C2
3 изменённых файлов: 26 добавлений и 21 удалений

Просмотреть файл

@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____| * \___|\___/|_| \_\_____|
* *
* Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
* *
* This software is licensed as described in the file COPYING, which * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -172,8 +172,6 @@ struct HTTP {
size_t pauselen; /* the number of bytes left in data */ size_t pauselen; /* the number of bytes left in data */
bool closed; /* TRUE on HTTP2 stream close */ bool closed; /* TRUE on HTTP2 stream close */
bool close_handled; /* TRUE if stream closure is handled by libcurl */ bool close_handled; /* TRUE if stream closure is handled by libcurl */
uint32_t error_code; /* HTTP/2 error code */
char *mem; /* points to a buffer in memory to store received data */ char *mem; /* points to a buffer in memory to store received data */
size_t len; /* size of the buffer 'mem' points to */ size_t len; /* size of the buffer 'mem' points to */
size_t memlen; /* size of data copied to mem */ size_t memlen; /* size of data copied to mem */
@ -226,6 +224,7 @@ struct http_conn {
/* list of settings that will be sent */ /* list of settings that will be sent */
nghttp2_settings_entry local_settings[3]; nghttp2_settings_entry local_settings[3];
size_t local_settings_num; size_t local_settings_num;
uint32_t error_code; /* HTTP/2 error code */
#else #else
int unused; /* prevent a compiler warning */ int unused; /* prevent a compiler warning */
#endif #endif

Просмотреть файл

@ -210,7 +210,6 @@ void Curl_http2_setup_req(struct Curl_easy *data)
http->status_code = -1; http->status_code = -1;
http->pausedata = NULL; http->pausedata = NULL;
http->pauselen = 0; http->pauselen = 0;
http->error_code = NGHTTP2_NO_ERROR;
http->closed = FALSE; http->closed = FALSE;
http->close_handled = FALSE; http->close_handled = FALSE;
http->mem = data->state.buffer; http->mem = data->state.buffer;
@ -223,6 +222,7 @@ void Curl_http2_setup_conn(struct connectdata *conn)
{ {
conn->proto.httpc.settings.max_concurrent_streams = conn->proto.httpc.settings.max_concurrent_streams =
DEFAULT_MAX_CONCURRENT_STREAMS; DEFAULT_MAX_CONCURRENT_STREAMS;
conn->proto.httpc.error_code = NGHTTP2_NO_ERROR;
} }
/* /*
@ -786,6 +786,7 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
(void)stream_id; (void)stream_id;
if(stream_id) { if(stream_id) {
struct http_conn *httpc;
/* get the stream from the hash based on Stream ID, stream ID zero is for /* get the stream from the hash based on Stream ID, stream ID zero is for
connection-oriented stuff */ connection-oriented stuff */
data_s = nghttp2_session_get_stream_user_data(session, stream_id); data_s = nghttp2_session_get_stream_user_data(session, stream_id);
@ -800,10 +801,11 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
if(!stream) if(!stream)
return NGHTTP2_ERR_CALLBACK_FAILURE; return NGHTTP2_ERR_CALLBACK_FAILURE;
stream->error_code = error_code;
stream->closed = TRUE; stream->closed = TRUE;
data_s->state.drain++; data_s->state.drain++;
conn->proto.httpc.drain_total++; httpc = &conn->proto.httpc;
httpc->drain_total++;
httpc->error_code = error_code;
/* remove the entry from the hash as the stream is now gone */ /* remove the entry from the hash as the stream is now gone */
nghttp2_session_set_stream_user_data(session, stream_id, 0); nghttp2_session_set_stream_user_data(session, stream_id, 0);
@ -1234,13 +1236,14 @@ static int h2_session_send(struct Curl_easy *data,
* This function returns 0 if it succeeds, or -1 and error code will * This function returns 0 if it succeeds, or -1 and error code will
* be assigned to *err. * be assigned to *err.
*/ */
static int h2_process_pending_input(struct Curl_easy *data, static int h2_process_pending_input(struct connectdata *conn,
struct http_conn *httpc, struct http_conn *httpc,
CURLcode *err) CURLcode *err)
{ {
ssize_t nread; ssize_t nread;
char *inbuf; char *inbuf;
ssize_t rv; ssize_t rv;
struct Curl_easy *data = conn->data;
nread = httpc->inbuflen - httpc->nread_inbuf; nread = httpc->inbuflen - httpc->nread_inbuf;
inbuf = httpc->inbuf + httpc->nread_inbuf; inbuf = httpc->inbuf + httpc->nread_inbuf;
@ -1278,7 +1281,13 @@ static int h2_process_pending_input(struct Curl_easy *data,
if(should_close_session(httpc)) { if(should_close_session(httpc)) {
H2BUGF(infof(data, H2BUGF(infof(data,
"h2_process_pending_input: nothing to do in this session\n")); "h2_process_pending_input: nothing to do in this session\n"));
if(httpc->error_code)
*err = CURLE_HTTP2; *err = CURLE_HTTP2;
else {
/* not an error per se, but should still close the connection */
connclose(conn, "GOAWAY received");
*err = CURLE_OK;
}
return -1; return -1;
} }
@ -1309,7 +1318,7 @@ CURLcode Curl_http2_done_sending(struct connectdata *conn)
that it can signal EOF to nghttp2 */ that it can signal EOF to nghttp2 */
(void)nghttp2_session_resume_data(h2, stream->stream_id); (void)nghttp2_session_resume_data(h2, stream->stream_id);
(void)h2_process_pending_input(conn->data, httpc, &result); (void)h2_process_pending_input(conn, httpc, &result);
} }
} }
return result; return result;
@ -1333,7 +1342,7 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn,
data->state.drain = 0; data->state.drain = 0;
if(httpc->pause_stream_id == 0) { if(httpc->pause_stream_id == 0) {
if(h2_process_pending_input(data, httpc, err) != 0) { if(h2_process_pending_input(conn, httpc, err) != 0) {
return -1; return -1;
} }
} }
@ -1342,10 +1351,10 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn,
/* Reset to FALSE to prevent infinite loop in readwrite_data function. */ /* Reset to FALSE to prevent infinite loop in readwrite_data function. */
stream->closed = FALSE; stream->closed = FALSE;
if(stream->error_code != NGHTTP2_NO_ERROR) { if(httpc->error_code != NGHTTP2_NO_ERROR) {
failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %d)", failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %d)",
stream->stream_id, Curl_http2_strerror(stream->error_code), stream->stream_id, Curl_http2_strerror(httpc->error_code),
stream->error_code); httpc->error_code);
*err = CURLE_HTTP2_STREAM; *err = CURLE_HTTP2_STREAM;
return -1; return -1;
} }
@ -1493,7 +1502,7 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
/* We have paused nghttp2, but we have no pause data (see /* We have paused nghttp2, but we have no pause data (see
on_data_chunk_recv). */ on_data_chunk_recv). */
httpc->pause_stream_id = 0; httpc->pause_stream_id = 0;
if(h2_process_pending_input(data, httpc, &result) != 0) { if(h2_process_pending_input(conn, httpc, &result) != 0) {
*err = result; *err = result;
return -1; return -1;
} }
@ -1523,7 +1532,7 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
frames, then we have to call it again with 0-length data. frames, then we have to call it again with 0-length data.
Without this, on_stream_close callback will not be called, Without this, on_stream_close callback will not be called,
and stream could be hanged. */ and stream could be hanged. */
if(h2_process_pending_input(data, httpc, &result) != 0) { if(h2_process_pending_input(conn, httpc, &result) != 0) {
*err = result; *err = result;
return -1; return -1;
} }

Просмотреть файл

@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____| * \___|\___/|_| \_\_____|
* *
* Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
* *
* This software is licensed as described in the file COPYING, which * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -538,11 +538,8 @@ static CURLcode multi_done(struct connectdata **connp,
result = CURLE_ABORTED_BY_CALLBACK; result = CURLE_ABORTED_BY_CALLBACK;
} }
if(conn->send_pipe.size + conn->recv_pipe.size != 0 && if(conn->send_pipe.size || conn->recv_pipe.size) {
!data->set.reuse_forbid && /* Stop if pipeline is not empty . */
!conn->bits.close) {
/* Stop if pipeline is not empty and we do not have to close
connection. */
data->easy_conn = NULL; data->easy_conn = NULL;
DEBUGF(infof(data, "Connection still in use, no more multi_done now!\n")); DEBUGF(infof(data, "Connection still in use, no more multi_done now!\n"));
return CURLE_OK; return CURLE_OK;