From 92c2a4c053f75bbfe8434379dbdd6acd714a2252 Mon Sep 17 00:00:00 2001
From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
Date: Wed, 17 Feb 2016 21:36:59 +0900
Subject: http2: Add handling stream level error

Previously, when a stream was closed with other than NGHTTP2_NO_ERROR
by RST_STREAM, underlying TCP connection was dropped.  This is
undesirable since there may be other streams multiplexed and they are
very much fine.  This change introduce new error code
CURLE_HTTP2_STREAM, which indicates stream error that only affects the
relevant stream, and connection should be kept open.  The existing
CURLE_HTTP2 means connection error in general.

Ref: https://github.com/curl/curl/issues/659
Ref: https://github.com/curl/curl/pull/663
---
 docs/libcurl/libcurl-errors.3    | 2 ++
 docs/libcurl/symbols-in-versions | 1 +
 include/curl/curl.h              | 2 ++
 lib/http2.c                      | 9 ++++++++-
 lib/multi.c                      | 3 ++-
 lib/strerror.c                   | 3 +++
 packages/OS400/curl.inc.in       | 2 ++
 7 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/docs/libcurl/libcurl-errors.3 b/docs/libcurl/libcurl-errors.3
index 5d525382a..0a21a7589 100644
--- a/docs/libcurl/libcurl-errors.3
+++ b/docs/libcurl/libcurl-errors.3
@@ -251,6 +251,8 @@ available, the session will be queued. (added in 7.30.0)
 Failed to match the pinned key specified with \fICURLOPT_PINNEDPUBLICKEY(3)\fP.
 .IP "CURLE_SSL_INVALIDCERTSTATUS (91)"
 Status returned failure when asked with \fICURLOPT_SSL_VERIFYSTATUS(3)\fP.
+.IP "CURLE_HTTP2_STREAM (92)"
+Stream error in the HTTP/2 framing layer.
 .IP "CURLE_OBSOLETE*"
 These error codes will never be returned. They were used in an old libcurl
 version and are currently unused.
diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions
index 3e64039a4..524d4ff62 100644
--- a/docs/libcurl/symbols-in-versions
+++ b/docs/libcurl/symbols-in-versions
@@ -75,6 +75,7 @@ CURLE_FTP_WRITE_ERROR           7.1           7.17.0
 CURLE_FUNCTION_NOT_FOUND        7.1
 CURLE_GOT_NOTHING               7.9.1
 CURLE_HTTP2                     7.38.0
+CURLE_HTTP2_STREAM              7.49.0
 CURLE_HTTP_NOT_FOUND            7.1
 CURLE_HTTP_PORT_FAILED          7.3           7.12.0
 CURLE_HTTP_POST_ERROR           7.1
diff --git a/include/curl/curl.h b/include/curl/curl.h
index a8697bb2a..6d9710117 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -537,6 +537,8 @@ typedef enum {
   CURLE_SSL_PINNEDPUBKEYNOTMATCH, /* 90 - specified pinned public key did not
                                      match */
   CURLE_SSL_INVALIDCERTSTATUS,   /* 91 - invalid certificate status */
+  CURLE_HTTP2_STREAM,            /* 92 - stream error in HTTP/2 framing layer
+                                    */
   CURL_LAST /* never use! */
 } CURLcode;
 
diff --git a/lib/http2.c b/lib/http2.c
index 8f19ebaee..e15237e77 100644
--- a/lib/http2.c
+++ b/lib/http2.c
@@ -1059,7 +1059,7 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn,
   if(stream->error_code != NGHTTP2_NO_ERROR) {
     failf(data, "HTTP/2 stream %u was not closed cleanly: error_code = %d",
           stream->stream_id, stream->error_code);
-    *err = CURLE_HTTP2;
+    *err = CURLE_HTTP2_STREAM;
     return -1;
   }
 
@@ -1231,6 +1231,13 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
     *err = CURLE_AGAIN;
     return -1;
   }
+  else if(!nghttp2_session_want_read(httpc->h2) &&
+          !nghttp2_session_want_write(httpc->h2)) {
+    DEBUGF(infof(data,
+                 "http2_recv: nothing to do in this session\n"));
+    *err = CURLE_HTTP2;
+    return -1;
+  }
   else {
     char *inbuf;
     /* remember where to store incoming data for this stream and how big the
diff --git a/lib/multi.c b/lib/multi.c
index 8c69e37a5..b1c1f5396 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -1880,7 +1880,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
          * happened in the data connection.
          */
 
-        if(!(data->easy_conn->handler->flags & PROTOPT_DUAL))
+        if(!(data->easy_conn->handler->flags & PROTOPT_DUAL) &&
+           result != CURLE_HTTP2_STREAM)
           connclose(data->easy_conn, "Transfer returned error");
 
         Curl_posttransfer(data);
diff --git a/lib/strerror.c b/lib/strerror.c
index cf1a04781..ac616f2c8 100644
--- a/lib/strerror.c
+++ b/lib/strerror.c
@@ -305,6 +305,9 @@ curl_easy_strerror(CURLcode error)
   case CURLE_SSL_INVALIDCERTSTATUS:
     return "SSL server certificate status verification FAILED";
 
+  case CURLE_HTTP2_STREAM:
+    return "Stream error in the HTTP/2 framing layer";
+
     /* error codes not used by current libcurl */
   case CURLE_OBSOLETE20:
   case CURLE_OBSOLETE24:
diff --git a/packages/OS400/curl.inc.in b/packages/OS400/curl.inc.in
index 7c646868e..18d842993 100644
--- a/packages/OS400/curl.inc.in
+++ b/packages/OS400/curl.inc.in
@@ -534,6 +534,8 @@
      d                 c                   90
      d  CURLE_SSL_INVALIDCERTSTATUS...
      d                 c                   91
+     d  CURLE_HTTP2_STREAM...
+     d                 c                   92
       *
       /if not defined(CURL_NO_OLDIES)
      d  CURLE_URL_MALFORMAT_USER...
-- 
cgit v1.2.3