.. confval:: rgw_restore_processor_period
These values can be tuned based upon your specific workload to further increase the aggressiveness of restore processing.
+
+Global CORS
+===========
+
+Configuration options that allows administrators to define a global CORS policy applied at the gateway level, affecting all buckets served by RGW.
+Previously, CORS (Cross-Origin Resource Sharing) support in RGW is limited to per-bucket configuration using the Get/PutBucketCors API, following the standard AWS S3 behavior.
+While this suffices for most use cases, it becomes a blocker when using browser-based tools that require interaction with gateway-wide resources, such as listing all buckets accessible to a user.
+These configuration options are necessary to enable CORS-based access for tools like S3 Browser, which need to list and create buckets across the gateway from a browser client.
+
+.. confval:: rgw_gcors_allow_origins
+.. confval:: rgw_gcors_allow_headers
+.. confval:: rgw_gcors_allow_methods
+.. confval:: rgw_gcors_expose_headers
--- /dev/null
+import requests
+from requests_aws4auth import AWS4Auth
+import json
+try:
+ import xmltodict
+except ModuleNotFoundError:
+ print("Module 'xmltodict' is not installed.")
+
+
+ACCESS_KEY = '0555b35654ad1656d804'
+SECRET_KEY = 'h7GhxuBLTrlhVUyxSPUKUV8r/2EI4ngqJxD7iBdBYLhwluN30JaT3Q=='
+REGION = 'us-east-1' # Replace with your bucket's region
+
+# 1. Initialize AWS4Auth for Signature Version 4
+# This generates the signature and handles the Authorization header automatically
+auth = AWS4Auth(ACCESS_KEY, SECRET_KEY, REGION, 's3')
+
+url = 'http://localhost:8000/'
+origin = "https://www.your-website.com"
+custom_headers = 'Content-Type,Authorization'
+methods = 'GET,PUT, DELETE'
+
+# 1. Step: Send the Preflight OPTIONS Request
+preflight_headers = {
+ "Host": "8.8.8.8",
+ 'Origin': origin,
+ 'Access-Control-Request-Method': methods,
+ 'Access-Control-Request-Headers': custom_headers
+}
+
+# 2. Step: Send the Actual GET Request
+actual_headers = {
+ "Host": "8.8.8.8",
+ 'Origin': origin,
+}
+
+def list_buckets():
+ print("Listing all buckets...")
+ url = 'http://localhost:8000/'
+ response = requests.get(url, headers=actual_headers, auth=auth)
+ print(f"Actual Response Status: {response.status_code}")
+ data_dict = xmltodict.parse(response.text, process_namespaces=False)
+ json_data = json.dumps(data_dict["ListAllMyBucketsResult"]["Buckets"], indent=4)
+ print(f"Buckets: {json_data}")
+
+def create_bucket(bucket_name):
+ print(f"Creating a bucket named {bucket_name}...")
+ url = f'http://localhost:8000/{bucket_name}'
+ response = requests.put(url, headers=actual_headers, auth=auth)
+ print(f"Actual Response Status: {response.status_code}")
+
+def delete_bucket(bucket_name):
+ print(f"Deleting a bucket named {bucket_name}...")
+ url = f'http://localhost:8000/{bucket_name}'
+ response = requests.delete(url, headers=actual_headers, auth=auth)
+ print(f"Actual Response Status: {response.status_code}")
+
+preflight_resp = requests.options(url, headers=preflight_headers)
+
+print(f"Preflight Status: {preflight_resp.status_code}")
+
+# Check if the server allows the custom headers
+allowed_headers = preflight_resp.headers.get('Access-Control-Allow-Headers', '')
+
+if preflight_resp.status_code in [200, 204] and custom_headers.lower() in allowed_headers.lower():
+ print(f"Preflight successful! Proceeding with {methods} request...")
+
+ list_buckets()
+
+ create_bucket('bkt')
+ list_buckets()
+
+ create_bucket('bkt2')
+ list_buckets()
+
+ delete_bucket('bkt')
+ list_buckets()
+
+ delete_bucket('bkt2')
+ list_buckets()
see_also:
- rgw_enable_usage_log
with_legacy: true
+- name: rgw_gcors_allow_origins
+ type: str
+ level: advanced
+ desc: When not empty, this value is returned as a response header Access-Control-Allow-Origins.
+ services:
+ - rgw
+- name: rgw_gcors_allow_headers
+ type: str
+ level: advanced
+ desc: When not empty, this value is returned as a response header Access-Control-Allow-Headers to preflight requests.
+ services:
+ - rgw
+- name: rgw_gcors_allow_methods
+ type: str
+ level: advanced
+ desc: When not empty, this value is returned as a response header Access-Control-Allow-Methods.
+ services:
+ - rgw
+- name: rgw_gcors_expose_headers
+ type: str
+ level: advanced
+ desc: When not empty, this value is returned as a response header Access-Control-Expose-Headers.
+ services:
+ - rgw
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/use_awaitable.hpp>
+#include "rgw_cors_s3.h"
+
extern "C" {
#include <liboath/oath.h>
}
ACCOUNT_LIST,
RESTORE_STATUS,
RESTORE_LIST,
+ GLOBAL_CORS_GET,
};
}
{ "account list", OPT::ACCOUNT_LIST },
{ "restore status", OPT::RESTORE_STATUS },
{ "restore list", OPT::RESTORE_LIST },
+ { "global-cors get", OPT::GLOBAL_CORS_GET},
};
static SimpleCmd::Aliases cmd_aliases = {
std::optional<std::string> restore_status_filter;
int show_restore_stats = false;
+ // global CORS settings
+ std::optional<std::string> gcors_allow_origins;
+ std::optional<std::string> gcors_allow_methods;
+ std::optional<std::string> gcors_allow_headers;
+ std::optional<std::string> gcors_expose_headers;
+
init_realm_param(cct.get(), realm_id, opt_realm_id, "rgw_realm_id");
init_realm_param(cct.get(), zonegroup_id, opt_zonegroup_id, "rgw_zonegroup_id");
init_realm_param(cct.get(), zone_id, opt_zone_id, "rgw_zone_id");
restore_status_filter = val;
} else if (ceph_argparse_binary_flag(args, i, &show_restore_stats, NULL, "--show-restore-stats", (char*)NULL)){
// do nothing
+ } else if (ceph_argparse_witharg(args, i, &val, "--allow-origin", (char*)NULL)) {
+ gcors_allow_origins = val;
+ } else if (ceph_argparse_witharg(args, i, &val, "--allow-methods", (char*)NULL)) {
+ gcors_allow_methods = val;
+ } else if (ceph_argparse_witharg(args, i, &val, "--allow-headers", (char*)NULL)) {
+ gcors_allow_headers = val;
+ } else if (ceph_argparse_witharg(args, i, &val, "--expose-headers", (char*)NULL)) {
+ gcors_expose_headers = val;
} else if (strncmp(*i, "-", 1) == 0) {
cerr << "ERROR: invalid flag " << *i << std::endl;
return EINVAL;
err_msg, stream_flusher, null_yield);
}
}
+ if (opt_cmd == OPT::GLOBAL_CORS_GET) {
+ string allow_origins, allow_headers, allow_methods, expose_headers;
+ ret = g_conf().get_val("rgw_gcors_allow_origins", &allow_origins);
+ if (ret < 0 || allow_origins.empty()) {
+ cerr << "ERROR in OPT::GLOBAL_CORS_GET, no rgw_gcors_allow_origins config found or empty, ret=" << ret << std::endl;
+ return -EINVAL;
+ }
+ ret = g_conf().get_val("rgw_gcors_allow_headers", &allow_headers);
+ if (ret < 0 || allow_headers.empty()) {
+ cerr << "ERROR in OPT::GLOBAL_CORS_GET, no rgw_gcors_allow_headers config found or empty, ret=" << ret << std::endl;
+ return -EINVAL;
+ }
+ ret = g_conf().get_val("rgw_gcors_allow_methods", &allow_methods);
+ if (ret < 0 || allow_methods.empty()) {
+ cerr << "ERROR in OPT::GLOBAL_CORS_GET, no rgw_gcors_allow_methods config found or empty, ret=" << ret << std::endl;
+ return -EINVAL;
+ }
+ ret = g_conf().get_val("rgw_gcors_expose_headers", &expose_headers);
+ std::optional<RGWCORSRule> optional_global_cors;
+ if (RGWCORSRule::create_rule(allow_origins.c_str(), allow_headers.c_str(),
+ expose_headers.c_str(), allow_methods.c_str(), optional_global_cors) < 0) {
+ cerr << "ERROR: couldn't create RGWCORSRule from rgw_gcors_allow_origins=" << allow_origins <<
+ ", rgw_gcors_allow_headers=" << allow_headers << ", rgw_gcors_allow_methods=" << allow_methods <<
+ ", rgw_gcors_expose_headers=" << expose_headers << std::endl;
+ return -EINVAL;
+ }
+
+ optional_global_cors->dump(formatter.get());
+ formatter->flush(cout);
+ }
return 0;
}
encode_json("AllowedOrigin", allowed_origins, f);
encode_json("AllowedHeader", allowed_hdrs, f);
encode_json("ExposeHeader", exposable_hdrs, f);
+ f->close_section();//CORSRule
}
void RGWCORSRule::erase_origin_if_present(string& origin, bool *rule_empty) {
return o;
}
+int RGWCORSRule::create_rule(const char *allow_origins, const char *allow_headers,
+ const char *expose_headers, const char* allowed_methods, std::optional<RGWCORSRule>& rule, const char *max_age)
+{
+ std::set<std::string> o, h;
+ std::list<std::string> e;
+ unsigned long a = CORS_MAX_AGE_INVALID;
+ const uint8_t flags = ("*"s == allowed_methods)? RGW_CORS_ALL:get_multi_cors_method_flags(allowed_methods);
+
+ int nr_invalid_names = 0;
+ auto add_host = [&nr_invalid_names, &o] (auto host) {
+ if (validate_name_string(host) == 0) {
+ o.emplace(std::string{host});
+ } else {
+ nr_invalid_names++;
+ }
+ };
+ for_each_substr(allow_origins, ";,= \t", add_host);
+ if (o.empty() || nr_invalid_names > 0) {
+ return -EINVAL;
+ }
+
+ if (allow_headers) {
+ int nr_invalid_headers = 0;
+ auto add_header = [&nr_invalid_headers, &h] (auto allow_header) {
+ if (validate_name_string(allow_header) == 0) {
+ h.emplace(std::string{allow_header});
+ } else {
+ nr_invalid_headers++;
+ }
+ };
+ for_each_substr(allow_headers, ";,= \t", add_header);
+ if (h.empty() || nr_invalid_headers > 0) {
+ return -EINVAL;
+ }
+ }
+
+ if (expose_headers) {
+ for_each_substr(expose_headers, ";,= \t",
+ [&e] (auto expose_header) {
+ e.emplace_back(std::string(expose_header));
+ });
+ }
+ if (max_age) {
+ char *end = NULL;
+ a = strtoul(max_age, &end, 10);
+ if (a == ULONG_MAX)
+ a = CORS_MAX_AGE_INVALID;
+ }
+
+ rule = RGWCORSRule(o, h, e, flags, a);
+ return 0;
+}
+
/*
* make attrs look-like-this
* does not convert underscores or dashes
std::string& get_id() { return id; }
uint32_t get_max_age() { return max_age; }
uint8_t get_allowed_methods() { return allowed_methods; }
+ bool has_origin(const std::string& s) const { return allowed_origins.contains(s); }
void encode(bufferlist& bl) const {
ENCODE_START(1, 1, bl);
DECODE_FINISH(bl);
}
static std::list<RGWCORSRule> generate_test_instances();
+ static int create_rule(const char *allow_origins, const char *allow_headers,
+ const char *expose_headers, const char* allowed_methods, std::optional<RGWCORSRule>& rule, const char *max_age="");
bool has_wildcard_origin();
bool is_origin_present(const char *o);
void format_exp_headers(std::string& s);
void erase_origin_if_present(std::string& origin, bool *rule_empty);
- void dump_origins();
+ void dump_origins();
void dump(Formatter *f) const;
bool is_header_allowed(const char *hdr, size_t len);
};
else if (strcmp(req_meth, "PUT") == 0) flags = RGW_CORS_PUT;
else if (strcmp(req_meth, "DELETE") == 0) flags = RGW_CORS_DELETE;
else if (strcmp(req_meth, "HEAD") == 0) flags = RGW_CORS_HEAD;
+ else if (strcmp(req_meth, "COPY") == 0) flags = RGW_CORS_COPY;
+
+ return flags;
+}
+
+static inline uint8_t get_multi_cors_method_flags(const char *req_meth) {
+ uint8_t flags = 0;
+ const std::string allowed_methods(req_meth);
+ auto apply_flag = [&flags] (std::string_view method) {
+ if (method == "GET") flags |= RGW_CORS_GET;
+ else if (method == "POST") flags |= RGW_CORS_POST;
+ else if (method == "PUT") flags |= RGW_CORS_PUT;
+ else if (method == "DELETE") flags |= RGW_CORS_DELETE;
+ else if (method == "HEAD") flags |= RGW_CORS_HEAD;
+ else if (method == "COPY") flags |= RGW_CORS_COPY;
+ };
+ for_each_substr(allowed_methods, ";,= \t", apply_flag);
return flags;
}
~RGWCORSConfiguration_SWIFT() {}
int create_update(const char *allow_origins, const char *allow_headers,
const char *expose_headers, const char *max_age) {
- std::set<std::string> o, h;
- std::list<std::string> e;
- unsigned long a = CORS_MAX_AGE_INVALID;
- uint8_t flags = RGW_CORS_ALL;
-
- int nr_invalid_names = 0;
- auto add_host = [&nr_invalid_names, &o] (auto host) {
- if (validate_name_string(host) == 0) {
- o.emplace(std::string{host});
- } else {
- nr_invalid_names++;
- }
- };
- for_each_substr(allow_origins, ";,= \t", add_host);
- if (o.empty() || nr_invalid_names > 0) {
+ std::optional<RGWCORSRule> optional_rule;
+ if (RGWCORSRule::create_rule(allow_origins, allow_headers, expose_headers, "*", optional_rule, max_age) < 0) {
return -EINVAL;
}
-
- if (allow_headers) {
- int nr_invalid_headers = 0;
- auto add_header = [&nr_invalid_headers, &h] (auto allow_header) {
- if (validate_name_string(allow_header) == 0) {
- h.emplace(std::string{allow_header});
- } else {
- nr_invalid_headers++;
- }
- };
- for_each_substr(allow_headers, ";,= \t", add_header);
- if (h.empty() || nr_invalid_headers > 0) {
- return -EINVAL;
- }
- }
-
- if (expose_headers) {
- for_each_substr(expose_headers, ";,= \t",
- [&e] (auto expose_header) {
- e.emplace_back(std::string(expose_header));
- });
- }
- if (max_age) {
- char *end = NULL;
- a = strtoul(max_age, &end, 10);
- if (a == ULONG_MAX)
- a = CORS_MAX_AGE_INVALID;
- }
-
- RGWCORSRule rule(o, h, e, flags, a);
- stack_rule(rule);
+ stack_rule(optional_rule.value());
return 0;
}
};
return ret;
}
s->bucket_exists = false;
+ if (s->op_type == RGW_OP_OPTIONS_CORS) {
+ ldpp_dout(dpp, 0) << "NOTICE: RGW_OP_OPTIONS_CORS shouldn't return -ERR_NO_SUCH_BUCKET in case we have a global CORS!" << dendl;
+ return 0;
+ }
return -ERR_NO_SUCH_BUCKET;
}
if (!rgw::sal::Object::empty(s->object.get())) {
return false;
}
- uint8_t flags = get_cors_method_flags(req_meth);
+ uint8_t flags = get_multi_cors_method_flags(req_meth);
if (rule->get_allowed_methods() & flags) {
ldpp_dout(dpp, 10) << "Method " << req_meth << " is supported" << dendl;
map<string, bufferlist>::iterator aiter = s->bucket_attrs.find(RGW_ATTR_CORS);
if (aiter == s->bucket_attrs.end()) {
ldpp_dout(this, 20) << "no CORS configuration attr found" << dendl;
- cors_exist = false;
return 0; /* no CORS configuration found */
}
return 0;
}
+int RGWOp::read_global_cors()
+{
+ string allow_origins, allow_headers, allow_methods, expose_headers;
+ int ret = g_conf().get_val("rgw_gcors_allow_origins", &allow_origins);
+ if (ret < 0 || allow_origins.empty()) {
+ return -EINVAL;
+ }
+ ret = g_conf().get_val("rgw_gcors_allow_headers", &allow_headers);
+ if (ret < 0 || allow_headers.empty()) {
+ return -EINVAL;
+ }
+ ret = g_conf().get_val("rgw_gcors_allow_methods", &allow_methods);
+ if (ret < 0 || allow_methods.empty()) {
+ return -EINVAL;
+ }
+ g_conf().get_val("rgw_gcors_expose_headers", &expose_headers);
+ if (RGWCORSRule::create_rule(allow_origins.c_str(), allow_headers.c_str(), expose_headers.c_str(), allow_methods.c_str(),
+ optional_global_cors) < 0) {
+ return -EINVAL;
+ }
+
+ cors_exist = true;
+
+ if (s->cct->_conf->subsys.should_gather<ceph_subsys_rgw, 15>()) {
+ XMLFormatter f;
+ RGWCORSRule_S3 *s3cors = static_cast<RGWCORSRule_S3 *>(&(*optional_global_cors));
+ ldpp_dout(this, 15) << "Read global RGWCORSRule";
+ s3cors->to_xml(f);
+ f.flush(*_dout);
+ *_dout << dendl;
+ }
+ return 0;
+}
+
/** CORS 6.2.6.
* If any of the header field-names is not a ASCII case-insensitive match for
* any of the values in list of headers do not set any additional headers and
if (!rule->is_header_allowed((*it).c_str(), (*it).length())) {
ldpp_dout(dpp, 5) << "Header " << (*it) << " is not registered in this rule" << dendl;
} else {
+ ldpp_dout(dpp, 20) << "Header " << (*it) << " is registered in this rule" << dendl;
if (hdrs.length() > 0) hdrs.append(",");
hdrs.append((*it));
}
}
/* Custom: */
+ cors_exist = false;
origin = orig;
- int temp_op_ret = read_bucket_cors();
- if (temp_op_ret < 0) {
- op_ret = temp_op_ret;
- return false;
- }
+ const int read_global_cors_ret = read_global_cors();
+ const int temp_op_ret = read_bucket_cors();
if (!cors_exist) {
- ldpp_dout(this, 2) << "No CORS configuration set yet for this bucket" << dendl;
+ ldpp_dout(this, 2) << "No global CORS or bucket CORS configuration set yet" << dendl;
+ op_ret = std::min(temp_op_ret, read_global_cors_ret);
return false;
}
/* CORS 6.2.2. */
RGWCORSRule *rule = bucket_cors.host_name_rule(orig);
- if (!rule)
- return false;
+ auto is_allowed_to_generate_rule_cors_header = [this, &origin, &method, &headers, &exp_headers, &max_age] (RGWCORSRule *rule) {
+ if (!rule)
+ return false;
- /*
- * Set the Allowed-Origin header to a asterisk if this is allowed in the rule
- * and no Authorization was send by the client
- *
- * The origin parameter specifies a URI that may access the resource. The browser must enforce this.
- * For requests without credentials, the server may specify "*" as a wildcard,
- * thereby allowing any origin to access the resource.
- */
- const char *authorization = s->info.env->get("HTTP_AUTHORIZATION");
- if (!authorization && rule->has_wildcard_origin())
- origin = "*";
-
- /* CORS 6.2.3. */
- const char *req_meth = s->info.env->get("HTTP_ACCESS_CONTROL_REQUEST_METHOD");
- if (!req_meth) {
- req_meth = s->info.method;
- }
+ /*
+ * Set the Allowed-Origin header to a asterisk if this is allowed in the rule
+ * and no Authorization was send by the client
+ *
+ * The origin parameter specifies a URI that may access the resource. The browser must enforce this.
+ * For requests without credentials, the server may specify "*" as a wildcard,
+ * thereby allowing any origin to access the resource.
+ */
+ const char *authorization = s->info.env->get("HTTP_AUTHORIZATION");
+ if (!authorization && rule->has_wildcard_origin())
+ origin = "*";
- if (req_meth) {
- method = req_meth;
- /* CORS 6.2.5. */
- if (!validate_cors_rule_method(this, rule, req_meth)) {
- return false;
+ /* CORS 6.2.3. */
+ const char *req_meth = s->info.env->get("HTTP_ACCESS_CONTROL_REQUEST_METHOD");
+ if (!req_meth) {
+ req_meth = s->info.method;
+ }
+
+ if (req_meth) {
+ method = req_meth;
+ /* CORS 6.2.5. */
+ if (!validate_cors_rule_method(this, rule, req_meth)) {
+ return false;
+ }
}
- }
- /* CORS 6.2.4. */
- const char *req_hdrs = s->info.env->get("HTTP_ACCESS_CONTROL_REQUEST_HEADERS");
+ /* CORS 6.2.4. */
+ const char *req_hdrs = s->info.env->get("HTTP_ACCESS_CONTROL_REQUEST_HEADERS");
- /* CORS 6.2.6. */
- get_cors_response_headers(this, rule, req_hdrs, headers, exp_headers, max_age);
+ /* CORS 6.2.6. */
+ get_cors_response_headers(this, rule, req_hdrs, headers, exp_headers, max_age);
- return true;
+ return true;
+ };
+
+ if (is_allowed_to_generate_rule_cors_header(rule)) {
+ return true;
+ }
+
+ if (optional_global_cors.has_value() && is_allowed_to_generate_rule_cors_header(&(*optional_global_cors))) {
+ return true;
+ }
+
+ return false;
}
int rgw_policy_from_attrset(const DoutPrefixProvider *dpp, CephContext *cct, map<string, bufferlist>& attrset, RGWAccessControlPolicy *policy)
return 0;
}
+int RGWOptionsCORS::validate_global_cors_request(RGWCORSRule *global_cors_rule) {
+ ldpp_dout(this, 20) << "Validating request with global CORS" << dendl;
+ rule = global_cors_rule;
+ if (!rule) {
+ ldpp_dout(this, 10) << "There is no global cors rule present" << dendl;
+ return -ENOENT;
+ }
+
+ if (!rule->is_origin_present(origin)) {
+ ldpp_dout(this, 10) << "There is no cors rule present for " << origin << dendl;
+ return -ENOENT;
+ }
+
+ if (!validate_cors_rule_method(this, rule, req_meth)) {
+ return -ENOENT;
+ }
+
+ if (!validate_cors_rule_header(this, rule, req_hdrs)) {
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
void RGWOptionsCORS::execute(optional_yield y)
{
op_ret = read_bucket_cors();
- if (op_ret < 0)
- return;
+ int ret = read_global_cors();
+ if (ret < 0 && op_ret < 0) {
+ ldpp_dout(this, 2) << "No CORS configuration set yet for this bucket nor globally" << dendl;
+ return;
+ }
origin = s->info.env->get("HTTP_ORIGIN");
if (!origin) {
}
req_hdrs = s->info.env->get("HTTP_ACCESS_CONTROL_REQUEST_HEADERS");
op_ret = validate_cors_request(&bucket_cors);
- if (!rule) {
+ if ((op_ret < 0 || !rule) && optional_global_cors.has_value()) {
+ op_ret = validate_global_cors_request(&(*optional_global_cors)) ;
+ }
+ if (op_ret < 0 || !rule) {
origin = req_meth = NULL;
return;
}
- return;
}
int RGWGetRequestPayment::verify_permission(optional_yield y)
set_req_state_err(s, op_ret);
}
dump_errno(s);
- end_header(s);
+ end_header(s, this);
}
int RGWPutBucketPolicy::verify_permission(optional_yield y)
req_state *s;
RGWHandler *dialect_handler;
rgw::sal::Driver* driver;
+ std::optional<RGWCORSRule> optional_global_cors;
RGWCORSConfiguration bucket_cors;
bool cors_exist;
RGWQuota quota;
this->dialect_handler = dialect_handler;
}
int read_bucket_cors();
+ int read_global_cors();
bool generate_cors_headers(std::string& origin, std::string& method, std::string& headers, std::string& exp_headers, unsigned *max_age);
virtual int verify_params() { return 0; }
int verify_permission(optional_yield y) override {return 0;}
int validate_cors_request(RGWCORSConfiguration *cc);
+ int validate_global_cors_request(RGWCORSRule *cc);
void execute(optional_yield y) override;
void get_response_params(std::string& allowed_hdrs, std::string& exp_hdrs, unsigned *max_age);
void send_response() override = 0;
ldpp_dout(op, 2) << "init op" << dendl;
ret = op->init_processing(y);
+ if (op->get_type() == RGW_OP_OPTIONS_CORS && ret == -EINVAL) {
+ ldpp_dout(op, 0) << "NOTICE: RGW_OP_OPTIONS_CORS shouldn't return -EINVAL in case we have a global CORS!" << dendl;
+ ret = 0;
+ }
if (ret < 0) {
return ret;
}
req->op = op;
ldpp_dout(op, 10) << "op=" << typeid(*op).name() << " " << dendl;
s->op_type = op->get_type();
-
try {
ldpp_dout(op, 2) << "verifying requester" << dendl;
ret = op->verify_requester(*penv.auth_registry, yield);
dump_start(s);
// Explicitly use chunked transfer encoding so that we can stream the result
// to the user without having to wait for the full length of it.
- end_header(s, NULL, to_mime_type(s->format), CHUNKED_TRANSFER_ENCODING);
+ end_header(s, this, to_mime_type(s->format), CHUNKED_TRANSFER_ENCODING);
if (! op_ret) {
list_all_buckets_start(s);
set_req_state_err(s, op_ret);
}
dump_errno(s);
- end_header(s);
+ end_header(s, this);
if (op_ret < 0)
return;
set_req_state_err(s, op_ret);
}
dump_errno(s);
- end_header(s);
+ end_header(s, this);
}
void RGWGetBucketEncryption_ObjStore_S3::send_response()
set_req_state_err(s, op_ret);
dump_errno(s);
- end_header(s);
+ end_header(s,this);
}
int RGWPutBucketOwnershipControls_ObjStore_S3::get_params(optional_yield y)
set_req_state_err(s, op_ret);
}
dump_errno(s);
- end_header(s);
+ end_header(s, this);
}
void RGWGetBucketObjectLock_ObjStore_S3::send_response()
}
}
+RGWOp *RGWHandler_REST_Service_S3::op_options()
+{
+ return new RGWOptionsCORS_ObjStore_S3;
+}
+
RGWOp *RGWHandler_REST_Service_S3::op_head()
{
return new RGWListBuckets_ObjStore_S3;
return s->info.args.exists("usage");
}
RGWOp *op_get() override;
+ RGWOp *op_options() override;
RGWOp *op_head() override;
public:
RGWHandler_REST_Service_S3(const rgw::auth::StrategyRegistry& auth_registry) :
s->user_acl);
dump_errno(s);
dump_header(s, "Accept-Ranges", "bytes");
- end_header(s, NULL, NULL, NO_CONTENT_LENGTH, true);
+ end_header(s, this, NULL, NO_CONTENT_LENGTH, true);
}
if (! op_ret) {
s->user->get_max_buckets(),
s->user_acl);
dump_errno(s);
- end_header(s, nullptr, nullptr, s->formatter->get_len(), true);
+ end_header(s, this, nullptr, s->formatter->get_len(), true);
}
if (sent_data || s->cct->_conf->rgw_swift_enforce_content_length) {