Blame SOURCES/nghttp2-1.31.1-CVE-2019-9511-and-CVE-2019-9513.patch

39f95e
From 4b7aefd8fd1612d455f2f128c09230335ed0cee6 Mon Sep 17 00:00:00 2001
39f95e
From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
39f95e
Date: Tue, 6 Aug 2019 20:48:50 +0900
39f95e
Subject: [PATCH 1/3] nghttpx: Fix request stall
39f95e
39f95e
Fix request stall if backend connection is reused and buffer is full.
39f95e
39f95e
Upstream-commit: db2f612a30d54aa152ce5530fa1d683738baa4d1
39f95e
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
39f95e
---
39f95e
 integration-tests/nghttpx_http1_test.go | 29 +++++++++++++++++++++++++
39f95e
 integration-tests/server_tester.go      |  4 +++-
39f95e
 src/shrpx_downstream.cc                 | 12 +++++++++-
39f95e
 src/shrpx_downstream.h                  |  4 ++++
39f95e
 src/shrpx_http_downstream_connection.cc | 16 +++++++++++++-
39f95e
 src/shrpx_https_upstream.cc             |  4 +---
39f95e
 6 files changed, 63 insertions(+), 6 deletions(-)
39f95e
39f95e
diff --git a/integration-tests/nghttpx_http1_test.go b/integration-tests/nghttpx_http1_test.go
39f95e
index a765333..3d41677 100644
39f95e
--- a/integration-tests/nghttpx_http1_test.go
39f95e
+++ b/integration-tests/nghttpx_http1_test.go
39f95e
@@ -625,6 +625,35 @@ func TestH1H1HTTPSRedirectPort(t *testing.T) {
39f95e
 	}
39f95e
 }
39f95e
 
39f95e
+// TestH1H1POSTRequests tests that server can handle 2 requests with
39f95e
+// request body.
39f95e
+func TestH1H1POSTRequests(t *testing.T) {
39f95e
+	st := newServerTester(nil, t, noopHandler)
39f95e
+	defer st.Close()
39f95e
+
39f95e
+	res, err := st.http1(requestParam{
39f95e
+		name: "TestH1H1POSTRequestsNo1",
39f95e
+		body: make([]byte, 1),
39f95e
+	})
39f95e
+	if err != nil {
39f95e
+		t.Fatalf("Error st.http1() = %v", err)
39f95e
+	}
39f95e
+	if got, want := res.status, 200; got != want {
39f95e
+		t.Errorf("res.status: %v; want %v", got, want)
39f95e
+	}
39f95e
+
39f95e
+	res, err = st.http1(requestParam{
39f95e
+		name: "TestH1H1POSTRequestsNo2",
39f95e
+		body: make([]byte, 65536),
39f95e
+	})
39f95e
+	if err != nil {
39f95e
+		t.Fatalf("Error st.http1() = %v", err)
39f95e
+	}
39f95e
+	if got, want := res.status, 200; got != want {
39f95e
+		t.Errorf("res.status: %v; want %v", got, want)
39f95e
+	}
39f95e
+}
39f95e
+
39f95e
 // // TestH1H2ConnectFailure tests that server handles the situation that
39f95e
 // // connection attempt to HTTP/2 backend failed.
39f95e
 // func TestH1H2ConnectFailure(t *testing.T) {
39f95e
diff --git a/integration-tests/server_tester.go b/integration-tests/server_tester.go
39f95e
index d145519..1156986 100644
39f95e
--- a/integration-tests/server_tester.go
39f95e
+++ b/integration-tests/server_tester.go
39f95e
@@ -662,7 +662,9 @@ func cloneHeader(h http.Header) http.Header {
39f95e
 	return h2
39f95e
 }
39f95e
 
39f95e
-func noopHandler(w http.ResponseWriter, r *http.Request) {}
39f95e
+func noopHandler(w http.ResponseWriter, r *http.Request) {
39f95e
+	ioutil.ReadAll(r.Body)
39f95e
+}
39f95e
 
39f95e
 type APIResponse struct {
39f95e
 	Status string                 `json:"status,omitempty"`
39f95e
diff --git a/src/shrpx_downstream.cc b/src/shrpx_downstream.cc
39f95e
index 360a9a9..48db65b 100644
39f95e
--- a/src/shrpx_downstream.cc
39f95e
+++ b/src/shrpx_downstream.cc
39f95e
@@ -144,7 +144,8 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool,
39f95e
       request_header_sent_(false),
39f95e
       accesslog_written_(false),
39f95e
       new_affinity_cookie_(false),
39f95e
-      blocked_request_data_eof_(false) {
39f95e
+      blocked_request_data_eof_(false),
39f95e
+      expect_100_continue_(false) {
39f95e
 
39f95e
   auto &timeoutconf = get_config()->http2.timeout;
39f95e
 
39f95e
@@ -807,6 +808,11 @@ void Downstream::inspect_http1_request() {
39f95e
       chunked_request_ = true;
39f95e
     }
39f95e
   }
39f95e
+
39f95e
+  auto expect = req_.fs.header(http2::HD_EXPECT);
39f95e
+  expect_100_continue_ =
39f95e
+      expect &&
39f95e
+      util::strieq(expect->value, StringRef::from_lit("100-continue"));
39f95e
 }
39f95e
 
39f95e
 void Downstream::inspect_http1_response() {
39f95e
@@ -1103,4 +1109,8 @@ bool Downstream::get_blocked_request_data_eof() const {
39f95e
   return blocked_request_data_eof_;
39f95e
 }
39f95e
 
39f95e
+bool Downstream::get_expect_100_continue() const {
39f95e
+  return expect_100_continue_;
39f95e
+}
39f95e
+
39f95e
 } // namespace shrpx
39f95e
diff --git a/src/shrpx_downstream.h b/src/shrpx_downstream.h
39f95e
index c81fcf6..b9a851f 100644
39f95e
--- a/src/shrpx_downstream.h
39f95e
+++ b/src/shrpx_downstream.h
39f95e
@@ -466,6 +466,8 @@ public:
39f95e
     EVENT_TIMEOUT = 0x2,
39f95e
   };
39f95e
 
39f95e
+  bool get_expect_100_continue() const;
39f95e
+
39f95e
   enum {
39f95e
     DISPATCH_NONE,
39f95e
     DISPATCH_PENDING,
39f95e
@@ -556,6 +558,8 @@ private:
39f95e
   // true if eof is received from client before sending header fields
39f95e
   // to backend.
39f95e
   bool blocked_request_data_eof_;
39f95e
+  // true if request contains "expect: 100-continue" header field.
39f95e
+  bool expect_100_continue_;
39f95e
 };
39f95e
 
39f95e
 } // namespace shrpx
39f95e
diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc
39f95e
index f50c0f4..85ca947 100644
39f95e
--- a/src/shrpx_http_downstream_connection.cc
39f95e
+++ b/src/shrpx_http_downstream_connection.cc
39f95e
@@ -698,7 +698,8 @@ int HttpDownstreamConnection::push_request_headers() {
39f95e
   // signal_write() when we received request body chunk, and it
39f95e
   // enables us to send headers and data in one writev system call.
39f95e
   if (connect_method || downstream_->get_blocked_request_buf()->rleft() ||
39f95e
-      (!req.http2_expect_body && req.fs.content_length == 0)) {
39f95e
+      (!req.http2_expect_body && req.fs.content_length == 0) ||
39f95e
+      downstream_->get_expect_100_continue()) {
39f95e
     signal_write();
39f95e
   }
39f95e
 
39f95e
@@ -1172,6 +1173,19 @@ int HttpDownstreamConnection::write_reuse_first() {
39f95e
 
39f95e
   reuse_first_write_done_ = true;
39f95e
 
39f95e
+  // upstream->resume_read() might be called in
39f95e
+  // write_tls()/write_clear(), but before blocked_request_buf_ is
39f95e
+  // reset.  So upstream read might still be blocked.  Let's do it
39f95e
+  // again here.
39f95e
+  auto input = downstream_->get_request_buf();
39f95e
+  if (input->rleft() == 0) {
39f95e
+    auto upstream = downstream_->get_upstream();
39f95e
+    auto &req = downstream_->request();
39f95e
+
39f95e
+    upstream->resume_read(SHRPX_NO_BUFFER, downstream_,
39f95e
+                          req.unconsumed_body_length);
39f95e
+  }
39f95e
+
39f95e
   return 0;
39f95e
 }
39f95e
 
39f95e
diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc
39f95e
index 452ec90..96ca2cd 100644
39f95e
--- a/src/shrpx_https_upstream.cc
39f95e
+++ b/src/shrpx_https_upstream.cc
39f95e
@@ -467,9 +467,7 @@ int htp_hdrs_completecb(http_parser *htp) {
39f95e
     // and let them decide whether responds with 100 Continue or not.
39f95e
     // For alternative mode, we have no backend, so just send 100
39f95e
     // Continue here to make the client happy.
39f95e
-    auto expect = req.fs.header(http2::HD_EXPECT);
39f95e
-    if (expect &&
39f95e
-        util::strieq(expect->value, StringRef::from_lit("100-continue"))) {
39f95e
+    if (downstream->get_expect_100_continue()) {
39f95e
       auto output = downstream->get_response_buf();
39f95e
       constexpr auto res = StringRef::from_lit("HTTP/1.1 100 Continue\r\n\r\n");
39f95e
       output->append(res);
39f95e
-- 
39f95e
2.20.1
39f95e
39f95e
39f95e
From 589a98eba0b3c7a4dbb2262c60b609cac2b1f838 Mon Sep 17 00:00:00 2001
39f95e
From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
39f95e
Date: Tue, 25 Jun 2019 22:33:35 +0900
39f95e
Subject: [PATCH 2/3] Add nghttp2_option_set_max_outbound_ack
39f95e
39f95e
Upstream-commit: a76d0723b5f52902139ff453e0ec840673e86e75
39f95e
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
39f95e
---
39f95e
 doc/Makefile.am                |  1 +
39f95e
 lib/includes/nghttp2/nghttp2.h | 11 +++++++++++
39f95e
 lib/nghttp2_option.c           |  5 +++++
39f95e
 lib/nghttp2_option.h           |  5 +++++
39f95e
 lib/nghttp2_session.c          |  9 +++++++--
39f95e
 lib/nghttp2_session.h          |  8 ++++++--
39f95e
 tests/nghttp2_session_test.c   |  4 ++--
39f95e
 7 files changed, 37 insertions(+), 6 deletions(-)
39f95e
39f95e
diff --git a/doc/Makefile.am b/doc/Makefile.am
39f95e
index 07cd34e..66e5ba3 100644
39f95e
--- a/doc/Makefile.am
39f95e
+++ b/doc/Makefile.am
39f95e
@@ -67,6 +67,7 @@ APIDOCS= \
39f95e
 	nghttp2_option_set_no_recv_client_magic.rst \
39f95e
 	nghttp2_option_set_peer_max_concurrent_streams.rst \
39f95e
 	nghttp2_option_set_user_recv_extension_type.rst \
39f95e
+	nghttp2_option_set_max_outbound_ack.rst \
39f95e
 	nghttp2_pack_settings_payload.rst \
39f95e
 	nghttp2_priority_spec_check_default.rst \
39f95e
 	nghttp2_priority_spec_default_init.rst \
39f95e
diff --git a/lib/includes/nghttp2/nghttp2.h b/lib/includes/nghttp2/nghttp2.h
39f95e
index 14f8950..137a675 100644
39f95e
--- a/lib/includes/nghttp2/nghttp2.h
39f95e
+++ b/lib/includes/nghttp2/nghttp2.h
39f95e
@@ -2637,6 +2637,17 @@ nghttp2_option_set_max_deflate_dynamic_table_size(nghttp2_option *option,
39f95e
 NGHTTP2_EXTERN void nghttp2_option_set_no_closed_streams(nghttp2_option *option,
39f95e
                                                          int val);
39f95e
 
39f95e
+/**
39f95e
+ * @function
39f95e
+ *
39f95e
+ * This function sets the maximum number of outgoing SETTINGS ACK and
39f95e
+ * PING ACK frames retained in :type:`nghttp2_session` object.  If
39f95e
+ * more than those frames are retained, the peer is considered to be
39f95e
+ * misbehaving and session will be closed.  The default value is 1000.
39f95e
+ */
39f95e
+NGHTTP2_EXTERN void nghttp2_option_set_max_outbound_ack(nghttp2_option *option,
39f95e
+                                                        size_t val);
39f95e
+
39f95e
 /**
39f95e
  * @function
39f95e
  *
39f95e
diff --git a/lib/nghttp2_option.c b/lib/nghttp2_option.c
39f95e
index aec5dcf..ae22493 100644
39f95e
--- a/lib/nghttp2_option.c
39f95e
+++ b/lib/nghttp2_option.c
39f95e
@@ -116,3 +116,8 @@ void nghttp2_option_set_no_closed_streams(nghttp2_option *option, int val) {
39f95e
   option->opt_set_mask |= NGHTTP2_OPT_NO_CLOSED_STREAMS;
39f95e
   option->no_closed_streams = val;
39f95e
 }
39f95e
+
39f95e
+void nghttp2_option_set_max_outbound_ack(nghttp2_option *option, size_t val) {
39f95e
+  option->opt_set_mask |= NGHTTP2_OPT_MAX_OUTBOUND_ACK;
39f95e
+  option->max_outbound_ack = val;
39f95e
+}
39f95e
diff --git a/lib/nghttp2_option.h b/lib/nghttp2_option.h
39f95e
index c743e33..86d31f7 100644
39f95e
--- a/lib/nghttp2_option.h
39f95e
+++ b/lib/nghttp2_option.h
39f95e
@@ -66,6 +66,7 @@ typedef enum {
39f95e
   NGHTTP2_OPT_MAX_SEND_HEADER_BLOCK_LENGTH = 1 << 8,
39f95e
   NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE = 1 << 9,
39f95e
   NGHTTP2_OPT_NO_CLOSED_STREAMS = 1 << 10,
39f95e
+  NGHTTP2_OPT_MAX_OUTBOUND_ACK = 1 << 11,
39f95e
 } nghttp2_option_flag;
39f95e
 
39f95e
 /**
39f95e
@@ -80,6 +81,10 @@ struct nghttp2_option {
39f95e
    * NGHTTP2_OPT_MAX_DEFLATE_DYNAMIC_TABLE_SIZE
39f95e
    */
39f95e
   size_t max_deflate_dynamic_table_size;
39f95e
+  /**
39f95e
+   * NGHTTP2_OPT_MAX_OUTBOUND_ACK
39f95e
+   */
39f95e
+  size_t max_outbound_ack;
39f95e
   /**
39f95e
    * Bitwise OR of nghttp2_option_flag to determine that which fields
39f95e
    * are specified.
39f95e
diff --git a/lib/nghttp2_session.c b/lib/nghttp2_session.c
39f95e
index c58f059..8628cc7 100644
39f95e
--- a/lib/nghttp2_session.c
39f95e
+++ b/lib/nghttp2_session.c
39f95e
@@ -457,6 +457,7 @@ static int session_new(nghttp2_session **session_ptr,
39f95e
   (*session_ptr)->remote_settings.max_concurrent_streams = 100;
39f95e
 
39f95e
   (*session_ptr)->max_send_header_block_length = NGHTTP2_MAX_HEADERSLEN;
39f95e
+  (*session_ptr)->max_outbound_ack = NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM;
39f95e
 
39f95e
   if (option) {
39f95e
     if ((option->opt_set_mask & NGHTTP2_OPT_NO_AUTO_WINDOW_UPDATE) &&
39f95e
@@ -516,6 +517,10 @@ static int session_new(nghttp2_session **session_ptr,
39f95e
         option->no_closed_streams) {
39f95e
       (*session_ptr)->opt_flags |= NGHTTP2_OPTMASK_NO_CLOSED_STREAMS;
39f95e
     }
39f95e
+
39f95e
+    if (option->opt_set_mask & NGHTTP2_OPT_MAX_OUTBOUND_ACK) {
39f95e
+      (*session_ptr)->max_outbound_ack = option->max_outbound_ack;
39f95e
+    }
39f95e
   }
39f95e
 
39f95e
   rv = nghttp2_hd_deflate_init2(&(*session_ptr)->hd_deflater,
39f95e
@@ -6831,7 +6836,7 @@ int nghttp2_session_add_ping(nghttp2_session *session, uint8_t flags,
39f95e
   mem = &session->mem;
39f95e
 
39f95e
   if ((flags & NGHTTP2_FLAG_ACK) &&
39f95e
-      session->obq_flood_counter_ >= NGHTTP2_MAX_OBQ_FLOOD_ITEM) {
39f95e
+      session->obq_flood_counter_ >= session->max_outbound_ack) {
39f95e
     return NGHTTP2_ERR_FLOODED;
39f95e
   }
39f95e
 
39f95e
@@ -6976,7 +6981,7 @@ int nghttp2_session_add_settings(nghttp2_session *session, uint8_t flags,
39f95e
       return NGHTTP2_ERR_INVALID_ARGUMENT;
39f95e
     }
39f95e
 
39f95e
-    if (session->obq_flood_counter_ >= NGHTTP2_MAX_OBQ_FLOOD_ITEM) {
39f95e
+    if (session->obq_flood_counter_ >= session->max_outbound_ack) {
39f95e
       return NGHTTP2_ERR_FLOODED;
39f95e
     }
39f95e
   }
39f95e
diff --git a/lib/nghttp2_session.h b/lib/nghttp2_session.h
39f95e
index c7cb27d..d9e2846 100644
39f95e
--- a/lib/nghttp2_session.h
39f95e
+++ b/lib/nghttp2_session.h
39f95e
@@ -97,7 +97,7 @@ typedef struct {
39f95e
    response frames are stacked up, which leads to memory exhaustion.
39f95e
    The value selected here is arbitrary, but safe value and if we have
39f95e
    these frames in this number, it is considered suspicious. */
39f95e
-#define NGHTTP2_MAX_OBQ_FLOOD_ITEM 10000
39f95e
+#define NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM 1000
39f95e
 
39f95e
 /* The default value of maximum number of concurrent streams. */
39f95e
 #define NGHTTP2_DEFAULT_MAX_CONCURRENT_STREAMS 0xffffffffu
39f95e
@@ -260,8 +260,12 @@ struct nghttp2_session {
39f95e
   size_t num_idle_streams;
39f95e
   /* The number of bytes allocated for nvbuf */
39f95e
   size_t nvbuflen;
39f95e
-  /* Counter for detecting flooding in outbound queue */
39f95e
+  /* Counter for detecting flooding in outbound queue.  If it exceeds
39f95e
+     max_outbound_ack, session will be closed. */
39f95e
   size_t obq_flood_counter_;
39f95e
+  /* The maximum number of outgoing SETTINGS ACK and PING ACK in
39f95e
+     outbound queue. */
39f95e
+  size_t max_outbound_ack;
39f95e
   /* The maximum length of header block to send.  Calculated by the
39f95e
      same way as nghttp2_hd_deflate_bound() does. */
39f95e
   size_t max_send_header_block_length;
39f95e
diff --git a/tests/nghttp2_session_test.c b/tests/nghttp2_session_test.c
39f95e
index 783b0ed..debec59 100644
39f95e
--- a/tests/nghttp2_session_test.c
39f95e
+++ b/tests/nghttp2_session_test.c
39f95e
@@ -9894,7 +9894,7 @@ void test_nghttp2_session_flooding(void) {
39f95e
 
39f95e
   buf = &bufs.head->buf;
39f95e
 
39f95e
-  for (i = 0; i < NGHTTP2_MAX_OBQ_FLOOD_ITEM; ++i) {
39f95e
+  for (i = 0; i < NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM; ++i) {
39f95e
     CU_ASSERT(
39f95e
         (ssize_t)nghttp2_buf_len(buf) ==
39f95e
         nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)));
39f95e
@@ -9916,7 +9916,7 @@ void test_nghttp2_session_flooding(void) {
39f95e
 
39f95e
   buf = &bufs.head->buf;
39f95e
 
39f95e
-  for (i = 0; i < NGHTTP2_MAX_OBQ_FLOOD_ITEM; ++i) {
39f95e
+  for (i = 0; i < NGHTTP2_DEFAULT_MAX_OBQ_FLOOD_ITEM; ++i) {
39f95e
     CU_ASSERT(
39f95e
         (ssize_t)nghttp2_buf_len(buf) ==
39f95e
         nghttp2_session_mem_recv(session, buf->pos, nghttp2_buf_len(buf)));
39f95e
-- 
39f95e
2.20.1
39f95e
39f95e
39f95e
From e32b3e4c9df4abb83ca1c1c41901fadbae44699b Mon Sep 17 00:00:00 2001
39f95e
From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
39f95e
Date: Tue, 25 Jun 2019 22:38:43 +0900
39f95e
Subject: [PATCH 3/3] Don't read too greedily
39f95e
39f95e
Upstream-commit: 83d362c6d21f76599b86e7b94cd1992288a1d43c
39f95e
Signed-off-by: Kamil Dudka <kdudka@redhat.com>
39f95e
---
39f95e
 src/HttpServer.cc           | 2 ++
39f95e
 src/shrpx_client_handler.cc | 9 +++++++--
39f95e
 2 files changed, 9 insertions(+), 2 deletions(-)
39f95e
39f95e
diff --git a/src/HttpServer.cc b/src/HttpServer.cc
39f95e
index b3e35ef..a75cee4 100644
39f95e
--- a/src/HttpServer.cc
39f95e
+++ b/src/HttpServer.cc
39f95e
@@ -650,6 +650,7 @@ int Http2Handler::read_clear() {
39f95e
       }
39f95e
       return -1;
39f95e
     }
39f95e
+    break;
39f95e
   }
39f95e
 
39f95e
   return write_(*this);
39f95e
@@ -775,6 +776,7 @@ int Http2Handler::read_tls() {
39f95e
       }
39f95e
       return -1;
39f95e
     }
39f95e
+    break;
39f95e
   }
39f95e
 
39f95e
 fin:
39f95e
diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc
39f95e
index 21430dd..fa1fc87 100644
39f95e
--- a/src/shrpx_client_handler.cc
39f95e
+++ b/src/shrpx_client_handler.cc
39f95e
@@ -111,6 +111,7 @@ void writecb(struct ev_loop *loop, ev_io *w, int revents) {
39f95e
 int ClientHandler::noop() { return 0; }
39f95e
 
39f95e
 int ClientHandler::read_clear() {
39f95e
+  auto should_break = false;
39f95e
   rb_.ensure_chunk();
39f95e
   for (;;) {
39f95e
     if (rb_.rleft() && on_read() != 0) {
39f95e
@@ -123,7 +124,7 @@ int ClientHandler::read_clear() {
39f95e
       return 0;
39f95e
     }
39f95e
 
39f95e
-    if (!ev_is_active(&conn_.rev)) {
39f95e
+    if (!ev_is_active(&conn_.rev) || should_break) {
39f95e
       return 0;
39f95e
     }
39f95e
 
39f95e
@@ -141,6 +142,7 @@ int ClientHandler::read_clear() {
39f95e
     }
39f95e
 
39f95e
     rb_.write(nread);
39f95e
+    should_break = true;
39f95e
   }
39f95e
 }
39f95e
 
39f95e
@@ -205,6 +207,8 @@ int ClientHandler::tls_handshake() {
39f95e
 }
39f95e
 
39f95e
 int ClientHandler::read_tls() {
39f95e
+  auto should_break = false;
39f95e
+
39f95e
   ERR_clear_error();
39f95e
 
39f95e
   rb_.ensure_chunk();
39f95e
@@ -221,7 +225,7 @@ int ClientHandler::read_tls() {
39f95e
       return 0;
39f95e
     }
39f95e
 
39f95e
-    if (!ev_is_active(&conn_.rev)) {
39f95e
+    if (!ev_is_active(&conn_.rev) || should_break) {
39f95e
       return 0;
39f95e
     }
39f95e
 
39f95e
@@ -239,6 +243,7 @@ int ClientHandler::read_tls() {
39f95e
     }
39f95e
 
39f95e
     rb_.write(nread);
39f95e
+    should_break = true;
39f95e
   }
39f95e
 }
39f95e
 
39f95e
-- 
39f95e
2.20.1
39f95e