]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd/migration/HttpClient: avoid reusing ssl_stream after shut down
authorIlya Dryomov <idryomov@gmail.com>
Fri, 6 Dec 2024 15:51:51 +0000 (16:51 +0100)
committerIlya Dryomov <idryomov@gmail.com>
Mon, 16 Dec 2024 08:49:40 +0000 (09:49 +0100)
ssl_stream objects can't be reused after shut down: despite
a successful reconnect and handshake, any attempt to read or write
fails with "end of stream" (beast.http:1) or "protocol is shutdown"
(asio.ssl:337690831) error respectively.  This doesn't appear to be
documented, but Beast and ASIO authors both mention that the stream
must be destroyed and recreated [1][2].

This was missed because the only integration test with a big enough
image used http instead of https.

[1] https://github.com/boostorg/beast/issues/821#issuecomment-338354949
[2] https://github.com/chriskohlhoff/asio/issues/804#issuecomment-872746894

Fixes: https://tracker.ceph.com/issues/69178
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
(cherry picked from commit 20885b11794ba80d5cddd178994865a83da7240f)

qa/suites/rbd/migration/6-prepare/qcow2-https.yaml [new file with mode: 0644]
src/librbd/migration/HttpClient.cc

diff --git a/qa/suites/rbd/migration/6-prepare/qcow2-https.yaml b/qa/suites/rbd/migration/6-prepare/qcow2-https.yaml
new file mode 100644 (file)
index 0000000..d2072c4
--- /dev/null
@@ -0,0 +1,8 @@
+tasks:
+  - exec:
+      client.0:
+        - mkdir /home/ubuntu/cephtest/migration
+        - qemu-img create -f qcow2 /home/ubuntu/cephtest/migration/empty.qcow2 1G
+        - echo '{"type":"qcow","stream":{"type":"http","url":"https://download.ceph.com/qa/ubuntu-12.04.qcow2"}}' | rbd migration prepare --import-only --source-spec-path - client.0.0
+        - rbd migration prepare --import-only --source-spec '{"type":"qcow","stream":{"type":"file","file_path":"/home/ubuntu/cephtest/migration/empty.qcow2"}}' client.0.1
+        - rbd migration prepare --import-only --source-spec '{"type":"qcow","stream":{"type":"file","file_path":"/home/ubuntu/cephtest/migration/empty.qcow2"}}' client.0.2
index 8815e23b60cffdbc1d5ca63ac012b23f3d24b615..da58bf88b8292d464e3264dff2b1189a2da14787 100644 (file)
@@ -187,6 +187,7 @@ protected:
   virtual void connect(boost::asio::ip::tcp::resolver::results_type results,
                        Context* on_finish) = 0;
   virtual void disconnect(Context* on_finish) = 0;
+  virtual void reset_stream() = 0;
 
   void close_socket() {
     auto cct = m_http_client->m_cct;
@@ -527,6 +528,7 @@ private:
       ceph_assert(next_state == STATE_RESET_CONNECTING);
       ceph_assert(on_finish == nullptr);
       shutdown_socket();
+      reset_stream();
       resolve_host(nullptr);
       return;
     } else if (current_state == STATE_RESET_CONNECTING) {
@@ -612,9 +614,12 @@ protected:
     on_finish->complete(0);
   }
 
+  void reset_stream() override {
+    // no-op -- tcp_stream object can be reused after shut down
+  }
+
 private:
   boost::beast::tcp_stream m_stream;
-
 };
 
 #undef dout_prefix
@@ -668,6 +673,17 @@ protected:
         shutdown(r, on_finish); }));
   }
 
+  void reset_stream() override {
+    auto http_client = this->m_http_client;
+    auto cct = http_client->m_cct;
+    ldout(cct, 15) << dendl;
+
+    // ssl_stream object can't be reused after shut down -- move-in
+    // a freshly constructed instance
+    m_stream = boost::beast::ssl_stream<boost::beast::tcp_stream>(
+      http_client->m_strand, http_client->m_ssl_context);
+  }
+
 private:
   boost::beast::ssl_stream<boost::beast::tcp_stream> m_stream;
   bool m_ssl_enabled = false;