r = search_err(err_no, RGW_HTTP_ERRORS, ARRAY_LEN(RGW_HTTP_ERRORS));
if (r) {
- if (prot_flags & RGW_REST_WEBSITE && err_no == ERR_WEBSITE_REDIRECT && err.is_clear()) {
- // http_ret was custom set, so don't change it!
- } else {
- err.http_ret = r->http_ret;
- }
+ err.http_ret = r->http_ret;
err.s3_code = r->s3_code;
return;
}
<< " new_err_no=" << new_err_no << dendl;
err_no = new_err_no;
}
- set_req_state_err(s, err_no);
- dump_errno(s);
- dump_bucket_from_state(s);
- if (err_no == -ERR_PERMANENT_REDIRECT || err_no == -ERR_WEBSITE_REDIRECT) {
- string dest_uri;
- if (!s->redirect.empty()) {
- dest_uri = s->redirect;
- } else if (!s->zonegroup_endpoint.empty()) {
- string dest_uri = s->zonegroup_endpoint;
- /*
- * reqest_uri is always start with slash, so we need to remove
- * the unnecessary slash at the end of dest_uri.
- */
- if (dest_uri[dest_uri.size() - 1] == '/') {
- dest_uri = dest_uri.substr(0, dest_uri.size() - 1);
+
+ // If the error handler(s) above dealt with it completely, they should have
+ // returned 0. If non-zero, we need to continue here.
+ if(err_no) {
+ // Watch out, we might have a custom error state already set!
+ if(s->err.http_ret && s->err.http_ret != 200) {
+ dump_errno(s);
+ } else {
+ set_req_state_err(s, err_no);
+ dump_errno(s);
+ }
+ dump_bucket_from_state(s);
+ if (err_no == -ERR_PERMANENT_REDIRECT || err_no == -ERR_WEBSITE_REDIRECT) {
+ string dest_uri;
+ if (!s->redirect.empty()) {
+ dest_uri = s->redirect;
+ } else if (!s->zonegroup_endpoint.empty()) {
+ string dest_uri = s->zonegroup_endpoint;
+ /*
+ * reqest_uri is always start with slash, so we need to remove
+ * the unnecessary slash at the end of dest_uri.
+ */
+ if (dest_uri[dest_uri.size() - 1] == '/') {
+ dest_uri = dest_uri.substr(0, dest_uri.size() - 1);
+ }
+ dest_uri += s->info.request_uri;
+ dest_uri += "?";
+ dest_uri += s->info.request_params;
+ }
+
+ if (!dest_uri.empty()) {
+ dump_redirect(s, dest_uri);
}
- dest_uri += s->info.request_uri;
- dest_uri += "?";
- dest_uri += s->info.request_params;
}
- if (!dest_uri.empty()) {
- dump_redirect(s, dest_uri);
+ if (!error_content.empty()) {
+ /*
+ * TODO we must add all error entries as headers here:
+ * when having a working errordoc, then the s3 error fields are
+ * rendered as HTTP headers, e.g.:
+ * x-amz-error-code: NoSuchKey
+ * x-amz-error-message: The specified key does not exist.
+ * x-amz-error-detail-Key: foo
+ */
+ end_header(s, op, NULL, error_content.size(), false, true);
+ STREAM_IO(s)->write(error_content.c_str(), error_content.size());
+ } else {
+ end_header(s, op);
}
- }
- if (!error_content.empty()) {
- ldout(s->cct, 20) << "error_content is set, we need to serve it INSTEAD"
- " of firing the formatter" << dendl;
- /*
- * FIXME we must add all error entries as headers here:
- * when having a working errordoc, then the s3 error fields are
- * rendered as HTTP headers, e.g.:
- *
- * x-amz-error-code: NoSuchKey
- * x-amz-error-message: The specified key does not exist.
- * x-amz-error-detail-Key: foo
- */
- end_header(s, op, NULL, NO_CONTENT_LENGTH, false, true);
- STREAM_IO(s)->write(error_content.c_str(), error_content.size());
- s->formatter->reset();
- } else {
- end_header(s, op);
- rgw_flush_formatter_and_reset(s, s->formatter);
+ rgw_flush_formatter(s, s->formatter);
}
perfcounter->inc(l_rgw_failed_req);
}
}
done:
- set_req_state_err(s, (partial_content && !op_ret) ? STATUS_PARTIAL_CONTENT
- : op_ret);
- dump_errno(s);
+ if(custom_http_ret) {
+ set_req_state_err(s, 0);
+ dump_errno(s, custom_http_ret);
+ } else {
+ set_req_state_err(s, (partial_content && !op_ret) ? STATUS_PARTIAL_CONTENT
+ : op_ret);
+ dump_errno(s);
+ }
for (riter = response_attrs.begin(); riter != response_attrs.end();
++riter) {
return get_obj_op(false);
}
-int RGWHandler_REST_S3Website::get_errordoc(const string& errordoc_key,
- std::string* error_content) {
- ldout(s->cct, 20) << "TODO Serve Custom error page here if bucket has "
- "<Error>" << dendl;
- *error_content = errordoc_key;
- // 1. Check if errordoc exists
- // 2. Check if errordoc is public
- // 3. Fetch errordoc content
- /*
- * FIXME maybe: need to make sure all of the fields for conditional
- * requests are cleared
- */
- RGWGetObj_ObjStore_S3Website* getop =
- new RGWGetObj_ObjStore_S3Website(true);
- getop->set_get_data(true);
- getop->init(store, s, this);
+int RGWHandler_REST_S3Website::serve_errordoc(int http_ret, const string& errordoc_key) {
+ int ret = 0;
+ s->formatter->reset(); /* Try to throw it all away */
- RGWGetObj_CB cb(getop);
- rgw_obj obj(s->bucket, errordoc_key);
- RGWObjectCtx rctx(store);
- //RGWRados::Object op_target(store, s->bucket_info, *static_cast<RGWObjectCtx *>(s->obj_ctx), obj);
- RGWRados::Object op_target(store, s->bucket_info, rctx, obj);
- RGWRados::Object::Read read_op(&op_target);
+ RGWGetObj_ObjStore_S3Website* getop = (RGWGetObj_ObjStore_S3Website*) op_get();
+ if(!getop) {
+ return -1; // Trigger double error handler
+ }
+ getop->init(store, s, this);
+ getop->range_str = NULL;
+ getop->if_mod = NULL;
+ getop->if_unmod = NULL;
+ getop->if_match = NULL;
+ getop->if_nomatch = NULL;
+ s->object = errordoc_key;
+
+ ret = init_permissions(getop);
+ if (ret < 0) {
+ ldout(s->cct, 20) << "serve_errordoc failed, init_permissions ret=" << ret << dendl;
+ return -1; // Trigger double error handler
+ }
- int ret;
- int64_t ofs = 0;
- int64_t end = -1;
- ret = read_op.prepare(&ofs, &end);
+ ret = read_permissions(getop);
if (ret < 0) {
- goto done;
+ ldout(s->cct, 20) << "serve_errordoc failed, read_permissions ret=" << ret << dendl;
+ return -1; // Trigger double error handler
}
- ret = read_op.iterate(ofs, end, &cb); // FIXME: need to know the final size?
-done:
- delete getop;
- return ret;
+ if(http_ret) {
+ getop->set_custom_http_response(http_ret);
+ }
+
+ ret = getop->init_processing();
+ if(ret < 0) {
+ ldout(s->cct, 20) << "serve_errordoc failed, init_processing ret=" << ret << dendl;
+ return -1; // Trigger double error handler
+ }
+
+ ret = getop->verify_op_mask();
+ if(ret < 0) {
+ ldout(s->cct, 20) << "serve_errordoc failed, verify_op_mask ret=" << ret << dendl;
+ return -1; // Trigger double error handler
+ }
+
+ ret = getop->verify_permission();
+ if(ret < 0) {
+ ldout(s->cct, 20) << "serve_errordoc failed, verify_permission ret=" << ret << dendl;
+ return -1; // Trigger double error handler
+ }
+
+ ret = getop->verify_params();
+ if(ret < 0) {
+ ldout(s->cct, 20) << "serve_errordoc failed, verify_params ret=" << ret << dendl;
+ return -1; // Trigger double error handler
+ }
+
+ // No going back now
+ getop->pre_exec();
+ /*
+ * FIXME Missing headers:
+ * With a working errordoc, the s3 error fields are rendered as HTTP headers,
+ * x-amz-error-code: NoSuchKey
+ * x-amz-error-message: The specified key does not exist.
+ * x-amz-error-detail-Key: foo
+ */
+ getop->execute();
+ getop->complete();
+ return 0;
+
}
-
+
int RGWHandler_REST_S3Website::error_handler(int err_no,
string* error_content) {
+ int new_err_no = -1;
const struct rgw_http_errors* r;
int http_error_code = -1;
- r = search_err(err_no, RGW_HTTP_ERRORS, ARRAY_LEN(RGW_HTTP_ERRORS));
+ r = search_err(err_no > 0 ? err_no : -err_no, RGW_HTTP_ERRORS, ARRAY_LEN(RGW_HTTP_ERRORS));
if (r) {
http_error_code = r->http_ret;
}
+ ldout(s->cct, 10) << "RGWHandler_REST_S3Website::error_handler err_no=" << err_no << " http_ret=" << http_error_code << dendl;
RGWBWRoutingRule rrule;
bool should_redirect =
<< " proto+host:" << protocol << "://" << hostname
<< " -> " << s->redirect << dendl;
return -ERR_WEBSITE_REDIRECT;
+ } else if (err_no == -ERR_WEBSITE_REDIRECT) {
+ // Do nothing here, this redirect will be handled in abort_early's ERR_WEBSITE_REDIRECT block
+ // Do NOT fire the ErrorDoc handler
} else if (!s->bucket_info.website_conf.error_doc.empty()) {
- RGWHandler_REST_S3Website::get_errordoc(
- s->bucket_info.website_conf.error_doc, error_content);
+ /* This serves an entire page!
+ On success, it will return zero, and no further content should be sent to the socket
+ On failure, we need the double-error handler
+ */
+ new_err_no = RGWHandler_REST_S3Website::serve_errordoc(http_error_code, s->bucket_info.website_conf.error_doc);
+ if(new_err_no && new_err_no != -1) {
+ err_no = new_err_no;
+ }
} else {
ldout(s->cct, 20) << "No special error handling today!" << dendl;
}
class RGWGetObj_ObjStore_S3 : public RGWGetObj_ObjStore
{
+protected:
+ // Serving a custom error page from an object is really a 200 response with
+ // just the status line altered.
+ int custom_http_ret = 0;
public:
RGWGetObj_ObjStore_S3() {}
~RGWGetObj_ObjStore_S3() {}
int send_response_data_error();
int send_response_data(bufferlist& bl, off_t ofs, off_t len);
+ void set_custom_http_response(int http_ret) { custom_http_ret = http_ret; }
};
class RGWListBuckets_ObjStore_S3 : public RGWListBuckets_ObjStore {
RGWHandler_REST_S3() : RGWHandler_REST() {}
virtual ~RGWHandler_REST_S3() {}
- int get_errordoc(const string& errordoc_key, string* error_content);
-
virtual int init(RGWRados *store, struct req_state *s, RGWClientIO *cio);
virtual int authorize() {
return RGW_Auth_S3::authorize(store, s);
RGWOp *op_copy() { return NULL; }
RGWOp *op_options() { return NULL; }
- int get_errordoc(const string& errordoc_key, string *error_content);
+ int serve_errordoc(int http_ret, const string &errordoc_key);
public:
RGWHandler_REST_S3Website() : RGWHandler_REST_S3() {}
virtual ~RGWHandler_REST_S3Website() {}
// TODO: do we actually need this?
class RGWGetObj_ObjStore_S3Website : public RGWGetObj_ObjStore_S3
{
+ friend class RGWHandler_REST_S3Website;
private:
bool is_errordoc_request;
public:
if_unmod = NULL;
if_match = NULL;
if_nomatch = NULL;
- return 0;
+ return 0;
} else {
return RGWGetObj_ObjStore_S3::get_params();
}