]> granicus.if.org Git - curl/commitdiff
http2: mark the connection for close on GOAWAY
authorDaniel Stenberg <daniel@haxx.se>
Sat, 10 Mar 2018 22:48:43 +0000 (23:48 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 12 Mar 2018 07:07:42 +0000 (08:07 +0100)
... don't consider it an error!

Assisted-by: Jay Satiro
Reported-by: Ɓukasz Domeradzki
Fixes #2365
Closes #2375

lib/http.h
lib/http2.c
lib/multi.c

index d2781bc0f083016a61989bb633af307d457e6584..2ce44bbfc3377eb9f5a5c3e41d451f39be9154a4 100644 (file)
@@ -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
  * 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 */
   bool closed; /* TRUE on HTTP2 stream close */
   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 */
   size_t len;    /* size of the buffer 'mem' points to */
   size_t memlen; /* size of data copied to mem */
@@ -226,6 +224,7 @@ struct http_conn {
   /* list of settings that will be sent */
   nghttp2_settings_entry local_settings[3];
   size_t local_settings_num;
+  uint32_t error_code; /* HTTP/2 error code */
 #else
   int unused; /* prevent a compiler warning */
 #endif
index ec1b7cf91e393c6f3db143be76c184a0c7d25da1..b2c34e941047384d32f763c9652233546febdcb8 100644 (file)
@@ -210,7 +210,6 @@ void Curl_http2_setup_req(struct Curl_easy *data)
   http->status_code = -1;
   http->pausedata = NULL;
   http->pauselen = 0;
-  http->error_code = NGHTTP2_NO_ERROR;
   http->closed = FALSE;
   http->close_handled = FALSE;
   http->mem = data->state.buffer;
@@ -223,6 +222,7 @@ void Curl_http2_setup_conn(struct connectdata *conn)
 {
   conn->proto.httpc.settings.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;
 
   if(stream_id) {
+    struct http_conn *httpc;
     /* get the stream from the hash based on Stream ID, stream ID zero is for
        connection-oriented stuff */
     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)
       return NGHTTP2_ERR_CALLBACK_FAILURE;
 
-    stream->error_code = error_code;
     stream->closed = TRUE;
     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 */
     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
  * 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,
                                     CURLcode *err)
 {
   ssize_t nread;
   char *inbuf;
   ssize_t rv;
+  struct Curl_easy *data = conn->data;
 
   nread = httpc->inbuflen - 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)) {
     H2BUGF(infof(data,
                  "h2_process_pending_input: nothing to do in this session\n"));
-    *err = CURLE_HTTP2;
+    if(httpc->error_code)
+      *err = CURLE_HTTP2;
+    else {
+      /* not an error per se, but should still close the connection */
+      connclose(conn, "GOAWAY received");
+      *err = CURLE_OK;
+    }
     return -1;
   }
 
@@ -1309,7 +1318,7 @@ CURLcode Curl_http2_done_sending(struct connectdata *conn)
          that it can signal EOF to nghttp2 */
       (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;
@@ -1333,7 +1342,7 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn,
   data->state.drain = 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;
     }
   }
@@ -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. */
   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)",
-          stream->stream_id, Curl_http2_strerror(stream->error_code),
-          stream->error_code);
+          stream->stream_id, Curl_http2_strerror(httpc->error_code),
+          httpc->error_code);
     *err = CURLE_HTTP2_STREAM;
     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
          on_data_chunk_recv). */
       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;
         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.
          Without this, on_stream_close callback will not be called,
          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;
         return -1;
       }
index ca3a877eb4786990fe8e70b13978a9447c576856..98e5fca2ab903ab4b2d5b66ca37bfa3c3be70851 100644 (file)
@@ -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
  * 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;
   }
 
-  if(conn->send_pipe.size + conn->recv_pipe.size != 0 &&
-     !data->set.reuse_forbid &&
-     !conn->bits.close) {
-    /* Stop if pipeline is not empty and we do not have to close
-       connection. */
+  if(conn->send_pipe.size || conn->recv_pipe.size) {
+    /* Stop if pipeline is not empty . */
     data->easy_conn = NULL;
     DEBUGF(infof(data, "Connection still in use, no more multi_done now!\n"));
     return CURLE_OK;