]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw/rest: add 'zero' rest api 50507/head
authorCasey Bodley <cbodley@redhat.com>
Mon, 13 Mar 2023 19:54:41 +0000 (15:54 -0400)
committerCasey Bodley <cbodley@redhat.com>
Tue, 14 Mar 2023 20:51:02 +0000 (16:51 -0400)
when 'zero' is included in rgw_enable_apis, a /zero endpoint is added.
all paths under /zero refer to a single resource that supports the
following operations:

PUT requests discard the request body and update the content length
GET requests return a response body full of zeroes
HEAD requests return the current content length
DELETE requests reset the content length to 0

none of these operations require any authentication or i/o, so can be
used to benchmark the http frontend in isolation

Signed-off-by: Casey Bodley <cbodley@redhat.com>
src/rgw/CMakeLists.txt
src/rgw/rgw_appmain.cc
src/rgw/rgw_rest_zero.cc [new file with mode: 0644]
src/rgw/rgw_rest_zero.h [new file with mode: 0644]

index 34a346281850f49caf4d6f089bb1ccac8bf00f2c..a1f4825ae5476021267f8c14796f680f5826eacb 100644 (file)
@@ -112,6 +112,7 @@ set(librgw_common_srcs
   rgw_rest_role.cc
   rgw_rest_s3.cc
   rgw_rest_pubsub.cc
+  rgw_rest_zero.cc
   rgw_s3select.cc
   rgw_role.cc
   rgw_sal.cc
index 64c3258a54b38f713293c3d3670fe9b55ae1dc90..9409d2b14663d6f77bf074c362d3a03e02de3b2c 100644 (file)
@@ -42,6 +42,7 @@
 #include "rgw_rest_config.h"
 #include "rgw_rest_realm.h"
 #include "rgw_rest_ratelimit.h"
+#include "rgw_rest_zero.h"
 #include "rgw_swift_auth.h"
 #include "rgw_log.h"
 #include "rgw_lib.h"
@@ -327,6 +328,10 @@ void rgw::AppMain::cond_init_apis()
       env.driver->register_admin_apis(admin_resource);
       rest.register_resource(g_conf()->rgw_admin_entry, admin_resource);
     }
+
+    if (apis_map.count("zero")) {
+      rest.register_resource("zero", new rgw::RESTMgr_Zero());
+    }
   } /* have_http_frontend */
 } /* init_apis */
 
diff --git a/src/rgw/rgw_rest_zero.cc b/src/rgw/rgw_rest_zero.cc
new file mode 100644 (file)
index 0000000..5a245ee
--- /dev/null
@@ -0,0 +1,198 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+/*
+ * Ceph - scalable distributed file system
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include "rgw_rest_zero.h"
+
+#include <mutex>
+#include <vector>
+#include "common/strtol.h"
+
+namespace rgw {
+
+// all paths refer to a single resource that contains only a size
+class ZeroResource {
+ public:
+  std::mutex mutex;
+  std::size_t size = 0;
+};
+
+// base op
+class ZeroOp : public RGWOp {
+ protected:
+  ZeroResource* const resource;
+  const char* response_content_type = nullptr;
+  int64_t response_content_length = NO_CONTENT_LENGTH;
+ public:
+  explicit ZeroOp(ZeroResource* resource) : resource(resource) {}
+  int verify_permission(optional_yield y) override { return 0; }
+  void send_response() override;
+};
+
+void ZeroOp::send_response()
+{
+  if (op_ret) {
+    set_req_state_err(s, op_ret);
+  }
+  dump_errno(s);
+  end_header(s, this, response_content_type, response_content_length);
+}
+
+
+// DELETE op resets resource size to 0
+class ZeroDeleteOp : public ZeroOp {
+ public:
+  explicit ZeroDeleteOp(ZeroResource* resource) : ZeroOp(resource) {}
+  const char* name() const override { return "zero_delete"; }
+  void execute(optional_yield y) override;
+};
+
+void ZeroDeleteOp::execute(optional_yield y)
+{
+  auto lock = std::scoped_lock(resource->mutex);
+  resource->size = 0;
+}
+
+
+// GET op returns a request body of all zeroes
+class ZeroGetOp : public ZeroOp {
+ public:
+  explicit ZeroGetOp(ZeroResource* resource) : ZeroOp(resource) {}
+  const char* name() const override { return "zero_get"; }
+  void execute(optional_yield y) override;
+  void send_response() override;
+};
+
+void ZeroGetOp::execute(optional_yield y)
+{
+  response_content_type = "application/octet-stream";
+
+  auto lock = std::scoped_lock(resource->mutex);
+  response_content_length = resource->size;
+}
+
+void ZeroGetOp::send_response()
+{
+  // send the response header
+  ZeroOp::send_response();
+
+  // write zeroes for the entire response body
+  size_t remaining = static_cast<size_t>(response_content_length);
+  const size_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
+  std::vector<char> zeroes;
+  zeroes.resize(std::min(remaining, chunk_size), '\0');
+
+  try {
+    while (remaining) {
+      const size_t count = std::min(zeroes.size(), remaining);
+      const int bytes = dump_body(s, zeroes.data(), count);
+      remaining -= bytes;
+    }
+  } catch (const std::exception& e) {
+    ldpp_dout(this, 0) << "recv_body failed with " << e.what() << dendl;
+    op_ret = -EIO;
+    return;
+  }
+}
+
+
+// HEAD op returns the current content length
+class ZeroHeadOp : public ZeroOp {
+ public:
+  explicit ZeroHeadOp(ZeroResource* resource) : ZeroOp(resource) {}
+  const char* name() const override { return "zero_head"; }
+  void execute(optional_yield y) override;
+};
+
+void ZeroHeadOp::execute(optional_yield y)
+{
+  response_content_type = "application/octet-stream";
+
+  auto lock = std::scoped_lock(resource->mutex);
+  response_content_length = resource->size;
+}
+
+
+// PUT op discards the entire request body then updates the content length
+class ZeroPutOp : public ZeroOp {
+ public:
+  explicit ZeroPutOp(ZeroResource* resource) : ZeroOp(resource) {}
+  const char* name() const override { return "zero_put"; }
+  void execute(optional_yield y) override;
+};
+
+void ZeroPutOp::execute(optional_yield y)
+{
+  if (!s->length) {
+    ldpp_dout(this, 0) << "missing content length" << dendl;
+    op_ret = -ERR_LENGTH_REQUIRED;
+    return;
+  }
+  const auto content_length = ceph::parse<size_t>(s->length);
+  if (!content_length) {
+    ldpp_dout(this, 0) << "failed to parse content length \""
+        << s->length << '"' << dendl;
+    op_ret = -EINVAL;
+    return;
+  }
+
+  // read and discard the entire request body
+  size_t remaining = *content_length;
+  const size_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
+  std::vector<char> buffer;
+  buffer.resize(std::min(remaining, chunk_size));
+
+  try {
+    while (remaining) {
+      const size_t count = std::min(buffer.size(), remaining);
+      const int bytes = recv_body(s, buffer.data(), count);
+      remaining -= bytes;
+    }
+  } catch (const std::exception& e) {
+    ldpp_dout(this, 0) << "recv_body failed with " << e.what() << dendl;
+    op_ret = -EIO;
+    return;
+  }
+
+  // on success, update the resource size
+  auto lock = std::scoped_lock(resource->mutex);
+  resource->size = *content_length;
+}
+
+
+class ZeroHandler : public RGWHandler_REST {
+  ZeroResource* const resource;
+ public:
+  explicit ZeroHandler(ZeroResource* resource) : resource(resource) {}
+
+  int init_permissions(RGWOp*, optional_yield) override { return 0; }
+  int read_permissions(RGWOp*, optional_yield) override { return 0; }
+  int authorize(const DoutPrefixProvider* dpp, optional_yield y) override { return 0; }
+  int postauth_init(optional_yield y) override { return 0; }
+
+  // op factory functions
+  RGWOp* op_delete() override { return new ZeroDeleteOp(resource); }
+  RGWOp* op_get() override { return new ZeroGetOp(resource); }
+  RGWOp* op_head() override { return new ZeroHeadOp(resource); }
+  RGWOp* op_put() override { return new ZeroPutOp(resource); }
+};
+
+RESTMgr_Zero::RESTMgr_Zero() : resource(std::make_unique<ZeroResource>()) {}
+
+RGWHandler_REST* RESTMgr_Zero::get_handler(sal::Driver* driver, req_state* s,
+                                           const auth::StrategyRegistry& auth,
+                                           const std::string& prefix)
+{
+  return new ZeroHandler(resource.get());
+}
+
+} // namespace rgw
diff --git a/src/rgw/rgw_rest_zero.h b/src/rgw/rgw_rest_zero.h
new file mode 100644 (file)
index 0000000..bd31765
--- /dev/null
@@ -0,0 +1,34 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+/*
+ * Ceph - scalable distributed file system
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#pragma once
+
+#include <memory>
+#include "rgw_rest.h"
+
+namespace rgw {
+
+class ZeroResource;
+
+// a rest endpoint that's only useful for benchmarking the http frontend.
+// requests are not authenticated, and do no reads/writes to the backend
+class RESTMgr_Zero : public RGWRESTMgr {
+  std::unique_ptr<ZeroResource> resource;
+ public:
+  RESTMgr_Zero();
+  RGWHandler_REST* get_handler(sal::Driver* driver, req_state* s,
+                               const auth::StrategyRegistry& auth,
+                               const std::string& prefix) override;
+};
+
+} // namespace rgw