]>
Commit | Line | Data |
---|---|---|
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 | 18 | int 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 | 27 | int 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 | 43 | int 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 | ||
101 | static 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 |
106 | static 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 | ||
120 | int 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 | 166 | int 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 | 179 | int 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 | 195 | static 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 | 214 | static 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 |
225 | void RGWHTTPSimpleRequest::get_params_str(map<string, string>& extra_args, string& dest) |
226 | { | |
227 | do_get_params_str(params, extra_args, dest); | |
228 | } | |
229 | ||
230 | void 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 | ||
237 | static 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 | ||
273 | int 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 | ||
356 | class RGWRESTStreamOutCB : public RGWGetDataCB { | |
11fdf7f2 | 357 | RGWRESTStreamS3PutObj *req; |
7c673cae | 358 | public: |
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 | ||
363 | int 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 | 379 | RGWRESTStreamS3PutObj::~RGWRESTStreamS3PutObj() |
7c673cae | 380 | { |
11fdf7f2 | 381 | delete out_cb; |
7c673cae FG |
382 | } |
383 | ||
384 | static 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 | ||
408 | struct grant_type_to_header { | |
409 | int type; | |
410 | const char *header; | |
411 | }; | |
412 | ||
413 | struct 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 | ||
422 | static 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 | ||
431 | static 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 | 441 | static 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 | 454 | void 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 |
480 | static bool is_x_amz(const string& s) { |
481 | return boost::algorithm::starts_with(s, "x-amz-"); | |
482 | } | |
7c673cae | 483 | |
11fdf7f2 TL |
484 | void 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 | ||
495 | int 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 | ||
524 | static std::set<string> keep_headers = { "content-type", | |
525 | "content-encoding", | |
526 | "content-disposition", | |
527 | "content-language" }; | |
528 | ||
529 | void 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 | ||
546 | void 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 | ||
561 | int 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 | ||
572 | void 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 | 597 | int 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 |
604 | int 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 |
613 | int 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 |
632 | int 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 | |
638 | void 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 | ||
648 | static 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 | 679 | static 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 |
687 | int 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 |
695 | int 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 |
703 | int 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 |
713 | int 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 | ||
785 | int 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 | |
797 | int 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 |
810 | int 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 | 878 | int 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 | 893 | int 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 | ||
922 | void RGWHTTPStreamRWRequest::set_stream_write(bool s) { | |
9f95a23c | 923 | std::lock_guard wl{write_lock}; |
11fdf7f2 | 924 | stream_writes = s; |
7c673cae FG |
925 | } |
926 | ||
11fdf7f2 | 927 | void 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 | ||
935 | void 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 | ||
942 | uint64_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 |
948 | void 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 | ||
955 | int 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 | ||
990 | class StreamIntoBufferlist : public RGWGetDataCB { | |
991 | bufferlist& bl; | |
992 | public: | |
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 |