]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_rest_client.cc
import ceph 15.2.14
[ceph.git] / ceph / src / rgw / rgw_rest_client.cc
CommitLineData
7c673cae 1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
9f95a23c 2// vim: ts=8 sw=2 smarttab ft=cpp
7c673cae
FG
3
4#include "rgw_common.h"
5#include "rgw_rest_client.h"
6#include "rgw_auth_s3.h"
7#include "rgw_http_errors.h"
8#include "rgw_rados.h"
9
7c673cae
FG
10#include "common/armor.h"
11#include "common/strtol.h"
12#include "include/str_list.h"
13#include "rgw_crypt_sanitize.h"
14
15#define dout_context g_ceph_context
16#define dout_subsys ceph_subsys_rgw
17
11fdf7f2 18int RGWHTTPSimpleRequest::get_status()
7c673cae
FG
19{
20 int retcode = get_req_retcode();
21 if (retcode < 0) {
22 return retcode;
23 }
24 return status;
25}
26
11fdf7f2 27int RGWHTTPSimpleRequest::handle_header(const string& name, const string& val)
7c673cae
FG
28{
29 if (name == "CONTENT_LENGTH") {
30 string err;
31 long len = strict_strtol(val.c_str(), 10, &err);
32 if (!err.empty()) {
33 ldout(cct, 0) << "ERROR: failed converting content length (" << val << ") to int " << dendl;
34 return -EINVAL;
35 }
36
37 max_response = len;
38 }
39
40 return 0;
41}
42
11fdf7f2 43int RGWHTTPSimpleRequest::receive_header(void *ptr, size_t len)
7c673cae 44{
11fdf7f2
TL
45 unique_lock guard(out_headers_lock);
46
7c673cae
FG
47 char line[len + 1];
48
49 char *s = (char *)ptr, *end = (char *)ptr + len;
50 char *p = line;
51 ldout(cct, 10) << "receive_http_header" << dendl;
52
53 while (s != end) {
54 if (*s == '\r') {
55 s++;
56 continue;
57 }
58 if (*s == '\n') {
59 *p = '\0';
60 ldout(cct, 10) << "received header:" << line << dendl;
61 // TODO: fill whatever data required here
62 char *l = line;
63 char *tok = strsep(&l, " \t:");
64 if (tok && l) {
65 while (*l == ' ')
66 l++;
67
68 if (strcmp(tok, "HTTP") == 0 || strncmp(tok, "HTTP/", 5) == 0) {
69 http_status = atoi(l);
70 if (http_status == 100) /* 100-continue response */
71 continue;
72 status = rgw_http_error_to_errno(http_status);
73 } else {
74 /* convert header field name to upper case */
75 char *src = tok;
76 char buf[len + 1];
77 size_t i;
78 for (i = 0; i < len && *src; ++i, ++src) {
79 switch (*src) {
80 case '-':
81 buf[i] = '_';
82 break;
83 default:
84 buf[i] = toupper(*src);
85 }
86 }
87 buf[i] = '\0';
88 out_headers[buf] = l;
89 int r = handle_header(buf, l);
90 if (r < 0)
91 return r;
92 }
93 }
94 }
95 if (s != end)
96 *p++ = *s++;
97 }
98 return 0;
99}
100
101static void get_new_date_str(string& date_str)
102{
c07f9fc5 103 date_str = rgw_to_asctime(ceph_clock_now());
7c673cae
FG
104}
105
11fdf7f2
TL
106static void get_gmt_date_str(string& date_str)
107{
108 auto now_time = ceph::real_clock::now();
109 time_t rawtime = ceph::real_clock::to_time_t(now_time);
110
111 char buffer[80];
112
113 struct tm timeInfo;
114 gmtime_r(&rawtime, &timeInfo);
115 strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S %z", &timeInfo);
116
117 date_str = buffer;
118}
119
120int RGWRESTSimpleRequest::execute(RGWAccessKey& key, const char *_method, const char *resource)
7c673cae 121{
11fdf7f2 122 method = _method;
7c673cae
FG
123 string new_url = url;
124 string new_resource = resource;
125
126 if (new_url[new_url.size() - 1] == '/' && resource[0] == '/') {
127 new_url = new_url.substr(0, new_url.size() - 1);
128 } else if (resource[0] != '/') {
129 new_resource = "/";
130 new_resource.append(resource);
131 }
132 new_url.append(new_resource);
11fdf7f2 133 url = new_url;
7c673cae
FG
134
135 string date_str;
136 get_new_date_str(date_str);
137 headers.push_back(pair<string, string>("HTTP_DATE", date_str));
138
139 string canonical_header;
9f95a23c 140 meta_map_t meta_map;
7c673cae 141 map<string, string> sub_resources;
11fdf7f2
TL
142
143 rgw_create_s3_canonical_header(method.c_str(), NULL, NULL, date_str.c_str(),
144 meta_map, meta_map, url.c_str(), sub_resources,
145 canonical_header);
7c673cae
FG
146
147 string digest;
31f18b77
FG
148 try {
149 digest = rgw::auth::s3::get_v2_signature(cct, key.key, canonical_header);
150 } catch (int ret) {
7c673cae
FG
151 return ret;
152 }
153
154 string auth_hdr = "AWS " + key.id + ":" + digest;
155
156 ldout(cct, 15) << "generated auth header: " << auth_hdr << dendl;
157
158 headers.push_back(pair<string, string>("AUTHORIZATION", auth_hdr));
9f95a23c 159 int r = process(null_yield);
7c673cae
FG
160 if (r < 0)
161 return r;
162
163 return status;
164}
165
11fdf7f2 166int RGWHTTPSimpleRequest::send_data(void *ptr, size_t len, bool* pause)
7c673cae
FG
167{
168 if (!send_iter)
169 return 0;
170
171 if (len > send_iter->get_remaining())
172 len = send_iter->get_remaining();
173
174 send_iter->copy(len, (char *)ptr);
175
176 return len;
177}
178
11fdf7f2 179int RGWHTTPSimpleRequest::receive_data(void *ptr, size_t len, bool *pause)
7c673cae
FG
180{
181 size_t cp_len, left_len;
182
183 left_len = max_response > response.length() ? (max_response - response.length()) : 0;
184 if (left_len == 0)
185 return 0; /* don't read extra data */
186
187 cp_len = (len > left_len) ? left_len : len;
188 bufferptr p((char *)ptr, cp_len);
189
190 response.append(p);
191
192 return 0;
7c673cae
FG
193}
194
11fdf7f2 195static void append_param(string& dest, const string& name, const string& val)
7c673cae
FG
196{
197 if (dest.empty()) {
198 dest.append("?");
199 } else {
200 dest.append("&");
201 }
202 string url_name;
203 url_encode(name, url_name);
204 dest.append(url_name);
205
206 if (!val.empty()) {
207 string url_val;
208 url_encode(val, url_val);
209 dest.append("=");
210 dest.append(url_val);
211 }
212}
213
11fdf7f2 214static void do_get_params_str(const param_vec_t& params, map<string, string>& extra_args, string& dest)
7c673cae
FG
215{
216 map<string, string>::iterator miter;
217 for (miter = extra_args.begin(); miter != extra_args.end(); ++miter) {
218 append_param(dest, miter->first, miter->second);
219 }
11fdf7f2 220 for (auto iter = params.begin(); iter != params.end(); ++iter) {
7c673cae
FG
221 append_param(dest, iter->first, iter->second);
222 }
223}
224
11fdf7f2
TL
225void RGWHTTPSimpleRequest::get_params_str(map<string, string>& extra_args, string& dest)
226{
227 do_get_params_str(params, extra_args, dest);
228}
229
230void RGWHTTPSimpleRequest::get_out_headers(map<string, string> *pheaders)
231{
232 unique_lock guard(out_headers_lock);
233 pheaders->swap(out_headers);
234 out_headers.clear();
235}
236
237static int sign_request(CephContext *cct, RGWAccessKey& key, RGWEnv& env, req_info& info)
7c673cae
FG
238{
239 /* don't sign if no key is provided */
240 if (key.key.empty()) {
241 return 0;
242 }
243
11fdf7f2 244 if (cct->_conf->subsys.should_gather<ceph_subsys_rgw, 20>()) {
31f18b77
FG
245 for (const auto& i: env.get_map()) {
246 ldout(cct, 20) << "> " << i.first << " -> " << rgw::crypt_sanitize::x_meta_map{i.first, i.second} << dendl;
7c673cae
FG
247 }
248 }
249
250 string canonical_header;
251 if (!rgw_create_s3_canonical_header(info, NULL, canonical_header, false)) {
252 ldout(cct, 0) << "failed to create canonical s3 header" << dendl;
253 return -EINVAL;
254 }
255
256 ldout(cct, 10) << "generated canonical header: " << canonical_header << dendl;
257
258 string digest;
31f18b77
FG
259 try {
260 digest = rgw::auth::s3::get_v2_signature(cct, key.key, canonical_header);
261 } catch (int ret) {
7c673cae
FG
262 return ret;
263 }
264
265 string auth_hdr = "AWS " + key.id + ":" + digest;
266 ldout(cct, 15) << "generated auth header: " << auth_hdr << dendl;
267
31f18b77 268 env.set("AUTHORIZATION", auth_hdr);
7c673cae
FG
269
270 return 0;
271}
272
273int RGWRESTSimpleRequest::forward_request(RGWAccessKey& key, req_info& info, size_t max_response, bufferlist *inbl, bufferlist *outbl)
274{
275
276 string date_str;
277 get_new_date_str(date_str);
278
279 RGWEnv new_env;
280 req_info new_info(cct, &new_env);
281 new_info.rebuild_from(info);
f91f0fd5
TL
282 string bucket_encode;
283 string request_uri_encode;
284 size_t pos = new_info.request_uri.substr(1, new_info.request_uri.size() - 1).find("/");
285 string bucket = new_info.request_uri.substr(1, pos);
286 url_encode(bucket, bucket_encode);
287 if (std::string::npos != pos)
288 request_uri_encode = string("/") + bucket_encode + new_info.request_uri.substr(pos + 1);
289 else
290 request_uri_encode = string("/") + bucket_encode;
291 new_info.request_uri = request_uri_encode;
7c673cae 292 new_env.set("HTTP_DATE", date_str.c_str());
7f7e6c64
TL
293 const char* const content_md5 = info.env->get("HTTP_CONTENT_MD5");
294 if (content_md5) {
295 new_env.set("HTTP_CONTENT_MD5", content_md5);
296 }
11fdf7f2 297 int ret = sign_request(cct, key, new_env, new_info);
7c673cae
FG
298 if (ret < 0) {
299 ldout(cct, 0) << "ERROR: failed to sign request" << dendl;
300 return ret;
301 }
302
31f18b77
FG
303 for (const auto& kv: new_env.get_map()) {
304 headers.emplace_back(kv);
7c673cae
FG
305 }
306
9f95a23c 307 meta_map_t& meta_map = new_info.x_meta_map;
31f18b77
FG
308 for (const auto& kv: meta_map) {
309 headers.emplace_back(kv);
7c673cae
FG
310 }
311
312 string params_str;
313 get_params_str(info.args.get_params(), params_str);
314
315 string new_url = url;
316 string& resource = new_info.request_uri;
317 string new_resource = resource;
318 if (new_url[new_url.size() - 1] == '/' && resource[0] == '/') {
319 new_url = new_url.substr(0, new_url.size() - 1);
320 } else if (resource[0] != '/') {
321 new_resource = "/";
322 new_resource.append(resource);
323 }
324 new_url.append(new_resource + params_str);
325
326 bufferlist::iterator bliter;
327
328 if (inbl) {
329 bliter = inbl->begin();
330 send_iter = &bliter;
331
332 set_send_length(inbl->length());
333 }
334
11fdf7f2
TL
335 method = new_info.method;
336 url = new_url;
337
9f95a23c 338 int r = process(null_yield);
7c673cae
FG
339 if (r < 0){
340 if (r == -EINVAL){
341 // curl_easy has errored, generally means the service is not available
342 r = -ERR_SERVICE_UNAVAILABLE;
343 }
344 return r;
345 }
346
347 response.append((char)0); /* NULL terminate response */
348
349 if (outbl) {
350 outbl->claim(response);
351 }
352
353 return status;
354}
355
356class RGWRESTStreamOutCB : public RGWGetDataCB {
11fdf7f2 357 RGWRESTStreamS3PutObj *req;
7c673cae 358public:
11fdf7f2 359 explicit RGWRESTStreamOutCB(RGWRESTStreamS3PutObj *_req) : req(_req) {}
7c673cae
FG
360 int handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) override; /* callback for object iteration when sending data */
361};
362
363int RGWRESTStreamOutCB::handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_len)
364{
365 dout(20) << "RGWRESTStreamOutCB::handle_data bl.length()=" << bl.length() << " bl_ofs=" << bl_ofs << " bl_len=" << bl_len << dendl;
366 if (!bl_ofs && bl_len == bl.length()) {
11fdf7f2
TL
367 req->add_send_data(bl);
368 return 0;
7c673cae
FG
369 }
370
371 bufferptr bp(bl.c_str() + bl_ofs, bl_len);
372 bufferlist new_bl;
373 new_bl.push_back(bp);
374
11fdf7f2
TL
375 req->add_send_data(new_bl);
376 return 0;
7c673cae
FG
377}
378
11fdf7f2 379RGWRESTStreamS3PutObj::~RGWRESTStreamS3PutObj()
7c673cae 380{
11fdf7f2 381 delete out_cb;
7c673cae
FG
382}
383
384static void grants_by_type_add_one_grant(map<int, string>& grants_by_type, int perm, ACLGrant& grant)
385{
386 string& s = grants_by_type[perm];
387
388 if (!s.empty())
389 s.append(", ");
390
391 string id_type_str;
392 ACLGranteeType& type = grant.get_type();
393 switch (type.get_type()) {
394 case ACL_TYPE_GROUP:
395 id_type_str = "uri";
396 break;
397 case ACL_TYPE_EMAIL_USER:
398 id_type_str = "emailAddress";
399 break;
400 default:
401 id_type_str = "id";
402 }
403 rgw_user id;
404 grant.get_id(id);
405 s.append(id_type_str + "=\"" + id.to_str() + "\"");
406}
407
408struct grant_type_to_header {
409 int type;
410 const char *header;
411};
412
413struct grant_type_to_header grants_headers_def[] = {
414 { RGW_PERM_FULL_CONTROL, "x-amz-grant-full-control"},
415 { RGW_PERM_READ, "x-amz-grant-read"},
416 { RGW_PERM_WRITE, "x-amz-grant-write"},
417 { RGW_PERM_READ_ACP, "x-amz-grant-read-acp"},
418 { RGW_PERM_WRITE_ACP, "x-amz-grant-write-acp"},
419 { 0, NULL}
420};
421
422static bool grants_by_type_check_perm(map<int, string>& grants_by_type, int perm, ACLGrant& grant, int check_perm)
423{
181888fb 424 if ((perm & check_perm) == check_perm) {
7c673cae
FG
425 grants_by_type_add_one_grant(grants_by_type, check_perm, grant);
426 return true;
427 }
428 return false;
429}
430
431static void grants_by_type_add_perm(map<int, string>& grants_by_type, int perm, ACLGrant& grant)
432{
433 struct grant_type_to_header *t;
434
435 for (t = grants_headers_def; t->header; t++) {
436 if (grants_by_type_check_perm(grants_by_type, perm, grant, t->type))
437 return;
438 }
439}
440
9f95a23c 441static void add_grants_headers(map<int, string>& grants, RGWEnv& env, meta_map_t& meta_map)
7c673cae
FG
442{
443 struct grant_type_to_header *t;
444
445 for (t = grants_headers_def; t->header; t++) {
446 map<int, string>::iterator iter = grants.find(t->type);
447 if (iter != grants.end()) {
31f18b77 448 env.set(t->header,iter->second);
7c673cae
FG
449 meta_map[t->header] = iter->second;
450 }
451 }
452}
453
11fdf7f2 454void RGWRESTGenerateHTTPHeaders::init(const string& _method, const string& _url, const string& resource, const param_vec_t& params)
7c673cae 455{
11fdf7f2
TL
456 string params_str;
457 map<string, string>& args = new_info->args.get_params();
458 do_get_params_str(params, args, params_str);
459
460 /* merge params with extra args so that we can sign correctly */
461 for (auto iter = params.begin(); iter != params.end(); ++iter) {
462 new_info->args.append(iter->first, iter->second);
463 }
464
465 url = _url + resource + params_str;
7c673cae
FG
466
467 string date_str;
11fdf7f2 468 get_gmt_date_str(date_str);
7c673cae 469
11fdf7f2 470 new_env->set("HTTP_DATE", date_str.c_str());
7c673cae 471
11fdf7f2
TL
472 method = _method;
473 new_info->method = method.c_str();
7c673cae 474
11fdf7f2
TL
475 new_info->script_uri = "/";
476 new_info->script_uri.append(resource);
477 new_info->request_uri = new_info->script_uri;
478}
7c673cae 479
11fdf7f2
TL
480static bool is_x_amz(const string& s) {
481 return boost::algorithm::starts_with(s, "x-amz-");
482}
7c673cae 483
11fdf7f2
TL
484void RGWRESTGenerateHTTPHeaders::set_extra_headers(const map<string, string>& extra_headers)
485{
486 for (auto iter : extra_headers) {
487 const string& name = lowercase_dash_http_attr(iter.first);
488 new_env->set(name, iter.second.c_str());
489 if (is_x_amz(name)) {
490 new_info->x_meta_map[name] = iter.second;
491 }
492 }
493}
494
495int RGWRESTGenerateHTTPHeaders::set_obj_attrs(map<string, bufferlist>& rgw_attrs)
496{
497 map<string, string> new_attrs;
7c673cae 498
7c673cae 499 /* merge send headers */
11fdf7f2 500 for (auto& attr: rgw_attrs) {
31f18b77
FG
501 bufferlist& bl = attr.second;
502 const string& name = attr.first;
7c673cae
FG
503 string val = bl.c_str();
504 if (name.compare(0, sizeof(RGW_ATTR_META_PREFIX) - 1, RGW_ATTR_META_PREFIX) == 0) {
505 string header_name = RGW_AMZ_META_PREFIX;
506 header_name.append(name.substr(sizeof(RGW_ATTR_META_PREFIX) - 1));
11fdf7f2 507 new_attrs[header_name] = val;
7c673cae
FG
508 }
509 }
11fdf7f2 510
7c673cae 511 RGWAccessControlPolicy policy;
11fdf7f2 512 int ret = rgw_policy_from_attrset(cct, rgw_attrs, &policy);
7c673cae
FG
513 if (ret < 0) {
514 ldout(cct, 0) << "ERROR: couldn't get policy ret=" << ret << dendl;
515 return ret;
516 }
517
11fdf7f2
TL
518 set_http_attrs(new_attrs);
519 set_policy(policy);
520
521 return 0;
522}
523
524static std::set<string> keep_headers = { "content-type",
525 "content-encoding",
526 "content-disposition",
527 "content-language" };
528
529void RGWRESTGenerateHTTPHeaders::set_http_attrs(const map<string, string>& http_attrs)
530{
531 /* merge send headers */
532 for (auto& attr: http_attrs) {
533 const string& val = attr.second;
534 const string& name = lowercase_dash_http_attr(attr.first);
535 if (is_x_amz(name)) {
536 new_env->set(name, val);
537 new_info->x_meta_map[name] = val;
538 } else {
539 new_env->set(attr.first, val); /* Ugh, using the uppercase representation,
540 as the signing function calls info.env.get("CONTENT_TYPE").
541 This needs to be cleaned up! */
542 }
543 }
544}
545
546void RGWRESTGenerateHTTPHeaders::set_policy(RGWAccessControlPolicy& policy)
547{
7c673cae
FG
548 /* update acl headers */
549 RGWAccessControlList& acl = policy.get_acl();
550 multimap<string, ACLGrant>& grant_map = acl.get_grant_map();
551 multimap<string, ACLGrant>::iterator giter;
552 map<int, string> grants_by_type;
553 for (giter = grant_map.begin(); giter != grant_map.end(); ++giter) {
554 ACLGrant& grant = giter->second;
555 ACLPermission& perm = grant.get_permission();
556 grants_by_type_add_perm(grants_by_type, perm.get_permissions(), grant);
557 }
11fdf7f2
TL
558 add_grants_headers(grants_by_type, *new_env, new_info->x_meta_map);
559}
560
561int RGWRESTGenerateHTTPHeaders::sign(RGWAccessKey& key)
562{
563 int ret = sign_request(cct, key, *new_env, *new_info);
7c673cae
FG
564 if (ret < 0) {
565 ldout(cct, 0) << "ERROR: failed to sign request" << dendl;
566 return ret;
567 }
568
11fdf7f2
TL
569 return 0;
570}
571
572void RGWRESTStreamS3PutObj::send_init(rgw_obj& obj)
573{
574 string resource_str;
575 string resource;
576 string new_url = url;
577
578 if (host_style == VirtualStyle) {
579 resource_str = obj.get_oid();
580 new_url = obj.bucket.name + "." + new_url;
581 } else {
582 resource_str = obj.bucket.name + "/" + obj.get_oid();
7c673cae
FG
583 }
584
11fdf7f2
TL
585 //do not encode slash in object key name
586 url_encode(resource_str, resource, false);
7c673cae 587
11fdf7f2
TL
588 if (new_url[new_url.size() - 1] != '/')
589 new_url.append("/");
7c673cae 590
11fdf7f2
TL
591 method = "PUT";
592 headers_gen.init(method, new_url, resource, params);
7c673cae 593
11fdf7f2 594 url = headers_gen.get_url();
7c673cae
FG
595}
596
11fdf7f2 597int RGWRESTStreamS3PutObj::send_ready(RGWAccessKey& key, map<string, bufferlist>& rgw_attrs, bool send)
7c673cae 598{
11fdf7f2 599 headers_gen.set_obj_attrs(rgw_attrs);
7c673cae 600
11fdf7f2
TL
601 return send_ready(key, send);
602}
7c673cae 603
11fdf7f2
TL
604int RGWRESTStreamS3PutObj::send_ready(RGWAccessKey& key, const map<string, string>& http_attrs,
605 RGWAccessControlPolicy& policy, bool send)
606{
607 headers_gen.set_http_attrs(http_attrs);
608 headers_gen.set_policy(policy);
7c673cae 609
11fdf7f2
TL
610 return send_ready(key, send);
611}
7c673cae 612
11fdf7f2
TL
613int RGWRESTStreamS3PutObj::send_ready(RGWAccessKey& key, bool send)
614{
615 headers_gen.sign(key);
7c673cae 616
11fdf7f2
TL
617 for (const auto& kv: new_env.get_map()) {
618 headers.emplace_back(kv);
619 }
7c673cae 620
11fdf7f2 621 out_cb = new RGWRESTStreamOutCB(this);
7c673cae 622
11fdf7f2
TL
623 if (send) {
624 int r = RGWHTTP::send(this);
625 if (r < 0)
626 return r;
7c673cae 627 }
7c673cae 628
11fdf7f2 629 return 0;
7c673cae
FG
630}
631
11fdf7f2
TL
632int RGWRESTStreamS3PutObj::put_obj_init(RGWAccessKey& key, rgw_obj& obj, uint64_t obj_size, map<string, bufferlist>& attrs, bool send)
633{
634 send_init(obj);
635 return send_ready(key, attrs, send);
636}
7c673cae
FG
637
638void set_str_from_headers(map<string, string>& out_headers, const string& header_name, string& str)
639{
640 map<string, string>::iterator iter = out_headers.find(header_name);
641 if (iter != out_headers.end()) {
642 str = iter->second;
643 } else {
644 str.clear();
645 }
646}
647
648static int parse_rgwx_mtime(CephContext *cct, const string& s, ceph::real_time *rt)
649{
650 string err;
651 vector<string> vec;
652
653 get_str_vec(s, ".", vec);
654
655 if (vec.empty()) {
656 return -EINVAL;
657 }
658
659 long secs = strict_strtol(vec[0].c_str(), 10, &err);
660 long nsecs = 0;
661 if (!err.empty()) {
662 ldout(cct, 0) << "ERROR: failed converting mtime (" << s << ") to real_time " << dendl;
663 return -EINVAL;
664 }
665
666 if (vec.size() > 1) {
667 nsecs = strict_strtol(vec[1].c_str(), 10, &err);
668 if (!err.empty()) {
669 ldout(cct, 0) << "ERROR: failed converting mtime (" << s << ") to real_time " << dendl;
670 return -EINVAL;
671 }
672 }
673
674 *rt = utime_t(secs, nsecs).to_real_time();
675
676 return 0;
677}
678
11fdf7f2 679static void send_prepare_convert(const rgw_obj& obj, string *resource)
7c673cae 680{
11fdf7f2
TL
681 string urlsafe_bucket, urlsafe_object;
682 url_encode(obj.bucket.get_key(':', 0), urlsafe_bucket);
683 url_encode(obj.key.name, urlsafe_object);
684 *resource = urlsafe_bucket + "/" + urlsafe_object;
685}
7c673cae 686
11fdf7f2
TL
687int RGWRESTStreamRWRequest::send_request(RGWAccessKey& key, map<string, string>& extra_headers, const rgw_obj& obj, RGWHTTPManager *mgr)
688{
689 string resource;
690 send_prepare_convert(obj, &resource);
7c673cae 691
11fdf7f2
TL
692 return send_request(&key, extra_headers, resource, mgr);
693}
7c673cae 694
11fdf7f2
TL
695int RGWRESTStreamRWRequest::send_prepare(RGWAccessKey& key, map<string, string>& extra_headers, const rgw_obj& obj)
696{
697 string resource;
698 send_prepare_convert(obj, &resource);
699
700 return do_send_prepare(&key, extra_headers, resource);
7c673cae
FG
701}
702
11fdf7f2
TL
703int RGWRESTStreamRWRequest::send_prepare(RGWAccessKey *key, map<string, string>& extra_headers, const string& resource,
704 bufferlist *send_data)
7c673cae 705{
11fdf7f2
TL
706 string new_resource;
707 //do not encode slash
708 url_encode(resource, new_resource, false);
7c673cae 709
11fdf7f2 710 return do_send_prepare(key, extra_headers, new_resource, send_data);
7c673cae
FG
711}
712
11fdf7f2
TL
713int RGWRESTStreamRWRequest::do_send_prepare(RGWAccessKey *key, map<string, string>& extra_headers, const string& resource,
714 bufferlist *send_data)
7c673cae
FG
715{
716 string new_url = url;
ec96510d 717 if (!new_url.empty() && new_url.back() != '/')
7c673cae 718 new_url.append("/");
11fdf7f2 719
7c673cae
FG
720 RGWEnv new_env;
721 req_info new_info(cct, &new_env);
722
7c673cae 723 string new_resource;
11fdf7f2
TL
724 string bucket_name;
725 string old_resource = resource;
726
7c673cae
FG
727 if (resource[0] == '/') {
728 new_resource = resource.substr(1);
729 } else {
730 new_resource = resource;
731 }
732
11fdf7f2
TL
733 size_t pos = new_resource.find("/");
734 bucket_name = new_resource.substr(0, pos);
7c673cae 735
11fdf7f2
TL
736 //when dest is a bucket with out other params, uri should end up with '/'
737 if(pos == string::npos && params.size() == 0 && host_style == VirtualStyle) {
738 new_resource.append("/");
739 }
7c673cae 740
11fdf7f2
TL
741 if (host_style == VirtualStyle) {
742 new_url = bucket_name + "." + new_url;
743 if(pos == string::npos) {
744 new_resource = "";
745 } else {
746 new_resource = new_resource.substr(pos+1);
747 }
7c673cae
FG
748 }
749
11fdf7f2 750 RGWRESTGenerateHTTPHeaders headers_gen(cct, &new_env, &new_info);
7c673cae 751
11fdf7f2 752 headers_gen.init(method, new_url, new_resource, params);
7c673cae 753
11fdf7f2 754 headers_gen.set_http_attrs(extra_headers);
7c673cae 755
31f18b77 756 if (key) {
11fdf7f2
TL
757#if 0
758 new_info.init_meta_info(nullptr);
759#endif
760
761 int ret = headers_gen.sign(*key);
31f18b77
FG
762 if (ret < 0) {
763 ldout(cct, 0) << "ERROR: failed to sign request" << dendl;
764 return ret;
765 }
766 }
767
768 for (const auto& kv: new_env.get_map()) {
769 headers.emplace_back(kv);
7c673cae
FG
770 }
771
31f18b77 772 if (send_data) {
11fdf7f2
TL
773 set_send_length(send_data->length());
774 set_outbl(*send_data);
775 set_send_data_hint(true);
7c673cae 776 }
11fdf7f2 777
7c673cae 778
11fdf7f2
TL
779 method = new_info.method;
780 url = headers_gen.get_url();
781
782 return 0;
783}
784
785int RGWRESTStreamRWRequest::send_request(RGWAccessKey *key, map<string, string>& extra_headers, const string& resource,
786 RGWHTTPManager *mgr, bufferlist *send_data)
787{
788 int ret = send_prepare(key, extra_headers, resource, send_data);
789 if (ret < 0) {
790 return ret;
7c673cae
FG
791 }
792
11fdf7f2
TL
793 return send(mgr);
794}
7c673cae 795
11fdf7f2
TL
796
797int RGWRESTStreamRWRequest::send(RGWHTTPManager *mgr)
798{
7c673cae 799 if (!mgr) {
11fdf7f2 800 return RGWHTTP::send(this);
7c673cae
FG
801 }
802
11fdf7f2
TL
803 int r = mgr->add_request(this);
804 if (r < 0)
805 return r;
806
7c673cae
FG
807 return 0;
808}
809
11fdf7f2
TL
810int RGWRESTStreamRWRequest::complete_request(string *etag,
811 real_time *mtime,
812 uint64_t *psize,
813 map<string, string> *pattrs,
814 map<string, string> *pheaders)
7c673cae 815{
9f95a23c 816 int ret = wait(null_yield);
11fdf7f2
TL
817 if (ret < 0) {
818 return ret;
819 }
820
821 unique_lock guard(out_headers_lock);
822
823 if (etag) {
824 set_str_from_headers(out_headers, "ETAG", *etag);
825 }
7c673cae
FG
826 if (status >= 0) {
827 if (mtime) {
828 string mtime_str;
829 set_str_from_headers(out_headers, "RGWX_MTIME", mtime_str);
830 if (!mtime_str.empty()) {
831 int ret = parse_rgwx_mtime(cct, mtime_str, mtime);
832 if (ret < 0) {
833 return ret;
834 }
835 } else {
836 *mtime = real_time();
837 }
838 }
839 if (psize) {
840 string size_str;
841 set_str_from_headers(out_headers, "RGWX_OBJECT_SIZE", size_str);
842 string err;
843 *psize = strict_strtoll(size_str.c_str(), 10, &err);
844 if (!err.empty()) {
845 ldout(cct, 0) << "ERROR: failed parsing embedded metadata object size (" << size_str << ") to int " << dendl;
846 return -EIO;
847 }
848 }
849 }
850
11fdf7f2 851 for (auto iter = out_headers.begin(); pattrs && iter != out_headers.end(); ++iter) {
7c673cae
FG
852 const string& attr_name = iter->first;
853 if (attr_name.compare(0, sizeof(RGW_HTTP_RGWX_ATTR_PREFIX) - 1, RGW_HTTP_RGWX_ATTR_PREFIX) == 0) {
854 string name = attr_name.substr(sizeof(RGW_HTTP_RGWX_ATTR_PREFIX) - 1);
855 const char *src = name.c_str();
856 char buf[name.size() + 1];
857 char *dest = buf;
858 for (; *src; ++src, ++dest) {
859 switch(*src) {
860 case '_':
861 *dest = '-';
862 break;
863 default:
864 *dest = tolower(*src);
865 }
866 }
867 *dest = '\0';
11fdf7f2 868 (*pattrs)[buf] = iter->second;
7c673cae
FG
869 }
870 }
11fdf7f2
TL
871
872 if (pheaders) {
873 *pheaders = std::move(out_headers);
874 }
7c673cae
FG
875 return status;
876}
877
11fdf7f2 878int RGWHTTPStreamRWRequest::handle_header(const string& name, const string& val)
7c673cae
FG
879{
880 if (name == "RGWX_EMBEDDED_METADATA_LEN") {
881 string err;
882 long len = strict_strtol(val.c_str(), 10, &err);
883 if (!err.empty()) {
884 ldout(cct, 0) << "ERROR: failed converting embedded metadata len (" << val << ") to int " << dendl;
885 return -EINVAL;
886 }
887
888 cb->set_extra_data_len(len);
889 }
890 return 0;
891}
892
11fdf7f2 893int RGWHTTPStreamRWRequest::receive_data(void *ptr, size_t len, bool *pause)
7c673cae 894{
11fdf7f2
TL
895 size_t orig_len = len;
896
897 if (cb) {
898 in_data.append((const char *)ptr, len);
899
900 size_t orig_in_data_len = in_data.length();
901
902 int ret = cb->handle_data(in_data, pause);
903 if (ret < 0)
904 return ret;
905 if (ret == 0) {
906 in_data.clear();
907 } else {
908 /* partial read */
909 ceph_assert(in_data.length() <= orig_in_data_len);
910 len = ret;
911 bufferlist bl;
912 size_t left_to_read = orig_in_data_len - len;
913 if (in_data.length() > left_to_read) {
914 in_data.splice(0, in_data.length() - left_to_read, &bl);
915 }
916 }
917 }
7c673cae 918 ofs += len;
11fdf7f2
TL
919 return orig_len;
920}
921
922void RGWHTTPStreamRWRequest::set_stream_write(bool s) {
9f95a23c 923 std::lock_guard wl{write_lock};
11fdf7f2 924 stream_writes = s;
7c673cae
FG
925}
926
11fdf7f2 927void RGWHTTPStreamRWRequest::unpause_receive()
7c673cae 928{
9f95a23c 929 std::lock_guard req_locker{get_req_lock()};
11fdf7f2
TL
930 if (!read_paused) {
931 _set_read_paused(false);
7c673cae 932 }
11fdf7f2
TL
933}
934
935void RGWHTTPStreamRWRequest::add_send_data(bufferlist& bl)
936{
9f95a23c 937 std::scoped_lock locker{get_req_lock(), write_lock};
11fdf7f2
TL
938 outbl.claim_append(bl);
939 _set_write_paused(false);
940}
941
942uint64_t RGWHTTPStreamRWRequest::get_pending_send_size()
943{
9f95a23c 944 std::lock_guard wl{write_lock};
11fdf7f2
TL
945 return outbl.length();
946}
7c673cae 947
11fdf7f2
TL
948void RGWHTTPStreamRWRequest::finish_write()
949{
9f95a23c 950 std::scoped_lock locker{get_req_lock(), write_lock};
11fdf7f2
TL
951 write_stream_complete = true;
952 _set_write_paused(false);
953}
954
955int RGWHTTPStreamRWRequest::send_data(void *ptr, size_t len, bool *pause)
956{
957 uint64_t out_len;
958 uint64_t send_size;
959 {
9f95a23c 960 std::lock_guard wl{write_lock};
11fdf7f2
TL
961
962 if (outbl.length() == 0) {
963 if ((stream_writes && !write_stream_complete) ||
964 (write_ofs < send_len)) {
965 *pause = true;
966 }
967 return 0;
968 }
969
970 len = std::min(len, (size_t)outbl.length());
971
972 bufferlist bl;
973 outbl.splice(0, len, &bl);
974 send_size = bl.length();
975 if (send_size > 0) {
976 memcpy(ptr, bl.c_str(), send_size);
977 write_ofs += send_size;
978 }
979
980 out_len = outbl.length();
981 }
982 /* don't need to be under write_lock here, avoid deadlocks in case notify callback
983 * needs to lock */
984 if (write_drain_cb) {
985 write_drain_cb->notify(out_len);
7c673cae
FG
986 }
987 return send_size;
988}
989
990class StreamIntoBufferlist : public RGWGetDataCB {
991 bufferlist& bl;
992public:
11fdf7f2 993 explicit StreamIntoBufferlist(bufferlist& _bl) : bl(_bl) {}
7c673cae
FG
994 int handle_data(bufferlist& inbl, off_t bl_ofs, off_t bl_len) override {
995 bl.claim_append(inbl);
996 return bl_len;
997 }
998};
999