]>
Commit | Line | Data |
---|---|---|
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 | 19 | int 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 | 28 | int 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 | 44 | int 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 | ||
102 | static 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 |
107 | static 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 | ||
121 | int 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 | 167 | int 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 | 180 | int 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 | 196 | static 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 | 215 | static 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 |
226 | void RGWHTTPSimpleRequest::get_params_str(map<string, string>& extra_args, string& dest) |
227 | { | |
228 | do_get_params_str(params, extra_args, dest); | |
229 | } | |
230 | ||
231 | void 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 | ||
238 | static 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 | ||
274 | int 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 | ||
345 | class RGWRESTStreamOutCB : public RGWGetDataCB { | |
11fdf7f2 | 346 | RGWRESTStreamS3PutObj *req; |
7c673cae | 347 | public: |
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 | ||
352 | int 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 | 368 | RGWRESTStreamS3PutObj::~RGWRESTStreamS3PutObj() |
7c673cae | 369 | { |
11fdf7f2 | 370 | delete out_cb; |
7c673cae FG |
371 | } |
372 | ||
373 | static 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 | ||
397 | struct grant_type_to_header { | |
398 | int type; | |
399 | const char *header; | |
400 | }; | |
401 | ||
402 | struct 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 | ||
411 | static 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 | ||
420 | static 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 | 430 | static 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 | 443 | void 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 |
469 | static bool is_x_amz(const string& s) { |
470 | return boost::algorithm::starts_with(s, "x-amz-"); | |
471 | } | |
7c673cae | 472 | |
11fdf7f2 TL |
473 | void 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 | ||
484 | int 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 | ||
513 | static std::set<string> keep_headers = { "content-type", | |
514 | "content-encoding", | |
515 | "content-disposition", | |
516 | "content-language" }; | |
517 | ||
518 | void 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 | ||
535 | void 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 | ||
550 | int 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 | ||
561 | void 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 | 586 | int 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 |
593 | int 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 |
602 | int 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 |
621 | int 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 | |
627 | void 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 | ||
637 | static 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 | 668 | static 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 |
676 | int 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 |
684 | int 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 |
692 | int 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 |
702 | int 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 | ||
774 | int 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 | |
786 | int 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 |
799 | int 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 | 867 | int 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 | 882 | int 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 | ||
911 | void RGWHTTPStreamRWRequest::set_stream_write(bool s) { | |
912 | Mutex::Locker wl(write_lock); | |
913 | stream_writes = s; | |
7c673cae FG |
914 | } |
915 | ||
11fdf7f2 | 916 | void 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 | ||
924 | void 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 | ||
932 | uint64_t RGWHTTPStreamRWRequest::get_pending_send_size() | |
933 | { | |
934 | Mutex::Locker wl(write_lock); | |
935 | return outbl.length(); | |
936 | } | |
7c673cae | 937 | |
11fdf7f2 TL |
938 | void 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 | ||
946 | int 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 | ||
981 | class StreamIntoBufferlist : public RGWGetDataCB { | |
982 | bufferlist& bl; | |
983 | public: | |
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 |