From: Casey Bodley Date: Mon, 13 Mar 2023 19:54:41 +0000 (-0400) Subject: rgw/rest: add 'zero' rest api X-Git-Tag: v19.0.0~1279^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=5e5657127c27f8d5b11140da5c6b5ea0b41ec8ff;p=ceph.git rgw/rest: add 'zero' rest api 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 --- diff --git a/src/rgw/CMakeLists.txt b/src/rgw/CMakeLists.txt index 34a346281850f..a1f4825ae5476 100644 --- a/src/rgw/CMakeLists.txt +++ b/src/rgw/CMakeLists.txt @@ -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 diff --git a/src/rgw/rgw_appmain.cc b/src/rgw/rgw_appmain.cc index 64c3258a54b38..9409d2b14663d 100644 --- a/src/rgw/rgw_appmain.cc +++ b/src/rgw/rgw_appmain.cc @@ -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 index 0000000000000..5a245eee0b277 --- /dev/null +++ b/src/rgw/rgw_rest_zero.cc @@ -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 +#include +#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(response_content_length); + const size_t chunk_size = s->cct->_conf->rgw_max_chunk_size; + std::vector 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(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 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()) {} + +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 index 0000000000000..bd3176525894d --- /dev/null +++ b/src/rgw/rgw_rest_zero.h @@ -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 +#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 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