mirror of
https://github.com/ceph/ceph
synced 2024-12-26 05:25:09 +00:00
rgw: flexible attr fields
Fixes: #3114 Instead of having a few hard coded attrs that are special cased, make it more generic. This allows supporting more header fields, such as cache-control, expires, etc. Signed-off-by: Yehuda Sadeh <yehuda@inktank.com>
This commit is contained in:
parent
d4725c06ab
commit
f4a0b2d926
@ -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;
|
||||
}
|
||||
|
@ -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<string, string> generic_attrs;
|
||||
struct rgw_err err;
|
||||
bool expect_cont;
|
||||
bool header_ended;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -1165,6 +1165,7 @@ void RGWPutObj::execute()
|
||||
bufferlist bl, aclbl;
|
||||
map<string, bufferlist> attrs;
|
||||
int len;
|
||||
map<string, string>::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<string, string>::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<string, bufferlist> attrs;
|
||||
rgw_obj obj;
|
||||
map<string, string>::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);
|
||||
|
@ -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<string, string> 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) {
|
||||
|
@ -5,6 +5,11 @@
|
||||
#include "rgw_op.h"
|
||||
#include "rgw_formats.h"
|
||||
|
||||
|
||||
extern std::map<std::string, std::string> rgw_to_http_attrs;
|
||||
|
||||
extern void rgw_rest_init();
|
||||
|
||||
extern void rgw_flush_formatter_and_reset(struct req_state *s,
|
||||
ceph::Formatter *formatter);
|
||||
|
||||
|
@ -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<string, string> response_attrs;
|
||||
map<string, string>::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<string, string>::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);
|
||||
|
@ -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<string, string> response_attrs;
|
||||
map<string, string>::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<string, string>::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);
|
||||
|
Loading…
Reference in New Issue
Block a user