diff --git a/src/rgw/rgw_common.cc b/src/rgw/rgw_common.cc index 5acf80f60d6..efb3623ded4 100644 --- a/src/rgw/rgw_common.cc +++ b/src/rgw/rgw_common.cc @@ -119,7 +119,6 @@ req_state::req_state(CephContext *_cct, struct RGWEnv *e) : cct(_cct), cio(NULL) host = NULL; method = NULL; length = NULL; - content_type = NULL; copy_source = NULL; http_auth = NULL; } diff --git a/src/rgw/rgw_common.h b/src/rgw/rgw_common.h index 76dbb260529..f4a02eedcf4 100644 --- a/src/rgw/rgw_common.h +++ b/src/rgw/rgw_common.h @@ -44,6 +44,11 @@ using ceph::crypto::MD5; #define RGW_ATTR_BUCKETS RGW_ATTR_PREFIX "buckets" #define RGW_ATTR_META_PREFIX RGW_ATTR_PREFIX "x-amz-meta-" #define RGW_ATTR_CONTENT_TYPE RGW_ATTR_PREFIX "content_type" +#define RGW_ATTR_CACHE_CONTROL RGW_ATTR_PREFIX "cache_control" +#define RGW_ATTR_CONTENT_DISP RGW_ATTR_PREFIX "content_disposition" +#define RGW_ATTR_CONTENT_ENC RGW_ATTR_PREFIX "content_encoding" +#define RGW_ATTR_CONTENT_LANG RGW_ATTR_PREFIX "content_language" +#define RGW_ATTR_EXPIRES RGW_ATTR_PREFIX "expires" #define RGW_ATTR_ID_TAG RGW_ATTR_PREFIX "idtag" #define RGW_ATTR_SHADOW_OBJ RGW_ATTR_PREFIX "shadow_name" #define RGW_ATTR_MANIFEST RGW_ATTR_PREFIX "manifest" @@ -565,7 +570,7 @@ struct req_state { const char *method; const char *length; uint64_t content_length; - const char *content_type; + map generic_attrs; struct rgw_err err; bool expect_cont; bool header_ended; diff --git a/src/rgw/rgw_main.cc b/src/rgw/rgw_main.cc index 07b45c71436..50b05e5840d 100644 --- a/src/rgw/rgw_main.cc +++ b/src/rgw/rgw_main.cc @@ -416,6 +416,8 @@ int main(int argc, const char **argv) common_init_finish(g_ceph_context); rgw_tools_init(g_ceph_context); + + rgw_rest_init(); curl_global_init(CURL_GLOBAL_ALL); diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index d289b2249c2..bf4de3f2ea8 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -1165,6 +1165,7 @@ void RGWPutObj::execute() bufferlist bl, aclbl; map attrs; int len; + map::iterator iter; perfcounter->inc(l_rgw_put); @@ -1260,10 +1261,10 @@ void RGWPutObj::execute() attrs[RGW_ATTR_USER_MANIFEST] = manifest_bl; } - if (s->content_type) { - bl.clear(); - bl.append(s->content_type, strlen(s->content_type) + 1); - attrs[RGW_ATTR_CONTENT_TYPE] = bl; + for (iter = s->generic_attrs.begin(); iter != s->generic_attrs.end(); ++iter) { + bufferlist& attrbl = attrs[iter->first]; + const string& val = iter->second; + attrbl.append(val.c_str(), val.size() + 1); } rgw_get_request_metadata(s, attrs); @@ -1453,12 +1454,12 @@ int RGWCopyObj::init_common() attrs[RGW_ATTR_ACL] = aclbl; rgw_get_request_metadata(s, attrs); - if (s->content_type) { - bufferlist bl; - bl.append(s->content_type, strlen(s->content_type) + 1); - attrs[RGW_ATTR_CONTENT_TYPE] = bl; + map::iterator iter; + for (iter = s->generic_attrs.begin(); iter != s->generic_attrs.end(); ++iter) { + bufferlist& attrbl = attrs[iter->first]; + const string& val = iter->second; + attrbl.append(val.c_str(), val.size() + 1); } - return 0; } @@ -1617,6 +1618,7 @@ void RGWInitMultipart::execute() bufferlist aclbl; map attrs; rgw_obj obj; + map::iterator iter; if (get_params() < 0) return; @@ -1628,10 +1630,10 @@ void RGWInitMultipart::execute() attrs[RGW_ATTR_ACL] = aclbl; - if (s->content_type) { - bufferlist bl; - bl.append(s->content_type, strlen(s->content_type) + 1); - attrs[RGW_ATTR_CONTENT_TYPE] = bl; + for (iter = s->generic_attrs.begin(); iter != s->generic_attrs.end(); ++iter) { + bufferlist& attrbl = attrs[iter->first]; + const string& val = iter->second; + attrbl.append(val.c_str(), val.size() + 1); } rgw_get_request_metadata(s, attrs); diff --git a/src/rgw/rgw_rest.cc b/src/rgw/rgw_rest.cc index 80a080b342d..53bbeca8a8b 100644 --- a/src/rgw/rgw_rest.cc +++ b/src/rgw/rgw_rest.cc @@ -17,6 +17,53 @@ #define dout_subsys ceph_subsys_rgw + +struct rgw_http_attr { + const char *rgw_attr; + const char *http_attr; +}; + +/* + * mapping between rgw object attrs and output http fields + */ +static struct rgw_http_attr rgw_to_http_attr_list[] = { + { RGW_ATTR_CONTENT_TYPE, "Content-Type"}, + { RGW_ATTR_CONTENT_LANG, "Content-Language"}, + { RGW_ATTR_EXPIRES, "Expires"}, + { RGW_ATTR_CACHE_CONTROL, "Cache-Control"}, + { RGW_ATTR_CONTENT_DISP, "Content-Disposition"}, + { RGW_ATTR_CONTENT_ENC, "Content-Encoding"}, + { NULL, NULL}, +}; + + +map rgw_to_http_attrs; + +void rgw_rest_init() +{ + for (struct rgw_http_attr *attr = rgw_to_http_attr_list; attr->rgw_attr; attr++) { + rgw_to_http_attrs[attr->rgw_attr] = attr->http_attr; + } +} + +struct generic_attr { + const char *http_header; + const char *rgw_attr; +}; + +/* + * mapping between http env fields and rgw object attrs + */ +struct generic_attr generic_attrs[] = { + { "CONTENT_TYPE", RGW_ATTR_CONTENT_TYPE }, + { "HTTP_CONTENT_LANGUAGE", RGW_ATTR_CONTENT_LANG }, + { "HTTP_EXPIRES", RGW_ATTR_EXPIRES }, + { "HTTP_CACHE_CONTROL", RGW_ATTR_CACHE_CONTROL }, + { "HTTP_CONTENT_DISPOSITION", RGW_ATTR_CONTENT_DISP }, + { "HTTP_CONTENT_ENCODING", RGW_ATTR_CONTENT_ENC }, + { NULL, NULL }, +}; + static void dump_status(struct req_state *s, const char *status) { int r = s->cio->print("Status: %s\n", status); @@ -964,7 +1011,13 @@ int RGWREST::preprocess(struct req_state *s, RGWClientIO *cio) s->content_length = atoll(s->length); } - s->content_type = s->env->get("CONTENT_TYPE"); + for (int i = 0; generic_attrs[i].http_header; i++) { + const char *env = s->env->get(generic_attrs[i].http_header); + if (env) { + s->generic_attrs[generic_attrs[i].rgw_attr] = env; + } + } + s->http_auth = s->env->get("HTTP_AUTHORIZATION"); if (g_conf->rgw_print_continue) { diff --git a/src/rgw/rgw_rest.h b/src/rgw/rgw_rest.h index c225aa1490a..69056cb2a25 100644 --- a/src/rgw/rgw_rest.h +++ b/src/rgw/rgw_rest.h @@ -5,6 +5,11 @@ #include "rgw_op.h" #include "rgw_formats.h" + +extern std::map rgw_to_http_attrs; + +extern void rgw_rest_init(); + extern void rgw_flush_formatter_and_reset(struct req_state *s, ceph::Formatter *formatter); diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index 277cd9ac89f..995894b2397 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -49,11 +49,28 @@ void rgw_get_errno_s3(rgw_html_errors *e , int err_no) } } +struct response_attr_param { + const char *param; + const char *http_attr; +}; + +static struct response_attr_param resp_attr_params[] = { + {"response-content-type", "Content-Type"}, + {"response-content-language", "Content-Language"}, + {"response-expires", "Expires"}, + {"response-cache-control", "Cache-Control"}, + {"response-content-disposition", "Content-Disposition"}, + {"response-content-encoding", "Content-Encoding"}, + {NULL, NULL}, +}; + int RGWGetObj_ObjStore_S3::send_response(bufferlist& bl) { - string content_type_str; const char *content_type = NULL; + string content_type_str; int orig_ret = ret; + map response_attrs; + map::iterator riter; if (ret) goto done; @@ -77,36 +94,37 @@ int RGWGetObj_ObjStore_S3::send_response(bufferlist& bl) } } - if (s->args.has_response_modifier()) { + for (struct response_attr_param *p = resp_attr_params; p->param; p++) { bool exists; - content_type_str = s->args.get("response-content-type", &exists); - if (exists) - content_type = content_type_str.c_str(); - string val = s->args.get("response-content-language", &exists); - if (exists) - s->cio->print("Content-Language: %s\n", val.c_str()); - val = s->args.get("response-expires", &exists); - if (exists) - s->cio->print("Expires: %s\n", val.c_str()); - val = s->args.get("response-cache-control", &exists); - if (exists) - s->cio->print("Cache-Control: %s\n", val.c_str()); - val = s->args.get("response-content-disposition", &exists); - if (exists) - s->cio->print("Content-Disposition: %s\n", val.c_str()); - val = s->args.get("response-content-encoding", &exists); - if (exists) - s->cio->print("Content-Encoding: %s\n", val.c_str()); + string val = s->args.get(p->param, &exists); + if (exists) { + if (strcmp(p->param, "response-content-type") != 0) { + response_attrs[p->http_attr] = val; + } else { + content_type_str = val; + content_type = content_type_str.c_str(); + } + } } for (iter = attrs.begin(); iter != attrs.end(); ++iter) { - const char *name = iter->first.c_str(); - if (strncmp(name, RGW_ATTR_META_PREFIX, sizeof(RGW_ATTR_META_PREFIX)-1) == 0) { - name += sizeof(RGW_ATTR_PREFIX) - 1; - s->cio->print("%s: %s\r\n", name, iter->second.c_str()); - } else if (!content_type && strcmp(name, RGW_ATTR_CONTENT_TYPE) == 0) { - content_type = iter->second.c_str(); - } + const char *name = iter->first.c_str(); + map::iterator aiter = rgw_to_http_attrs.find(name); + if (aiter != rgw_to_http_attrs.end()) { + if (response_attrs.count(aiter->second) > 0) // was already overridden by a response param + continue; + + if ((!content_type) && aiter->first.compare(RGW_ATTR_CONTENT_TYPE) == 0) { // special handling for content_type + content_type = iter->second.c_str(); + continue; + } + response_attrs[aiter->second] = iter->second.c_str(); + } else { + if (strncmp(name, RGW_ATTR_META_PREFIX, sizeof(RGW_ATTR_META_PREFIX)-1) == 0) { + name += sizeof(RGW_ATTR_PREFIX) - 1; + s->cio->print("%s: %s\r\n", name, iter->second.c_str()); + } + } } } @@ -116,6 +134,11 @@ done: set_req_state_err(s, ret); dump_errno(s); + + for (riter = response_attrs.begin(); riter != response_attrs.end(); ++riter) { + s->cio->print("%s: %s\n", riter->first.c_str(), riter->second.c_str()); + } + if (!content_type) content_type = "binary/octet-stream"; end_header(s, content_type); diff --git a/src/rgw/rgw_rest_swift.cc b/src/rgw/rgw_rest_swift.cc index 88fc3e300e1..d3d45e5525c 100644 --- a/src/rgw/rgw_rest_swift.cc +++ b/src/rgw/rgw_rest_swift.cc @@ -297,14 +297,14 @@ int RGWPutObj_ObjStore_SWIFT::get_params() supplied_etag = s->env->get("HTTP_ETAG"); - if (!s->content_type) { + if (!s->generic_attrs.count(RGW_ATTR_CONTENT_TYPE)) { dout(5) << "content type wasn't provided, trying to guess" << dendl; const char *suffix = strrchr(s->object, '.'); if (suffix) { suffix++; if (*suffix) { string suffix_str(suffix); - s->content_type = rgw_find_mime_by_ext(suffix_str); + s->generic_attrs[RGW_ATTR_CONTENT_TYPE] = rgw_find_mime_by_ext(suffix_str); } } } @@ -433,6 +433,8 @@ int RGWGetObj_ObjStore_SWIFT::send_response(bufferlist& bl) { const char *content_type = NULL; int orig_ret = ret; + map response_attrs; + map::iterator riter; if (sent_header) goto send_data; @@ -454,13 +456,20 @@ int RGWGetObj_ObjStore_SWIFT::send_response(bufferlist& bl) } for (iter = attrs.begin(); iter != attrs.end(); ++iter) { - const char *name = iter->first.c_str(); - if (strncmp(name, RGW_ATTR_META_PREFIX, sizeof(RGW_ATTR_META_PREFIX)-1) == 0) { - name += sizeof(RGW_ATTR_META_PREFIX) - 1; - s->cio->print("X-%s-Meta-%s: %s\r\n", (s->object ? "Object" : "Container"), name, iter->second.c_str()); - } else if (!content_type && strcmp(name, RGW_ATTR_CONTENT_TYPE) == 0) { - content_type = iter->second.c_str(); - } + const char *name = iter->first.c_str(); + map::iterator aiter = rgw_to_http_attrs.find(name); + if (aiter != rgw_to_http_attrs.end()) { + if (aiter->first.compare(RGW_ATTR_CONTENT_TYPE) == 0) { // special handling for content_type + content_type = iter->second.c_str(); + continue; + } + response_attrs[aiter->second] = iter->second.c_str(); + } else { + if (strncmp(name, RGW_ATTR_META_PREFIX, sizeof(RGW_ATTR_META_PREFIX)-1) == 0) { + name += sizeof(RGW_ATTR_META_PREFIX) - 1; + s->cio->print("X-%s-Meta-%s: %s\r\n", (s->object ? "Object" : "Container"), name, iter->second.c_str()); + } + } } } @@ -470,6 +479,11 @@ int RGWGetObj_ObjStore_SWIFT::send_response(bufferlist& bl) if (ret) set_req_state_err(s, ret); dump_errno(s); + + for (riter = response_attrs.begin(); riter != response_attrs.end(); ++riter) { + s->cio->print("%s: %s\n", riter->first.c_str(), riter->second.c_str()); + } + if (!content_type) content_type = "binary/octet-stream"; end_header(s, content_type);