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