]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_rest.cc
bump version to 18.2.2-pve1
[ceph.git] / ceph / src / rgw / rgw_rest.cc
CommitLineData
7c673cae 1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
9f95a23c 2// vim: ts=8 sw=2 smarttab ft=cpp
7c673cae 3
31f18b77 4
7c673cae
FG
5#include <errno.h>
6#include <limits.h>
7
8#include <boost/algorithm/string.hpp>
92f5a8d4 9#include <boost/tokenizer.hpp>
7c673cae
FG
10#include "common/Formatter.h"
11#include "common/HTMLFormatter.h"
12#include "common/utf8.h"
13#include "include/str_list.h"
14#include "rgw_common.h"
15#include "rgw_rados.h"
11fdf7f2 16#include "rgw_zone.h"
92f5a8d4 17#include "rgw_auth_s3.h"
7c673cae
FG
18#include "rgw_formats.h"
19#include "rgw_op.h"
20#include "rgw_rest.h"
21#include "rgw_rest_swift.h"
22#include "rgw_rest_s3.h"
23#include "rgw_swift_auth.h"
24#include "rgw_cors_s3.h"
11fdf7f2 25#include "rgw_perf_counters.h"
7c673cae
FG
26
27#include "rgw_client_io.h"
28#include "rgw_resolve.h"
f67539c2 29#include "rgw_sal_rados.h"
7c673cae 30
20effc67 31#include "rgw_ratelimit.h"
7c673cae
FG
32#include <numeric>
33
34#define dout_subsys ceph_subsys_rgw
35
20effc67
TL
36using namespace std;
37
31f18b77
FG
38struct rgw_http_status_code {
39 int code;
40 const char *name;
41};
42
43const static struct rgw_http_status_code http_codes[] = {
44 { 100, "Continue" },
45 { 200, "OK" },
46 { 201, "Created" },
47 { 202, "Accepted" },
48 { 204, "No Content" },
49 { 205, "Reset Content" },
50 { 206, "Partial Content" },
51 { 207, "Multi Status" },
52 { 208, "Already Reported" },
53 { 300, "Multiple Choices" },
54 { 301, "Moved Permanently" },
55 { 302, "Found" },
56 { 303, "See Other" },
57 { 304, "Not Modified" },
58 { 305, "User Proxy" },
59 { 306, "Switch Proxy" },
60 { 307, "Temporary Redirect" },
61 { 308, "Permanent Redirect" },
62 { 400, "Bad Request" },
63 { 401, "Unauthorized" },
64 { 402, "Payment Required" },
65 { 403, "Forbidden" },
66 { 404, "Not Found" },
67 { 405, "Method Not Allowed" },
68 { 406, "Not Acceptable" },
69 { 407, "Proxy Authentication Required" },
70 { 408, "Request Timeout" },
71 { 409, "Conflict" },
72 { 410, "Gone" },
73 { 411, "Length Required" },
74 { 412, "Precondition Failed" },
75 { 413, "Request Entity Too Large" },
76 { 414, "Request-URI Too Long" },
77 { 415, "Unsupported Media Type" },
78 { 416, "Requested Range Not Satisfiable" },
79 { 417, "Expectation Failed" },
80 { 422, "Unprocessable Entity" },
11fdf7f2 81 { 498, "Rate Limited"},
31f18b77
FG
82 { 500, "Internal Server Error" },
83 { 501, "Not Implemented" },
11fdf7f2 84 { 503, "Slow Down"},
31f18b77
FG
85 { 0, NULL },
86};
7c673cae
FG
87
88struct rgw_http_attr {
89 const char *rgw_attr;
90 const char *http_attr;
91};
92
93/*
94 * mapping between rgw object attrs and output http fields
95 */
96static const struct rgw_http_attr base_rgw_to_http_attrs[] = {
97 { RGW_ATTR_CONTENT_LANG, "Content-Language" },
98 { RGW_ATTR_EXPIRES, "Expires" },
99 { RGW_ATTR_CACHE_CONTROL, "Cache-Control" },
100 { RGW_ATTR_CONTENT_DISP, "Content-Disposition" },
101 { RGW_ATTR_CONTENT_ENC, "Content-Encoding" },
102 { RGW_ATTR_USER_MANIFEST, "X-Object-Manifest" },
103 { RGW_ATTR_X_ROBOTS_TAG , "X-Robots-Tag" },
11fdf7f2 104 { RGW_ATTR_STORAGE_CLASS , "X-Amz-Storage-Class" },
7c673cae
FG
105 /* RGW_ATTR_AMZ_WEBSITE_REDIRECT_LOCATION header depends on access mode:
106 * S3 endpoint: x-amz-website-redirect-location
107 * S3Website endpoint: Location
108 */
109 { RGW_ATTR_AMZ_WEBSITE_REDIRECT_LOCATION, "x-amz-website-redirect-location" },
110};
111
112
113struct generic_attr {
114 const char *http_header;
115 const char *rgw_attr;
116};
117
118/*
119 * mapping between http env fields and rgw object attrs
120 */
121static const struct generic_attr generic_attrs[] = {
122 { "CONTENT_TYPE", RGW_ATTR_CONTENT_TYPE },
123 { "HTTP_CONTENT_LANGUAGE", RGW_ATTR_CONTENT_LANG },
124 { "HTTP_EXPIRES", RGW_ATTR_EXPIRES },
125 { "HTTP_CACHE_CONTROL", RGW_ATTR_CACHE_CONTROL },
126 { "HTTP_CONTENT_DISPOSITION", RGW_ATTR_CONTENT_DISP },
127 { "HTTP_CONTENT_ENCODING", RGW_ATTR_CONTENT_ENC },
128 { "HTTP_X_ROBOTS_TAG", RGW_ATTR_X_ROBOTS_TAG },
129};
130
131map<string, string> rgw_to_http_attrs;
132static map<string, string> generic_attrs_map;
133map<int, const char *> http_status_names;
134
135/*
136 * make attrs look_like_this
137 * converts dashes to underscores
138 */
139string lowercase_underscore_http_attr(const string& orig)
140{
141 const char *s = orig.c_str();
142 char buf[orig.size() + 1];
143 buf[orig.size()] = '\0';
144
145 for (size_t i = 0; i < orig.size(); ++i, ++s) {
146 switch (*s) {
147 case '-':
148 buf[i] = '_';
149 break;
150 default:
151 buf[i] = tolower(*s);
152 }
153 }
154 return string(buf);
155}
156
157/*
158 * make attrs LOOK_LIKE_THIS
159 * converts dashes to underscores
160 */
161string uppercase_underscore_http_attr(const string& orig)
162{
163 const char *s = orig.c_str();
164 char buf[orig.size() + 1];
165 buf[orig.size()] = '\0';
166
167 for (size_t i = 0; i < orig.size(); ++i, ++s) {
168 switch (*s) {
169 case '-':
170 buf[i] = '_';
171 break;
172 default:
173 buf[i] = toupper(*s);
174 }
175 }
176 return string(buf);
177}
178
7c673cae
FG
179/* avoid duplicate hostnames in hostnames lists */
180static set<string> hostnames_set;
181static set<string> hostnames_s3website_set;
182
1e59de90 183void rgw_rest_init(CephContext *cct, const rgw::sal::ZoneGroup& zone_group)
7c673cae 184{
7c673cae
FG
185 for (const auto& rgw2http : base_rgw_to_http_attrs) {
186 rgw_to_http_attrs[rgw2http.rgw_attr] = rgw2http.http_attr;
187 }
188
189 for (const auto& http2rgw : generic_attrs) {
190 generic_attrs_map[http2rgw.http_header] = http2rgw.rgw_attr;
191 }
192
193 list<string> extended_http_attrs;
194 get_str_list(cct->_conf->rgw_extended_http_attrs, extended_http_attrs);
195
196 list<string>::iterator iter;
197 for (iter = extended_http_attrs.begin(); iter != extended_http_attrs.end(); ++iter) {
198 string rgw_attr = RGW_ATTR_PREFIX;
199 rgw_attr.append(lowercase_underscore_http_attr(*iter));
200
201 rgw_to_http_attrs[rgw_attr] = camelcase_dash_http_attr(*iter);
202
203 string http_header = "HTTP_";
204 http_header.append(uppercase_underscore_http_attr(*iter));
205
206 generic_attrs_map[http_header] = rgw_attr;
207 }
208
209 for (const struct rgw_http_status_code *h = http_codes; h->code; h++) {
210 http_status_names[h->code] = h->name;
211 }
212
1e59de90
TL
213 std::list<std::string> rgw_dns_names;
214 std::string rgw_dns_names_str = cct->_conf->rgw_dns_name;
215 get_str_list(rgw_dns_names_str, ", ", rgw_dns_names);
216 hostnames_set.insert(rgw_dns_names.begin(), rgw_dns_names.end());
217
218 std::list<std::string> names;
219 zone_group.get_hostnames(names);
220 hostnames_set.insert(names.begin(), names.end());
7c673cae
FG
221 hostnames_set.erase(""); // filter out empty hostnames
222 ldout(cct, 20) << "RGW hostnames: " << hostnames_set << dendl;
223 /* TODO: We should have a sanity check that no hostname matches the end of
224 * any other hostname, otherwise we will get ambigious results from
225 * rgw_find_host_in_domains.
226 * Eg:
227 * Hostnames: [A, B.A]
228 * Inputs: [Z.A, X.B.A]
229 * Z.A clearly splits to subdomain=Z, domain=Z
230 * X.B.A ambigously splits to both {X, B.A} and {X.B, A}
231 */
232
1e59de90 233 zone_group.get_s3website_hostnames(names);
7c673cae 234 hostnames_s3website_set.insert(cct->_conf->rgw_dns_s3website_name);
1e59de90 235 hostnames_s3website_set.insert(names.begin(), names.end());
7c673cae
FG
236 hostnames_s3website_set.erase(""); // filter out empty hostnames
237 ldout(cct, 20) << "RGW S3website hostnames: " << hostnames_s3website_set << dendl;
238 /* TODO: we should repeat the hostnames_set sanity check here
239 * and ALSO decide about overlap, if any
240 */
241}
242
eafe8130 243static bool str_ends_with_nocase(const string& s, const string& suffix, size_t *pos)
7c673cae
FG
244{
245 size_t len = suffix.size();
246 if (len > (size_t)s.size()) {
247 return false;
248 }
249
250 ssize_t p = s.size() - len;
251 if (pos) {
252 *pos = p;
253 }
254
eafe8130 255 return boost::algorithm::iends_with(s, suffix);
7c673cae
FG
256}
257
eafe8130
TL
258static bool rgw_find_host_in_domains(const string& host, string *domain, string *subdomain,
259 const set<string>& valid_hostnames_set)
7c673cae
FG
260{
261 set<string>::iterator iter;
262 /** TODO, Future optimization
263 * store hostnames_set elements _reversed_, and look for a prefix match,
264 * which is much faster than a suffix match.
265 */
266 for (iter = valid_hostnames_set.begin(); iter != valid_hostnames_set.end(); ++iter) {
267 size_t pos;
eafe8130 268 if (!str_ends_with_nocase(host, *iter, &pos))
7c673cae
FG
269 continue;
270
271 if (pos == 0) {
272 *domain = host;
273 subdomain->clear();
274 } else {
275 if (host[pos - 1] != '.') {
276 continue;
277 }
278
279 *domain = host.substr(pos);
280 *subdomain = host.substr(0, pos - 1);
281 }
282 return true;
283 }
284 return false;
285}
286
1e59de90 287static void dump_status(req_state *s, int status,
7c673cae
FG
288 const char *status_name)
289{
290 s->formatter->set_status(status, status_name);
291 try {
292 RESTFUL_IO(s)->send_status(status, status_name);
293 } catch (rgw::io::Exception& e) {
b3b6e05e 294 ldpp_dout(s, 0) << "ERROR: s->cio->send_status() returned err="
7c673cae
FG
295 << e.what() << dendl;
296 }
297}
298
1e59de90 299void rgw_flush_formatter_and_reset(req_state *s, Formatter *formatter)
7c673cae
FG
300{
301 std::ostringstream oss;
302 formatter->output_footer();
303 formatter->flush(oss);
304 std::string outs(oss.str());
305 if (!outs.empty() && s->op != OP_HEAD) {
306 dump_body(s, outs);
307 }
308
309 s->formatter->reset();
310}
311
1e59de90 312void rgw_flush_formatter(req_state *s, Formatter *formatter)
7c673cae
FG
313{
314 std::ostringstream oss;
315 formatter->flush(oss);
316 std::string outs(oss.str());
317 if (!outs.empty() && s->op != OP_HEAD) {
318 dump_body(s, outs);
319 }
320}
321
7c673cae
FG
322void dump_errno(int http_ret, string& out) {
323 stringstream ss;
324
325 ss << http_ret << " " << http_status_names[http_ret];
326 out = ss.str();
327}
328
329void dump_errno(const struct rgw_err &err, string& out) {
330 dump_errno(err.http_ret, out);
331}
332
1e59de90 333void dump_errno(req_state *s)
7c673cae
FG
334{
335 dump_status(s, s->err.http_ret, http_status_names[s->err.http_ret]);
336}
337
1e59de90 338void dump_errno(req_state *s, int http_ret)
7c673cae
FG
339{
340 dump_status(s, http_ret, http_status_names[http_ret]);
341}
342
1e59de90 343void dump_header(req_state* const s,
f67539c2
TL
344 const std::string_view& name,
345 const std::string_view& val)
7c673cae
FG
346{
347 try {
348 RESTFUL_IO(s)->send_header(name, val);
349 } catch (rgw::io::Exception& e) {
b3b6e05e 350 ldpp_dout(s, 0) << "ERROR: s->cio->send_header() returned err="
7c673cae
FG
351 << e.what() << dendl;
352 }
353}
354
1e59de90 355void dump_header(req_state* const s,
f67539c2 356 const std::string_view& name,
7c673cae
FG
357 ceph::buffer::list& bl)
358{
11fdf7f2 359 return dump_header(s, name, rgw_sanitized_hdrval(bl));
7c673cae
FG
360}
361
1e59de90 362void dump_header(req_state* const s,
f67539c2 363 const std::string_view& name,
7c673cae
FG
364 const long long val)
365{
366 char buf[32];
367 const auto len = snprintf(buf, sizeof(buf), "%lld", val);
368
f67539c2 369 return dump_header(s, name, std::string_view(buf, len));
7c673cae
FG
370}
371
1e59de90 372void dump_header(req_state* const s,
f67539c2 373 const std::string_view& name,
7c673cae
FG
374 const utime_t& ut)
375{
376 char buf[32];
377 const auto len = snprintf(buf, sizeof(buf), "%lld.%05d",
378 static_cast<long long>(ut.sec()),
379 static_cast<int>(ut.usec() / 10));
380
f67539c2 381 return dump_header(s, name, std::string_view(buf, len));
7c673cae
FG
382}
383
1e59de90 384void dump_content_length(req_state* const s, const uint64_t len)
7c673cae
FG
385{
386 try {
387 RESTFUL_IO(s)->send_content_length(len);
388 } catch (rgw::io::Exception& e) {
b3b6e05e 389 ldpp_dout(s, 0) << "ERROR: s->cio->send_content_length() returned err="
7c673cae
FG
390 << e.what() << dendl;
391 }
392 dump_header(s, "Accept-Ranges", "bytes");
393}
394
1e59de90 395static void dump_chunked_encoding(req_state* const s)
7c673cae
FG
396{
397 try {
398 RESTFUL_IO(s)->send_chunked_transfer_encoding();
399 } catch (rgw::io::Exception& e) {
b3b6e05e 400 ldpp_dout(s, 0) << "ERROR: RESTFUL_IO(s)->send_chunked_transfer_encoding()"
7c673cae
FG
401 << " returned err=" << e.what() << dendl;
402 }
403}
404
1e59de90 405void dump_etag(req_state* const s,
f67539c2 406 const std::string_view& etag,
7c673cae
FG
407 const bool quoted)
408{
409 if (etag.empty()) {
410 return;
411 }
412
413 if (s->prot_flags & RGW_REST_SWIFT && ! quoted) {
414 return dump_header(s, "etag", etag);
415 } else {
416 return dump_header_quoted(s, "ETag", etag);
417 }
418}
419
1e59de90 420void dump_bucket_from_state(req_state *s)
7c673cae 421{
11fdf7f2 422 if (g_conf()->rgw_expose_bucket && ! s->bucket_name.empty()) {
7c673cae
FG
423 if (! s->bucket_tenant.empty()) {
424 dump_header(s, "Bucket",
425 url_encode(s->bucket_tenant + "/" + s->bucket_name));
426 } else {
427 dump_header(s, "Bucket", url_encode(s->bucket_name));
428 }
429 }
430}
431
1e59de90 432void dump_redirect(req_state * const s, const std::string& redirect)
7c673cae
FG
433{
434 return dump_header_if_nonempty(s, "Location", redirect);
435}
436
437static size_t dump_time_header_impl(char (&timestr)[TIME_BUF_SIZE],
438 const real_time t)
439{
440 const utime_t ut(t);
441 time_t secs = static_cast<time_t>(ut.sec());
442
443 struct tm result;
444 const struct tm * const tmp = gmtime_r(&secs, &result);
445 if (tmp == nullptr) {
446 return 0;
447 }
448
449 return strftime(timestr, sizeof(timestr),
450 "%a, %d %b %Y %H:%M:%S %Z", tmp);
451}
452
1e59de90 453void dump_time_header(req_state *s, const char *name, real_time t)
7c673cae
FG
454{
455 char timestr[TIME_BUF_SIZE];
456
457 const size_t len = dump_time_header_impl(timestr, t);
458 if (len == 0) {
459 return;
460 }
461
f67539c2 462 return dump_header(s, name, std::string_view(timestr, len));
7c673cae
FG
463}
464
465std::string dump_time_to_str(const real_time& t)
466{
467 char timestr[TIME_BUF_SIZE];
468 dump_time_header_impl(timestr, t);
469
470 return timestr;
471}
472
473
1e59de90 474void dump_last_modified(req_state *s, real_time t)
7c673cae
FG
475{
476 dump_time_header(s, "Last-Modified", t);
477}
478
1e59de90 479void dump_epoch_header(req_state *s, const char *name, real_time t)
7c673cae
FG
480{
481 utime_t ut(t);
482 char buf[65];
483 const auto len = snprintf(buf, sizeof(buf), "%lld.%09lld",
484 (long long)ut.sec(),
485 (long long)ut.nsec());
486
f67539c2 487 return dump_header(s, name, std::string_view(buf, len));
7c673cae
FG
488}
489
1e59de90 490void dump_time(req_state *s, const char *name, real_time t)
7c673cae
FG
491{
492 char buf[TIME_BUF_SIZE];
20effc67 493 rgw_to_iso8601(t, buf, sizeof(buf));
7c673cae
FG
494
495 s->formatter->dump_string(name, buf);
496}
497
1e59de90 498void dump_owner(req_state *s, const rgw_user& id, const string& name,
7c673cae
FG
499 const char *section)
500{
501 if (!section)
502 section = "Owner";
503 s->formatter->open_object_section(section);
504 s->formatter->dump_string("ID", id.to_str());
505 s->formatter->dump_string("DisplayName", name);
506 s->formatter->close_section();
507}
508
1e59de90 509void dump_access_control(req_state *s, const char *origin,
7c673cae
FG
510 const char *meth,
511 const char *hdr, const char *exp_hdr,
512 uint32_t max_age) {
513 if (origin && (origin[0] != '\0')) {
514 dump_header(s, "Access-Control-Allow-Origin", origin);
515 /* If the server specifies an origin host rather than "*",
516 * then it must also include Origin in the Vary response header
517 * to indicate to clients that server responses will differ
518 * based on the value of the Origin request header.
519 */
520 if (strcmp(origin, "*") != 0) {
521 dump_header(s, "Vary", "Origin");
522 }
523
524 if (meth && (meth[0] != '\0')) {
525 dump_header(s, "Access-Control-Allow-Methods", meth);
526 }
527 if (hdr && (hdr[0] != '\0')) {
528 dump_header(s, "Access-Control-Allow-Headers", hdr);
529 }
530 if (exp_hdr && (exp_hdr[0] != '\0')) {
531 dump_header(s, "Access-Control-Expose-Headers", exp_hdr);
532 }
533 if (max_age != CORS_MAX_AGE_INVALID) {
534 dump_header(s, "Access-Control-Max-Age", max_age);
535 }
536 }
537}
538
539void dump_access_control(req_state *s, RGWOp *op)
540{
541 string origin;
542 string method;
543 string header;
544 string exp_header;
545 unsigned max_age = CORS_MAX_AGE_INVALID;
546
547 if (!op->generate_cors_headers(origin, method, header, exp_header, &max_age))
548 return;
549
550 dump_access_control(s, origin.c_str(), method.c_str(), header.c_str(),
551 exp_header.c_str(), max_age);
552}
553
1e59de90 554void dump_start(req_state *s)
7c673cae
FG
555{
556 if (!s->content_started) {
557 s->formatter->output_header();
558 s->content_started = true;
559 }
560}
561
562void dump_trans_id(req_state *s)
563{
564 if (s->prot_flags & RGW_REST_SWIFT) {
565 dump_header(s, "X-Trans-Id", s->trans_id);
566 dump_header(s, "X-Openstack-Request-Id", s->trans_id);
567 } else if (s->trans_id.length()) {
568 dump_header(s, "x-amz-request-id", s->trans_id);
569 }
570}
571
1e59de90 572void end_header(req_state* s, RGWOp* op, const char *content_type,
7c673cae
FG
573 const int64_t proposed_content_length, bool force_content_type,
574 bool force_no_error)
575{
576 string ctype;
577
578 dump_trans_id(s);
579
f67539c2
TL
580 if ((!s->is_err()) && s->bucket &&
581 (s->bucket->get_info().owner != s->user->get_id()) &&
582 (s->bucket->get_info().requester_pays)) {
7c673cae
FG
583 dump_header(s, "x-amz-request-charged", "requester");
584 }
585
586 if (op) {
587 dump_access_control(s, op);
588 }
589
590 if (s->prot_flags & RGW_REST_SWIFT && !content_type) {
591 force_content_type = true;
592 }
593
594 /* do not send content type if content length is zero
595 and the content type was not set by the user */
596 if (force_content_type ||
31f18b77 597 (!content_type && s->formatter->get_len() != 0) || s->is_err()){
1e59de90 598 ctype = to_mime_type(s->format);
7c673cae
FG
599 if (s->prot_flags & RGW_REST_SWIFT)
600 ctype.append("; charset=utf-8");
601 content_type = ctype.c_str();
602 }
31f18b77 603 if (!force_no_error && s->is_err()) {
7c673cae 604 dump_start(s);
31f18b77 605 dump(s);
7c673cae
FG
606 dump_content_length(s, s->formatter->get_len());
607 } else {
608 if (proposed_content_length == CHUNKED_TRANSFER_ENCODING) {
609 dump_chunked_encoding(s);
610 } else if (proposed_content_length != NO_CONTENT_LENGTH) {
611 dump_content_length(s, proposed_content_length);
612 }
613 }
614
615 if (content_type) {
616 dump_header(s, "Content-Type", content_type);
617 }
11fdf7f2 618 dump_header_if_nonempty(s, "Server", g_conf()->rgw_service_provider_name);
7c673cae
FG
619
620 try {
621 RESTFUL_IO(s)->complete_header();
622 } catch (rgw::io::Exception& e) {
b3b6e05e 623 ldpp_dout(s, 0) << "ERROR: RESTFUL_IO(s)->complete_header() returned err="
7c673cae
FG
624 << e.what() << dendl;
625 }
626
627 ACCOUNTING_IO(s)->set_account(true);
628 rgw_flush_formatter_and_reset(s, s->formatter);
629}
630
11fdf7f2
TL
631static void build_redirect_url(req_state *s, const string& redirect_base, string *redirect_url)
632{
633 string& dest_uri = *redirect_url;
634
635 dest_uri = redirect_base;
636 /*
637 * reqest_uri is always start with slash, so we need to remove
638 * the unnecessary slash at the end of dest_uri.
639 */
640 if (dest_uri[dest_uri.size() - 1] == '/') {
641 dest_uri = dest_uri.substr(0, dest_uri.size() - 1);
642 }
643 dest_uri += s->info.request_uri;
644 dest_uri += "?";
645 dest_uri += s->info.request_params;
646}
647
1e59de90 648void abort_early(req_state *s, RGWOp* op, int err_no,
f67539c2 649 RGWHandler* handler, optional_yield y)
7c673cae
FG
650{
651 string error_content("");
652 if (!s->formatter) {
653 s->formatter = new JSONFormatter;
1e59de90 654 s->format = RGWFormat::JSON;
7c673cae
FG
655 }
656
657 // op->error_handler is responsible for calling it's handler error_handler
658 if (op != NULL) {
659 int new_err_no;
f67539c2 660 new_err_no = op->error_handler(err_no, &error_content, y);
b3b6e05e 661 ldpp_dout(s, 1) << "op->ERRORHANDLER: err_no=" << err_no
7c673cae
FG
662 << " new_err_no=" << new_err_no << dendl;
663 err_no = new_err_no;
664 } else if (handler != NULL) {
665 int new_err_no;
f67539c2 666 new_err_no = handler->error_handler(err_no, &error_content, y);
b3b6e05e 667 ldpp_dout(s, 1) << "handler->ERRORHANDLER: err_no=" << err_no
7c673cae
FG
668 << " new_err_no=" << new_err_no << dendl;
669 err_no = new_err_no;
670 }
671
672 // If the error handler(s) above dealt with it completely, they should have
673 // returned 0. If non-zero, we need to continue here.
674 if (err_no) {
675 // Watch out, we might have a custom error state already set!
31f18b77 676 if (!s->err.http_ret || s->err.http_ret == 200) {
7c673cae 677 set_req_state_err(s, err_no);
7c673cae 678 }
11fdf7f2
TL
679
680 if (s->err.http_ret == 404 && !s->redirect_zone_endpoint.empty()) {
681 s->err.http_ret = 301;
682 err_no = -ERR_PERMANENT_REDIRECT;
683 build_redirect_url(s, s->redirect_zone_endpoint, &s->redirect);
684 }
685
31f18b77 686 dump_errno(s);
7c673cae
FG
687 dump_bucket_from_state(s);
688 if (err_no == -ERR_PERMANENT_REDIRECT || err_no == -ERR_WEBSITE_REDIRECT) {
689 string dest_uri;
690 if (!s->redirect.empty()) {
691 dest_uri = s->redirect;
692 } else if (!s->zonegroup_endpoint.empty()) {
11fdf7f2 693 build_redirect_url(s, s->zonegroup_endpoint, &dest_uri);
7c673cae
FG
694 }
695
696 if (!dest_uri.empty()) {
697 dump_redirect(s, dest_uri);
698 }
699 }
700
701 if (!error_content.empty()) {
702 /*
703 * TODO we must add all error entries as headers here:
704 * when having a working errordoc, then the s3 error fields are
705 * rendered as HTTP headers, e.g.:
706 * x-amz-error-code: NoSuchKey
707 * x-amz-error-message: The specified key does not exist.
708 * x-amz-error-detail-Key: foo
709 */
710 end_header(s, op, NULL, error_content.size(), false, true);
711 RESTFUL_IO(s)->send_body(error_content.c_str(), error_content.size());
712 } else {
713 end_header(s, op);
714 }
715 }
716 perfcounter->inc(l_rgw_failed_req);
717}
718
1e59de90 719void dump_continue(req_state * const s)
7c673cae
FG
720{
721 try {
722 RESTFUL_IO(s)->send_100_continue();
723 } catch (rgw::io::Exception& e) {
b3b6e05e 724 ldpp_dout(s, 0) << "ERROR: RESTFUL_IO(s)->send_100_continue() returned err="
7c673cae
FG
725 << e.what() << dendl;
726 }
727}
728
1e59de90 729void dump_range(req_state* const s,
7c673cae
FG
730 const uint64_t ofs,
731 const uint64_t end,
732 const uint64_t total)
733{
734 /* dumping range into temp buffer first, as libfcgi will fail to digest
735 * %lld */
736 char range_buf[128];
737 size_t len;
738
739 if (! total) {
740 len = snprintf(range_buf, sizeof(range_buf), "bytes */%lld",
741 static_cast<long long>(total));
742 } else {
743 len = snprintf(range_buf, sizeof(range_buf), "bytes %lld-%lld/%lld",
744 static_cast<long long>(ofs),
745 static_cast<long long>(end),
746 static_cast<long long>(total));
747 }
748
f67539c2 749 return dump_header(s, "Content-Range", std::string_view(range_buf, len));
7c673cae
FG
750}
751
752
1e59de90 753int dump_body(req_state* const s,
7c673cae
FG
754 const char* const buf,
755 const size_t len)
756{
20effc67
TL
757 bool healthchk = false;
758 // we dont want to limit health checks
759 if(s->op_type == RGW_OP_GET_HEALTH_CHECK)
760 healthchk = true;
761 if(len > 0 && !healthchk) {
762 const char *method = s->info.method;
763 s->ratelimit_data->decrease_bytes(method, s->ratelimit_user_name, len, &s->user_ratelimit);
764 if(!rgw::sal::Bucket::empty(s->bucket.get()))
765 s->ratelimit_data->decrease_bytes(method, s->ratelimit_bucket_marker, len, &s->bucket_ratelimit);
766 }
7c673cae
FG
767 try {
768 return RESTFUL_IO(s)->send_body(buf, len);
769 } catch (rgw::io::Exception& e) {
770 return -e.code().value();
771 }
772}
773
1e59de90 774int dump_body(req_state* const s, /* const */ ceph::buffer::list& bl)
7c673cae
FG
775{
776 return dump_body(s, bl.c_str(), bl.length());
777}
778
1e59de90 779int dump_body(req_state* const s, const std::string& str)
7c673cae
FG
780{
781 return dump_body(s, str.c_str(), str.length());
782}
783
1e59de90 784int recv_body(req_state* const s,
7c673cae
FG
785 char* const buf,
786 const size_t max)
787{
20effc67 788 int len;
7c673cae 789 try {
20effc67 790 len = RESTFUL_IO(s)->recv_body(buf, max);
7c673cae
FG
791 } catch (rgw::io::Exception& e) {
792 return -e.code().value();
793 }
20effc67
TL
794 bool healthchk = false;
795 // we dont want to limit health checks
796 if(s->op_type == RGW_OP_GET_HEALTH_CHECK)
797 healthchk = true;
798 if(len > 0 && !healthchk) {
799 const char *method = s->info.method;
800 s->ratelimit_data->decrease_bytes(method, s->ratelimit_user_name, len, &s->user_ratelimit);
801 if(!rgw::sal::Bucket::empty(s->bucket.get()))
802 s->ratelimit_data->decrease_bytes(method, s->ratelimit_bucket_marker, len, &s->bucket_ratelimit);
803 }
804 return len;
805
7c673cae
FG
806}
807
f67539c2 808int RGWGetObj_ObjStore::get_params(optional_yield y)
7c673cae
FG
809{
810 range_str = s->info.env->get("HTTP_RANGE");
811 if_mod = s->info.env->get("HTTP_IF_MODIFIED_SINCE");
812 if_unmod = s->info.env->get("HTTP_IF_UNMODIFIED_SINCE");
813 if_match = s->info.env->get("HTTP_IF_MATCH");
814 if_nomatch = s->info.env->get("HTTP_IF_NONE_MATCH");
815
816 if (s->system_request) {
817 mod_zone_id = s->info.env->get_int("HTTP_DEST_ZONE_SHORT_ID", 0);
818 mod_pg_ver = s->info.env->get_int("HTTP_DEST_PG_VER", 0);
819 rgwx_stat = s->info.args.exists(RGW_SYS_PARAM_PREFIX "stat");
820 get_data &= (!rgwx_stat);
821 }
822
28e407b8
AA
823 if (s->info.args.exists(GET_TORRENT)) {
824 return torrent.get_params();
7c673cae 825 }
7c673cae
FG
826 return 0;
827}
828
1e59de90 829int RESTArgs::get_string(req_state *s, const string& name,
7c673cae
FG
830 const string& def_val, string *val, bool *existed)
831{
832 bool exists;
833 *val = s->info.args.get(name, &exists);
834
835 if (existed)
836 *existed = exists;
837
838 if (!exists) {
839 *val = def_val;
840 return 0;
841 }
842
843 return 0;
844}
845
1e59de90 846int RESTArgs::get_uint64(req_state *s, const string& name,
7c673cae
FG
847 uint64_t def_val, uint64_t *val, bool *existed)
848{
849 bool exists;
850 string sval = s->info.args.get(name, &exists);
851
852 if (existed)
853 *existed = exists;
854
855 if (!exists) {
856 *val = def_val;
857 return 0;
858 }
859
860 int r = stringtoull(sval, val);
861 if (r < 0)
862 return r;
863
864 return 0;
865}
866
1e59de90 867int RESTArgs::get_int64(req_state *s, const string& name,
7c673cae
FG
868 int64_t def_val, int64_t *val, bool *existed)
869{
870 bool exists;
871 string sval = s->info.args.get(name, &exists);
872
873 if (existed)
874 *existed = exists;
875
876 if (!exists) {
877 *val = def_val;
878 return 0;
879 }
880
881 int r = stringtoll(sval, val);
882 if (r < 0)
883 return r;
884
885 return 0;
886}
887
1e59de90 888int RESTArgs::get_uint32(req_state *s, const string& name,
7c673cae
FG
889 uint32_t def_val, uint32_t *val, bool *existed)
890{
891 bool exists;
892 string sval = s->info.args.get(name, &exists);
893
894 if (existed)
895 *existed = exists;
896
897 if (!exists) {
898 *val = def_val;
899 return 0;
900 }
901
902 int r = stringtoul(sval, val);
903 if (r < 0)
904 return r;
905
906 return 0;
907}
908
1e59de90 909int RESTArgs::get_int32(req_state *s, const string& name,
7c673cae
FG
910 int32_t def_val, int32_t *val, bool *existed)
911{
912 bool exists;
913 string sval = s->info.args.get(name, &exists);
914
915 if (existed)
916 *existed = exists;
917
918 if (!exists) {
919 *val = def_val;
920 return 0;
921 }
922
923 int r = stringtol(sval, val);
924 if (r < 0)
925 return r;
926
927 return 0;
928}
929
1e59de90 930int RESTArgs::get_time(req_state *s, const string& name,
7c673cae
FG
931 const utime_t& def_val, utime_t *val, bool *existed)
932{
933 bool exists;
934 string sval = s->info.args.get(name, &exists);
935
936 if (existed)
937 *existed = exists;
938
939 if (!exists) {
940 *val = def_val;
941 return 0;
942 }
943
944 uint64_t epoch, nsec;
945
946 int r = utime_t::parse_date(sval, &epoch, &nsec);
947 if (r < 0)
948 return r;
949
950 *val = utime_t(epoch, nsec);
951
952 return 0;
953}
954
1e59de90 955int RESTArgs::get_epoch(req_state *s, const string& name, uint64_t def_val, uint64_t *epoch, bool *existed)
7c673cae
FG
956{
957 bool exists;
958 string date = s->info.args.get(name, &exists);
959
960 if (existed)
961 *existed = exists;
962
963 if (!exists) {
964 *epoch = def_val;
965 return 0;
966 }
967
968 int r = utime_t::parse_date(date, epoch, NULL);
969 if (r < 0)
970 return r;
971
972 return 0;
973}
974
1e59de90 975int RESTArgs::get_bool(req_state *s, const string& name, bool def_val, bool *val, bool *existed)
7c673cae
FG
976{
977 bool exists;
978 string sval = s->info.args.get(name, &exists);
979
980 if (existed)
981 *existed = exists;
982
983 if (!exists) {
984 *val = def_val;
985 return 0;
986 }
987
988 const char *str = sval.c_str();
989
990 if (sval.empty() ||
991 strcasecmp(str, "true") == 0 ||
992 sval.compare("1") == 0) {
993 *val = true;
994 return 0;
995 }
996
997 if (strcasecmp(str, "false") != 0 &&
998 sval.compare("0") != 0) {
999 *val = def_val;
1000 return -EINVAL;
1001 }
1002
1003 *val = false;
1004 return 0;
1005}
1006
1007
1008void RGWRESTFlusher::do_start(int ret)
1009{
1010 set_req_state_err(s, ret); /* no going back from here */
1011 dump_errno(s);
1012 dump_start(s);
1013 end_header(s, op);
1014 rgw_flush_formatter_and_reset(s, s->formatter);
1015}
1016
1017void RGWRESTFlusher::do_flush()
1018{
1019 rgw_flush_formatter(s, s->formatter);
1020}
1021
1022int RGWPutObj_ObjStore::verify_params()
1023{
1024 if (s->length) {
1025 off_t len = atoll(s->length);
1026 if (len > (off_t)(s->cct->_conf->rgw_max_put_size)) {
1027 return -ERR_TOO_LARGE;
1028 }
1029 }
1030
1031 return 0;
1032}
1033
f67539c2 1034int RGWPutObj_ObjStore::get_params(optional_yield y)
7c673cae
FG
1035{
1036 /* start gettorrent */
1037 if (s->cct->_conf->rgw_torrent_flag)
1038 {
1039 int ret = 0;
1040 ret = torrent.get_params();
b3b6e05e 1041 ldpp_dout(s, 5) << "NOTICE: open produce torrent file " << dendl;
7c673cae
FG
1042 if (ret < 0)
1043 {
1044 return ret;
1045 }
f67539c2 1046 torrent.set_info_name(s->object->get_name());
7c673cae
FG
1047 }
1048 /* end gettorrent */
1049 supplied_md5_b64 = s->info.env->get("HTTP_CONTENT_MD5");
1050
1051 return 0;
1052}
1053
7c673cae
FG
1054int RGWPutObj_ObjStore::get_data(bufferlist& bl)
1055{
1056 size_t cl;
1057 uint64_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
1058 if (s->length) {
1059 cl = atoll(s->length) - ofs;
1060 if (cl > chunk_size)
1061 cl = chunk_size;
1062 } else {
1063 cl = chunk_size;
1064 }
1065
1066 int len = 0;
b5b8bbf5 1067 {
7c673cae
FG
1068 ACCOUNTING_IO(s)->set_account(true);
1069 bufferptr bp(cl);
1070
1071 const auto read_len = recv_body(s, bp.c_str(), cl);
1072 if (read_len < 0) {
1073 return read_len;
1074 }
1075
1076 len = read_len;
1077 bl.append(bp, 0, len);
1078
7c673cae
FG
1079 ACCOUNTING_IO(s)->set_account(false);
1080 }
1081
1082 if ((uint64_t)ofs + len > s->cct->_conf->rgw_max_put_size) {
1083 return -ERR_TOO_LARGE;
1084 }
1085
7c673cae
FG
1086 return len;
1087}
1088
1089
1090/*
1091 * parses params in the format: 'first; param1=foo; param2=bar'
1092 */
1093void RGWPostObj_ObjStore::parse_boundary_params(const std::string& params_str,
1094 std::string& first,
1095 std::map<std::string,
1096 std::string>& params)
1097{
1098 size_t pos = params_str.find(';');
1099 if (std::string::npos == pos) {
1100 first = rgw_trim_whitespace(params_str);
1101 return;
1102 }
1103
1104 first = rgw_trim_whitespace(params_str.substr(0, pos));
1105 pos++;
1106
1107 while (pos < params_str.size()) {
1108 size_t end = params_str.find(';', pos);
1109 if (std::string::npos == end) {
1110 end = params_str.size();
1111 }
1112
1113 std::string param = params_str.substr(pos, end - pos);
1114 size_t eqpos = param.find('=');
1115
1116 if (std::string::npos != eqpos) {
1117 std::string param_name = rgw_trim_whitespace(param.substr(0, eqpos));
1118 std::string val = rgw_trim_quotes(param.substr(eqpos + 1));
1119 params[std::move(param_name)] = std::move(val);
1120 } else {
1121 params[rgw_trim_whitespace(param)] = "";
1122 }
1123
1124 pos = end + 1;
1125 }
1126}
1127
1128int RGWPostObj_ObjStore::parse_part_field(const std::string& line,
1129 std::string& field_name, /* out */
1130 post_part_field& field) /* out */
1131{
1132 size_t pos = line.find(':');
1133 if (pos == string::npos)
1134 return -EINVAL;
1135
1136 field_name = line.substr(0, pos);
1137 if (pos >= line.size() - 1)
1138 return 0;
1139
1140 parse_boundary_params(line.substr(pos + 1), field.val, field.params);
1141
1142 return 0;
1143}
1144
1145static bool is_crlf(const char *s)
1146{
1147 return (*s == '\r' && *(s + 1) == '\n');
1148}
1149
1150/*
1151 * find the index of the boundary, if exists, or optionally the next end of line
1152 * also returns how many bytes to skip
1153 */
1154static int index_of(ceph::bufferlist& bl,
1155 uint64_t max_len,
1156 const std::string& str,
1157 const bool check_crlf,
1158 bool& reached_boundary,
1159 int& skip)
1160{
1161 reached_boundary = false;
1162 skip = 0;
1163
1164 if (str.size() < 2) // we assume boundary is at least 2 chars (makes it easier with crlf checks)
1165 return -EINVAL;
1166
1167 if (bl.length() < str.size())
1168 return -1;
1169
1170 const char *buf = bl.c_str();
1171 const char *s = str.c_str();
1172
1173 if (max_len > bl.length())
1174 max_len = bl.length();
1175
1176 for (uint64_t i = 0; i < max_len; i++, buf++) {
1177 if (check_crlf &&
1178 i >= 1 &&
1179 is_crlf(buf - 1)) {
1180 return i + 1; // skip the crlf
1181 }
1182 if ((i < max_len - str.size() + 1) &&
1183 (buf[0] == s[0] && buf[1] == s[1]) &&
1184 (strncmp(buf, s, str.size()) == 0)) {
1185 reached_boundary = true;
1186 skip = str.size();
1187
1188 /* oh, great, now we need to swallow the preceding crlf
1189 * if exists
1190 */
1191 if ((i >= 2) &&
1192 is_crlf(buf - 2)) {
1193 i -= 2;
1194 skip += 2;
1195 }
1196 return i;
1197 }
1198 }
1199
1200 return -1;
1201}
1202
1203int RGWPostObj_ObjStore::read_with_boundary(ceph::bufferlist& bl,
1204 uint64_t max,
1205 const bool check_crlf,
1206 bool& reached_boundary,
1207 bool& done)
1208{
1209 uint64_t cl = max + 2 + boundary.size();
1210
1211 if (max > in_data.length()) {
1212 uint64_t need_to_read = cl - in_data.length();
1213
1214 bufferptr bp(need_to_read);
1215
1216 const auto read_len = recv_body(s, bp.c_str(), need_to_read);
1217 if (read_len < 0) {
1218 return read_len;
1219 }
1220 in_data.append(bp, 0, read_len);
1221 }
1222
1223 done = false;
1224 int skip;
1225 const int index = index_of(in_data, cl, boundary, check_crlf,
1226 reached_boundary, skip);
1227 if (index >= 0) {
1228 max = index;
1229 }
1230
1231 if (max > in_data.length()) {
1232 max = in_data.length();
1233 }
1234
1235 bl.substr_of(in_data, 0, max);
1236
1237 ceph::bufferlist new_read_data;
1238
1239 /*
1240 * now we need to skip boundary for next time, also skip any crlf, or
1241 * check to see if it's the last final boundary (marked with "--" at the end
1242 */
1243 if (reached_boundary) {
1244 int left = in_data.length() - max;
1245 if (left < skip + 2) {
1246 int need = skip + 2 - left;
1247 bufferptr boundary_bp(need);
1248 const int r = recv_body(s, boundary_bp.c_str(), need);
1249 if (r < 0) {
1250 return r;
1251 }
1252 in_data.append(boundary_bp);
1253 }
1254 max += skip; // skip boundary for next time
1255 if (in_data.length() >= max + 2) {
1256 const char *data = in_data.c_str();
1257 if (is_crlf(data + max)) {
1258 max += 2;
1259 } else {
1260 if (*(data + max) == '-' &&
1261 *(data + max + 1) == '-') {
1262 done = true;
1263 max += 2;
1264 }
1265 }
1266 }
1267 }
1268
1269 new_read_data.substr_of(in_data, max, in_data.length() - max);
1270 in_data = new_read_data;
1271
1272 return 0;
1273}
1274
1275int RGWPostObj_ObjStore::read_line(ceph::bufferlist& bl,
1276 const uint64_t max,
1277 bool& reached_boundary,
1278 bool& done)
1279{
1280 return read_with_boundary(bl, max, true, reached_boundary, done);
1281}
1282
1283int RGWPostObj_ObjStore::read_data(ceph::bufferlist& bl,
1284 const uint64_t max,
1285 bool& reached_boundary,
1286 bool& done)
1287{
1288 return read_with_boundary(bl, max, false, reached_boundary, done);
1289}
1290
1291
1292int RGWPostObj_ObjStore::read_form_part_header(struct post_form_part* const part,
1293 bool& done)
1294{
1295 bufferlist bl;
1296 bool reached_boundary;
1297 uint64_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
1298 int r = read_line(bl, chunk_size, reached_boundary, done);
1299 if (r < 0) {
1300 return r;
1301 }
1302
1303 if (done) {
1304 return 0;
1305 }
1306
1307 if (reached_boundary) { // skip the first boundary
1308 r = read_line(bl, chunk_size, reached_boundary, done);
1309 if (r < 0) {
1310 return r;
1311 } else if (done) {
1312 return 0;
1313 }
1314 }
1315
1316 while (true) {
1317 /*
1318 * iterate through fields
1319 */
1320 std::string line = rgw_trim_whitespace(string(bl.c_str(), bl.length()));
1321
1322 if (line.empty()) {
1323 break;
1324 }
1325
1326 struct post_part_field field;
1327
1328 string field_name;
1329 r = parse_part_field(line, field_name, field);
1330 if (r < 0) {
1331 return r;
1332 }
1333
1334 part->fields[field_name] = field;
1335
1336 if (stringcasecmp(field_name, "Content-Disposition") == 0) {
1337 part->name = field.params["name"];
1338 }
1339
1340 if (reached_boundary) {
1341 break;
1342 }
1343
1344 r = read_line(bl, chunk_size, reached_boundary, done);
11fdf7f2
TL
1345 if (r < 0) {
1346 return r;
1347 }
7c673cae
FG
1348 }
1349
1350 return 0;
1351}
1352
1353bool RGWPostObj_ObjStore::part_str(parts_collection_t& parts,
1354 const std::string& name,
1355 std::string* val)
1356{
1357 const auto iter = parts.find(name);
1358 if (std::end(parts) == iter) {
1359 return false;
1360 }
1361
1362 ceph::bufferlist& data = iter->second.data;
1363 std::string str = string(data.c_str(), data.length());
1364 *val = rgw_trim_whitespace(str);
1365 return true;
1366}
1367
1368std::string RGWPostObj_ObjStore::get_part_str(parts_collection_t& parts,
1369 const std::string& name,
1370 const std::string& def_val)
1371{
1372 std::string val;
1373
1374 if (part_str(parts, name, &val)) {
1375 return val;
1376 } else {
1377 return rgw_trim_whitespace(def_val);
1378 }
1379}
1380
1381bool RGWPostObj_ObjStore::part_bl(parts_collection_t& parts,
1382 const std::string& name,
1383 ceph::bufferlist* pbl)
1384{
1385 const auto iter = parts.find(name);
1386 if (std::end(parts) == iter) {
1387 return false;
1388 }
1389
1390 *pbl = iter->second.data;
1391 return true;
1392}
1393
1394int RGWPostObj_ObjStore::verify_params()
1395{
1396 /* check that we have enough memory to store the object
1397 note that this test isn't exact and may fail unintentionally
1398 for large requests is */
1399 if (!s->length) {
1400 return -ERR_LENGTH_REQUIRED;
1401 }
1402 off_t len = atoll(s->length);
1403 if (len > (off_t)(s->cct->_conf->rgw_max_put_size)) {
1404 return -ERR_TOO_LARGE;
1405 }
1406
224ce89b
WB
1407 supplied_md5_b64 = s->info.env->get("HTTP_CONTENT_MD5");
1408
7c673cae
FG
1409 return 0;
1410}
1411
f67539c2 1412int RGWPostObj_ObjStore::get_params(optional_yield y)
7c673cae
FG
1413{
1414 if (s->expect_cont) {
1415 /* OK, here it really gets ugly. With POST, the params are embedded in the
1416 * request body, so we need to continue before being able to actually look
1417 * at them. This diverts from the usual request flow. */
1418 dump_continue(s);
1419 s->expect_cont = false;
1420 }
1421
1422 std::string req_content_type_str = s->info.env->get("CONTENT_TYPE", "");
1423 std::string req_content_type;
1424 std::map<std::string, std::string> params;
1425 parse_boundary_params(req_content_type_str, req_content_type, params);
1426
1427 if (req_content_type.compare("multipart/form-data") != 0) {
1428 err_msg = "Request Content-Type is not multipart/form-data";
1429 return -EINVAL;
1430 }
1431
11fdf7f2 1432 if (s->cct->_conf->subsys.should_gather<ceph_subsys_rgw, 20>()) {
b3b6e05e 1433 ldpp_dout(s, 20) << "request content_type_str="
7c673cae 1434 << req_content_type_str << dendl;
b3b6e05e 1435 ldpp_dout(s, 20) << "request content_type params:" << dendl;
7c673cae
FG
1436
1437 for (const auto& pair : params) {
b3b6e05e 1438 ldpp_dout(s, 20) << " " << pair.first << " -> " << pair.second
7c673cae
FG
1439 << dendl;
1440 }
1441 }
1442
1443 const auto iter = params.find("boundary");
1444 if (std::end(params) == iter) {
1445 err_msg = "Missing multipart boundary specification";
1446 return -EINVAL;
1447 }
1448
1449 /* Create the boundary. */
1450 boundary = "--";
1451 boundary.append(iter->second);
1452
1453 return 0;
1454}
1455
1456
f67539c2 1457int RGWPutACLs_ObjStore::get_params(optional_yield y)
7c673cae
FG
1458{
1459 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
20effc67 1460 std::tie(op_ret, data) = read_all_input(s, max_size, false);
b3b6e05e 1461 ldpp_dout(s, 0) << "RGWPutACLs_ObjStore::get_params read data is: " << data.c_str() << dendl;
7c673cae
FG
1462 return op_ret;
1463}
1464
f67539c2 1465int RGWPutLC_ObjStore::get_params(optional_yield y)
7c673cae
FG
1466{
1467 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
20effc67 1468 std::tie(op_ret, data) = read_all_input(s, max_size, false);
7c673cae
FG
1469 return op_ret;
1470}
1471
f67539c2 1472int RGWPutBucketObjectLock_ObjStore::get_params(optional_yield y)
eafe8130
TL
1473{
1474 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
20effc67 1475 std::tie(op_ret, data) = read_all_input(s, max_size, false);
eafe8130
TL
1476 return op_ret;
1477}
1478
f67539c2 1479int RGWPutObjLegalHold_ObjStore::get_params(optional_yield y)
eafe8130
TL
1480{
1481 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
20effc67 1482 std::tie(op_ret, data) = read_all_input(s, max_size, false);
eafe8130
TL
1483 return op_ret;
1484}
1485
1486
11fdf7f2 1487static std::tuple<int, bufferlist> read_all_chunked_input(req_state *s, const uint64_t max_read)
7c673cae
FG
1488{
1489#define READ_CHUNK 4096
1490#define MAX_READ_CHUNK (128 * 1024)
1491 int need_to_read = READ_CHUNK;
1492 int total = need_to_read;
11fdf7f2 1493 bufferlist bl;
7c673cae 1494
20effc67 1495 int read_len = 0;
7c673cae 1496 do {
11fdf7f2
TL
1497 bufferptr bp(need_to_read + 1);
1498 read_len = recv_body(s, bp.c_str(), need_to_read);
7c673cae 1499 if (read_len < 0) {
11fdf7f2 1500 return std::make_tuple(read_len, std::move(bl));
7c673cae
FG
1501 }
1502
11fdf7f2
TL
1503 bp.c_str()[read_len] = '\0';
1504 bp.set_length(read_len);
1505 bl.append(bp);
7c673cae
FG
1506
1507 if (read_len == need_to_read) {
1508 if (need_to_read < MAX_READ_CHUNK)
1509 need_to_read *= 2;
1510
1511 if ((unsigned)total > max_read) {
11fdf7f2 1512 return std::make_tuple(-ERANGE, std::move(bl));
7c673cae
FG
1513 }
1514 total += need_to_read;
7c673cae
FG
1515 } else {
1516 break;
1517 }
7c673cae 1518 } while (true);
7c673cae 1519
11fdf7f2 1520 return std::make_tuple(0, std::move(bl));
7c673cae
FG
1521}
1522
1e59de90 1523std::tuple<int, bufferlist > rgw_rest_read_all_input(req_state *s,
11fdf7f2
TL
1524 const uint64_t max_len,
1525 const bool allow_chunked)
7c673cae
FG
1526{
1527 size_t cl = 0;
1528 int len = 0;
11fdf7f2 1529 bufferlist bl;
7c673cae
FG
1530
1531 if (s->length)
1532 cl = atoll(s->length);
1533 else if (!allow_chunked)
11fdf7f2 1534 return std::make_tuple(-ERR_LENGTH_REQUIRED, std::move(bl));
7c673cae
FG
1535
1536 if (cl) {
1537 if (cl > (size_t)max_len) {
11fdf7f2 1538 return std::make_tuple(-ERANGE, std::move(bl));
7c673cae 1539 }
11fdf7f2
TL
1540
1541 bufferptr bp(cl + 1);
1542
1543 len = recv_body(s, bp.c_str(), cl);
7c673cae 1544 if (len < 0) {
11fdf7f2 1545 return std::make_tuple(len, std::move(bl));
7c673cae 1546 }
11fdf7f2
TL
1547
1548 bp.c_str()[len] = '\0';
1549 bp.set_length(len);
1550 bl.append(bp);
1551
7c673cae
FG
1552 } else if (allow_chunked && !s->length) {
1553 const char *encoding = s->info.env->get("HTTP_TRANSFER_ENCODING");
1554 if (!encoding || strcmp(encoding, "chunked") != 0)
11fdf7f2 1555 return std::make_tuple(-ERR_LENGTH_REQUIRED, std::move(bl));
7c673cae 1556
11fdf7f2
TL
1557 int ret = 0;
1558 std::tie(ret, bl) = read_all_chunked_input(s, max_len);
7c673cae 1559 if (ret < 0)
11fdf7f2 1560 return std::make_tuple(ret, std::move(bl));
7c673cae
FG
1561 }
1562
11fdf7f2 1563 return std::make_tuple(0, std::move(bl));
7c673cae
FG
1564}
1565
f67539c2 1566int RGWCompleteMultipart_ObjStore::get_params(optional_yield y)
7c673cae
FG
1567{
1568 upload_id = s->info.args.get("uploadId");
1569
1570 if (upload_id.empty()) {
1571 op_ret = -ENOTSUP;
1572 return op_ret;
1573 }
1574
9f95a23c 1575 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
20effc67 1576 std::tie(op_ret, data) = read_all_input(s, max_size);
7c673cae
FG
1577 if (op_ret < 0)
1578 return op_ret;
1579
1580 return 0;
1581}
1582
f67539c2 1583int RGWListMultipart_ObjStore::get_params(optional_yield y)
7c673cae
FG
1584{
1585 upload_id = s->info.args.get("uploadId");
1586
1587 if (upload_id.empty()) {
1588 op_ret = -ENOTSUP;
1589 }
1590 string marker_str = s->info.args.get("part-number-marker");
1591
1592 if (!marker_str.empty()) {
1593 string err;
1594 marker = strict_strtol(marker_str.c_str(), 10, &err);
1595 if (!err.empty()) {
b3b6e05e 1596 ldpp_dout(s, 20) << "bad marker: " << marker << dendl;
7c673cae
FG
1597 op_ret = -EINVAL;
1598 return op_ret;
1599 }
1600 }
1601
1602 string str = s->info.args.get("max-parts");
f64942e4 1603 op_ret = parse_value_and_bound(str, max_parts, 0,
11fdf7f2
TL
1604 g_conf().get_val<uint64_t>("rgw_max_listing_results"),
1605 max_parts);
7c673cae
FG
1606
1607 return op_ret;
1608}
1609
f67539c2 1610int RGWListBucketMultiparts_ObjStore::get_params(optional_yield y)
7c673cae
FG
1611{
1612 delimiter = s->info.args.get("delimiter");
1613 prefix = s->info.args.get("prefix");
94b18763 1614 string str = s->info.args.get("max-uploads");
f64942e4 1615 op_ret = parse_value_and_bound(str, max_uploads, 0,
11fdf7f2 1616 g_conf().get_val<uint64_t>("rgw_max_listing_results"),
f64942e4
AA
1617 default_max);
1618 if (op_ret < 0) {
1619 return op_ret;
1620 }
7c673cae 1621
9f95a23c
TL
1622 if (auto encoding_type = s->info.args.get_optional("encoding-type");
1623 encoding_type != boost::none) {
1624 if (strcasecmp(encoding_type->c_str(), "url") != 0) {
1625 op_ret = -EINVAL;
1626 s->err.message="Invalid Encoding Method specified in Request";
1627 return op_ret;
1628 }
1629 encode_url = true;
1630 }
1631
7c673cae
FG
1632 string key_marker = s->info.args.get("key-marker");
1633 string upload_id_marker = s->info.args.get("upload-id-marker");
20effc67
TL
1634 if (!key_marker.empty()) {
1635 std::unique_ptr<rgw::sal::MultipartUpload> upload;
1636 upload = s->bucket->get_multipart_upload(key_marker,
1637 upload_id_marker);
1638 marker_meta = upload->get_meta();
1639 marker_key = upload->get_key();
1640 marker_upload_id = upload->get_upload_id();
1641 }
7c673cae
FG
1642
1643 return 0;
1644}
1645
f67539c2 1646int RGWDeleteMultiObj_ObjStore::get_params(optional_yield y)
7c673cae
FG
1647{
1648
1649 if (s->bucket_name.empty()) {
1650 op_ret = -EINVAL;
1651 return op_ret;
1652 }
1653
1654 // everything is probably fine, set the bucket
f67539c2 1655 bucket = s->bucket.get();
7c673cae
FG
1656
1657 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
20effc67 1658 std::tie(op_ret, data) = read_all_input(s, max_size, false);
7c673cae
FG
1659 return op_ret;
1660}
1661
1662
1663void RGWRESTOp::send_response()
1664{
1665 if (!flusher.did_start()) {
f67539c2 1666 set_req_state_err(s, get_ret());
7c673cae
FG
1667 dump_errno(s);
1668 end_header(s, this);
1669 }
1670 flusher.flush();
1671}
1672
f67539c2 1673int RGWRESTOp::verify_permission(optional_yield)
7c673cae 1674{
9f95a23c 1675 return check_caps(s->user->get_info().caps);
7c673cae
FG
1676}
1677
9f95a23c 1678RGWOp* RGWHandler_REST::get_op(void)
7c673cae
FG
1679{
1680 RGWOp *op;
1681 switch (s->op) {
1682 case OP_GET:
1683 op = op_get();
1684 break;
1685 case OP_PUT:
1686 op = op_put();
1687 break;
1688 case OP_DELETE:
1689 op = op_delete();
1690 break;
1691 case OP_HEAD:
1692 op = op_head();
1693 break;
1694 case OP_POST:
1695 op = op_post();
1696 break;
1697 case OP_COPY:
1698 op = op_copy();
1699 break;
1700 case OP_OPTIONS:
1701 op = op_options();
1702 break;
1703 default:
1704 return NULL;
1705 }
1706
1707 if (op) {
1e59de90 1708 op->init(driver, s, this);
7c673cae
FG
1709 }
1710 return op;
1711} /* get_op */
1712
1713void RGWHandler_REST::put_op(RGWOp* op)
1714{
1715 delete op;
1716} /* put_op */
1717
1e59de90
TL
1718int RGWHandler_REST::allocate_formatter(req_state *s,
1719 RGWFormat default_type,
7c673cae
FG
1720 bool configurable)
1721{
1e59de90 1722 s->format = RGWFormat::BAD_FORMAT; // set to invalid value to allocation happens anyway
eafe8130 1723 auto type = default_type;
7c673cae
FG
1724 if (configurable) {
1725 string format_str = s->info.args.get("format");
1726 if (format_str.compare("xml") == 0) {
1e59de90 1727 type = RGWFormat::XML;
7c673cae 1728 } else if (format_str.compare("json") == 0) {
1e59de90 1729 type = RGWFormat::JSON;
7c673cae 1730 } else if (format_str.compare("html") == 0) {
1e59de90 1731 type = RGWFormat::HTML;
7c673cae
FG
1732 } else {
1733 const char *accept = s->info.env->get("HTTP_ACCEPT");
1734 if (accept) {
1e59de90
TL
1735 // trim at first ;
1736 std::string_view format = accept;
1737 format = format.substr(0, format.find(';'));
1738
1739 if (format == "text/xml" || format == "application/xml") {
1740 type = RGWFormat::XML;
1741 } else if (format == "application/json") {
1742 type = RGWFormat::JSON;
1743 } else if (format == "text/html") {
1744 type = RGWFormat::HTML;
7c673cae
FG
1745 }
1746 }
1747 }
1748 }
eafe8130
TL
1749 return RGWHandler_REST::reallocate_formatter(s, type);
1750}
1751
1e59de90 1752int RGWHandler_REST::reallocate_formatter(req_state *s, const RGWFormat type)
eafe8130
TL
1753{
1754 if (s->format == type) {
1755 // do nothing, just reset
1756 ceph_assert(s->formatter);
1757 s->formatter->reset();
1758 return 0;
1759 }
1760
1761 delete s->formatter;
1762 s->formatter = nullptr;
1763 s->format = type;
7c673cae
FG
1764
1765 const string& mm = s->info.args.get("multipart-manifest");
1766 const bool multipart_delete = (mm.compare("delete") == 0);
1767 const bool swift_bulkupload = s->prot_flags & RGW_REST_SWIFT &&
1768 s->info.args.exists("extract-archive");
1769 switch (s->format) {
1e59de90 1770 case RGWFormat::PLAIN:
7c673cae
FG
1771 {
1772 const bool use_kv_syntax = s->info.args.exists("bulk-delete") ||
1773 multipart_delete || swift_bulkupload;
1774 s->formatter = new RGWFormatter_Plain(use_kv_syntax);
1775 break;
1776 }
1e59de90 1777 case RGWFormat::XML:
7c673cae
FG
1778 {
1779 const bool lowercase_underscore = s->info.args.exists("bulk-delete") ||
1780 multipart_delete || swift_bulkupload;
1781
1782 s->formatter = new XMLFormatter(false, lowercase_underscore);
1783 break;
1784 }
1e59de90 1785 case RGWFormat::JSON:
7c673cae
FG
1786 s->formatter = new JSONFormatter(false);
1787 break;
1e59de90 1788 case RGWFormat::HTML:
7c673cae
FG
1789 s->formatter = new HTMLFormatter(s->prot_flags & RGW_REST_WEBSITE);
1790 break;
1791 default:
1792 return -EINVAL;
1793
1794 };
1795 //s->formatter->reset(); // All formatters should reset on create already
1796
1797 return 0;
1798}
7c673cae
FG
1799// This function enforces Amazon's spec for bucket names.
1800// (The requirements, not the recommendations.)
1801int RGWHandler_REST::validate_bucket_name(const string& bucket)
1802{
1803 int len = bucket.size();
1804 if (len < 3) {
1805 if (len == 0) {
1806 // This request doesn't specify a bucket at all
1807 return 0;
1808 }
1809 // Name too short
1810 return -ERR_INVALID_BUCKET_NAME;
1811 }
1812 else if (len > MAX_BUCKET_NAME_LEN) {
1813 // Name too long
1814 return -ERR_INVALID_BUCKET_NAME;
1815 }
1816
11fdf7f2
TL
1817 const char *s = bucket.c_str();
1818 for (int i = 0; i < len; ++i, ++s) {
1819 if (*(unsigned char *)s == 0xff)
1820 return -ERR_INVALID_BUCKET_NAME;
1821 if (*(unsigned char *)s == '/')
1822 return -ERR_INVALID_BUCKET_NAME;
1823 }
1824
7c673cae
FG
1825 return 0;
1826}
1827
1828// "The name for a key is a sequence of Unicode characters whose UTF-8 encoding
1829// is at most 1024 bytes long."
1830// However, we can still have control characters and other nasties in there.
1831// Just as long as they're utf-8 nasties.
1832int RGWHandler_REST::validate_object_name(const string& object)
1833{
1834 int len = object.size();
1835 if (len > MAX_OBJ_NAME_LEN) {
1836 // Name too long
1837 return -ERR_INVALID_OBJECT_NAME;
1838 }
1839
1840 if (check_utf8(object.c_str(), len)) {
1841 // Object names must be valid UTF-8.
1842 return -ERR_INVALID_OBJECT_NAME;
1843 }
1844 return 0;
1845}
1846
1847static http_op op_from_method(const char *method)
1848{
1849 if (!method)
1850 return OP_UNKNOWN;
1851 if (strcmp(method, "GET") == 0)
1852 return OP_GET;
1853 if (strcmp(method, "PUT") == 0)
1854 return OP_PUT;
1855 if (strcmp(method, "DELETE") == 0)
1856 return OP_DELETE;
1857 if (strcmp(method, "HEAD") == 0)
1858 return OP_HEAD;
1859 if (strcmp(method, "POST") == 0)
1860 return OP_POST;
1861 if (strcmp(method, "COPY") == 0)
1862 return OP_COPY;
1863 if (strcmp(method, "OPTIONS") == 0)
1864 return OP_OPTIONS;
1865
1866 return OP_UNKNOWN;
1867}
1868
f67539c2 1869int RGWHandler_REST::init_permissions(RGWOp* op, optional_yield y)
7c673cae 1870{
11fdf7f2
TL
1871 if (op->get_type() == RGW_OP_CREATE_BUCKET) {
1872 // We don't need user policies in case of STS token returned by AssumeRole, hence the check for user type
9f95a23c 1873 if (! s->user->get_id().empty() && s->auth.identity->get_identity_type() != TYPE_ROLE) {
11fdf7f2 1874 try {
20effc67
TL
1875 if (auto ret = s->user->read_attrs(s, y); ! ret) {
1876 auto user_policies = get_iam_user_policy_from_attr(s->cct, s->user->get_attrs(), s->user->get_tenant());
f67539c2
TL
1877 s->iam_user_policies.insert(s->iam_user_policies.end(),
1878 std::make_move_iterator(user_policies.begin()),
1879 std::make_move_iterator(user_policies.end()));
1880
11fdf7f2
TL
1881 }
1882 } catch (const std::exception& e) {
b3b6e05e 1883 ldpp_dout(op, -1) << "Error reading IAM User Policy: " << e.what() << dendl;
11fdf7f2
TL
1884 }
1885 }
1e59de90 1886 rgw_build_iam_environment(driver, s);
7c673cae 1887 return 0;
11fdf7f2 1888 }
7c673cae 1889
b3b6e05e 1890 return do_init_permissions(op, y);
7c673cae
FG
1891}
1892
f67539c2 1893int RGWHandler_REST::read_permissions(RGWOp* op_obj, optional_yield y)
7c673cae 1894{
224ce89b 1895 bool only_bucket = false;
7c673cae
FG
1896
1897 switch (s->op) {
1898 case OP_HEAD:
1899 case OP_GET:
1900 only_bucket = false;
1901 break;
1902 case OP_PUT:
1903 case OP_POST:
1904 case OP_COPY:
1905 /* is it a 'multi-object delete' request? */
1906 if (s->info.args.exists("delete")) {
1907 only_bucket = true;
1908 break;
1909 }
1910 if (is_obj_update_op()) {
1911 only_bucket = false;
1912 break;
1913 }
1914 /* is it a 'create bucket' request? */
1915 if (op_obj->get_type() == RGW_OP_CREATE_BUCKET)
1916 return 0;
f67539c2 1917
7c673cae
FG
1918 only_bucket = true;
1919 break;
1920 case OP_DELETE:
224ce89b
WB
1921 if (!s->info.args.exists("tagging")){
1922 only_bucket = true;
1923 }
7c673cae
FG
1924 break;
1925 case OP_OPTIONS:
1926 only_bucket = true;
1927 break;
1928 default:
1929 return -EINVAL;
1930 }
1931
f67539c2 1932 return do_read_permissions(op_obj, only_bucket, y);
7c673cae
FG
1933}
1934
1935void RGWRESTMgr::register_resource(string resource, RGWRESTMgr *mgr)
1936{
1937 string r = "/";
1938 r.append(resource);
1939
1940 /* do we have a resource manager registered for this entry point? */
1941 map<string, RGWRESTMgr *>::iterator iter = resource_mgrs.find(r);
1942 if (iter != resource_mgrs.end()) {
1943 delete iter->second;
1944 }
1945 resource_mgrs[r] = mgr;
1946 resources_by_size.insert(pair<size_t, string>(r.size(), r));
1947
1948 /* now build default resource managers for the path (instead of nested entry points)
1949 * e.g., if the entry point is /auth/v1.0/ then we'd want to create a default
1950 * manager for /auth/
1951 */
1952
1953 size_t pos = r.find('/', 1);
1954
1955 while (pos != r.size() - 1 && pos != string::npos) {
1956 string s = r.substr(0, pos);
1957
1958 iter = resource_mgrs.find(s);
1959 if (iter == resource_mgrs.end()) { /* only register it if one does not exist */
1960 resource_mgrs[s] = new RGWRESTMgr; /* a default do-nothing manager */
1961 resources_by_size.insert(pair<size_t, string>(s.size(), s));
1962 }
1963
1964 pos = r.find('/', pos + 1);
1965 }
1966}
1967
1968void RGWRESTMgr::register_default_mgr(RGWRESTMgr *mgr)
1969{
1970 delete default_mgr;
1971 default_mgr = mgr;
1972}
1973
1e59de90 1974RGWRESTMgr* RGWRESTMgr::get_resource_mgr(req_state* const s,
7c673cae
FG
1975 const std::string& uri,
1976 std::string* const out_uri)
1977{
1978 *out_uri = uri;
1979
1980 multimap<size_t, string>::reverse_iterator iter;
1981
1982 for (iter = resources_by_size.rbegin(); iter != resources_by_size.rend(); ++iter) {
1983 string& resource = iter->second;
1984 if (uri.compare(0, iter->first, resource) == 0 &&
1985 (uri.size() == iter->first ||
1986 uri[iter->first] == '/')) {
1987 std::string suffix = uri.substr(iter->first);
1988 return resource_mgrs[resource]->get_resource_mgr(s, suffix, out_uri);
1989 }
1990 }
1991
1992 if (default_mgr) {
1993 return default_mgr->get_resource_mgr_as_default(s, uri, out_uri);
1994 }
1995
1996 return this;
1997}
1998
1999void RGWREST::register_x_headers(const string& s_headers)
2000{
2001 std::vector<std::string> hdrs = get_str_vec(s_headers);
2002 for (auto& hdr : hdrs) {
2003 boost::algorithm::to_upper(hdr); // XXX
2004 (void) x_headers.insert(hdr);
2005 }
2006}
2007
2008RGWRESTMgr::~RGWRESTMgr()
2009{
2010 map<string, RGWRESTMgr *>::iterator iter;
2011 for (iter = resource_mgrs.begin(); iter != resource_mgrs.end(); ++iter) {
2012 delete iter->second;
2013 }
2014 delete default_mgr;
2015}
2016
31f18b77 2017int64_t parse_content_length(const char *content_length)
7c673cae
FG
2018{
2019 int64_t len = -1;
2020
2021 if (*content_length == '\0') {
2022 len = 0;
2023 } else {
2024 string err;
2025 len = strict_strtoll(content_length, 10, &err);
2026 if (!err.empty()) {
2027 len = -1;
2028 }
2029 }
2030
2031 return len;
2032}
2033
1e59de90 2034int RGWREST::preprocess(req_state *s, rgw::io::BasicClient* cio)
7c673cae
FG
2035{
2036 req_info& info = s->info;
2037
2038 /* save the request uri used to hash on the client side. request_uri may suffer
2039 modifications as part of the bucket encoding in the subdomain calling format.
2040 request_uri_aws4 will be used under aws4 auth */
2041 s->info.request_uri_aws4 = s->info.request_uri;
2042
2043 s->cio = cio;
2044
2045 // We need to know if this RGW instance is running the s3website API with a
2046 // higher priority than regular S3 API, or possibly in place of the regular
2047 // S3 API.
2048 // Map the listing of rgw_enable_apis in REVERSE order, so that items near
2049 // the front of the list have a higher number assigned (and -1 for items not in the list).
2050 list<string> apis;
11fdf7f2 2051 get_str_list(g_conf()->rgw_enable_apis, apis);
7c673cae
FG
2052 int api_priority_s3 = -1;
2053 int api_priority_s3website = -1;
2054 auto api_s3website_priority_rawpos = std::find(apis.begin(), apis.end(), "s3website");
2055 auto api_s3_priority_rawpos = std::find(apis.begin(), apis.end(), "s3");
2056 if (api_s3_priority_rawpos != apis.end()) {
2057 api_priority_s3 = apis.size() - std::distance(apis.begin(), api_s3_priority_rawpos);
2058 }
2059 if (api_s3website_priority_rawpos != apis.end()) {
2060 api_priority_s3website = apis.size() - std::distance(apis.begin(), api_s3website_priority_rawpos);
2061 }
b3b6e05e 2062 ldpp_dout(s, 10) << "rgw api priority: s3=" << api_priority_s3 << " s3website=" << api_priority_s3website << dendl;
7c673cae
FG
2063 bool s3website_enabled = api_priority_s3website >= 0;
2064
2065 if (info.host.size()) {
9f95a23c
TL
2066 ssize_t pos;
2067 if (info.host.find('[') == 0) {
2068 pos = info.host.find(']');
2069 if (pos >=1) {
2070 info.host = info.host.substr(1, pos-1);
2071 }
2072 } else {
2073 pos = info.host.find(':');
2074 if (pos >= 0) {
2075 info.host = info.host.substr(0, pos);
2076 }
7c673cae 2077 }
b3b6e05e 2078 ldpp_dout(s, 10) << "host=" << info.host << dendl;
7c673cae
FG
2079 string domain;
2080 string subdomain;
2081 bool in_hosted_domain_s3website = false;
2082 bool in_hosted_domain = rgw_find_host_in_domains(info.host, &domain, &subdomain, hostnames_set);
2083
2084 string s3website_domain;
2085 string s3website_subdomain;
2086
2087 if (s3website_enabled) {
2088 in_hosted_domain_s3website = rgw_find_host_in_domains(info.host, &s3website_domain, &s3website_subdomain, hostnames_s3website_set);
2089 if (in_hosted_domain_s3website) {
2090 in_hosted_domain = true; // TODO: should hostnames be a strict superset of hostnames_s3website?
2091 domain = s3website_domain;
2092 subdomain = s3website_subdomain;
2093 }
2094 }
2095
b3b6e05e 2096 ldpp_dout(s, 20)
7c673cae
FG
2097 << "subdomain=" << subdomain
2098 << " domain=" << domain
2099 << " in_hosted_domain=" << in_hosted_domain
2100 << " in_hosted_domain_s3website=" << in_hosted_domain_s3website
2101 << dendl;
2102
11fdf7f2 2103 if (g_conf()->rgw_resolve_cname
7c673cae
FG
2104 && !in_hosted_domain
2105 && !in_hosted_domain_s3website) {
2106 string cname;
2107 bool found;
2108 int r = rgw_resolver->resolve_cname(info.host, cname, &found);
2109 if (r < 0) {
b3b6e05e 2110 ldpp_dout(s, 0)
7c673cae
FG
2111 << "WARNING: rgw_resolver->resolve_cname() returned r=" << r
2112 << dendl;
2113 }
2114
2115 if (found) {
b3b6e05e 2116 ldpp_dout(s, 5) << "resolved host cname " << info.host << " -> "
7c673cae
FG
2117 << cname << dendl;
2118 in_hosted_domain =
2119 rgw_find_host_in_domains(cname, &domain, &subdomain, hostnames_set);
2120
2121 if (s3website_enabled
2122 && !in_hosted_domain_s3website) {
2123 in_hosted_domain_s3website =
2124 rgw_find_host_in_domains(cname, &s3website_domain,
2125 &s3website_subdomain,
2126 hostnames_s3website_set);
2127 if (in_hosted_domain_s3website) {
2128 in_hosted_domain = true; // TODO: should hostnames be a
2129 // strict superset of hostnames_s3website?
2130 domain = s3website_domain;
2131 subdomain = s3website_subdomain;
2132 }
2133 }
2134
b3b6e05e 2135 ldpp_dout(s, 20)
7c673cae
FG
2136 << "subdomain=" << subdomain
2137 << " domain=" << domain
2138 << " in_hosted_domain=" << in_hosted_domain
2139 << " in_hosted_domain_s3website=" << in_hosted_domain_s3website
2140 << dendl;
2141 }
2142 }
2143
2144 // Handle A/CNAME records that point to the RGW storage, but do match the
2145 // CNAME test above, per issue http://tracker.ceph.com/issues/15975
2146 // If BOTH domain & subdomain variables are empty, then none of the above
2147 // cases matched anything, and we should fall back to using the Host header
2148 // directly as the bucket name.
2149 // As additional checks:
2150 // - if the Host header is an IP, we're using path-style access without DNS
2151 // - Also check that the Host header is a valid bucket name before using it.
2152 // - Don't enable virtual hosting if no hostnames are configured
2153 if (subdomain.empty()
2154 && (domain.empty() || domain != info.host)
2155 && !looks_like_ip_address(info.host.c_str())
2156 && RGWHandler_REST::validate_bucket_name(info.host) == 0
2157 && !(hostnames_set.empty() && hostnames_s3website_set.empty())) {
2158 subdomain.append(info.host);
2159 in_hosted_domain = 1;
2160 }
2161
2162 if (s3website_enabled && api_priority_s3website > api_priority_s3) {
2163 in_hosted_domain_s3website = 1;
2164 }
2165
2166 if (in_hosted_domain_s3website) {
2167 s->prot_flags |= RGW_REST_WEBSITE;
2168 }
2169
2170
2171 if (in_hosted_domain && !subdomain.empty()) {
2172 string encoded_bucket = "/";
2173 encoded_bucket.append(subdomain);
2174 if (s->info.request_uri[0] != '/')
2175 encoded_bucket.append("/");
2176 encoded_bucket.append(s->info.request_uri);
2177 s->info.request_uri = encoded_bucket;
2178 }
2179
2180 if (!domain.empty()) {
2181 s->info.domain = domain;
2182 }
2183
b3b6e05e 2184 ldpp_dout(s, 20)
7c673cae
FG
2185 << "final domain/bucket"
2186 << " subdomain=" << subdomain
2187 << " domain=" << domain
2188 << " in_hosted_domain=" << in_hosted_domain
2189 << " in_hosted_domain_s3website=" << in_hosted_domain_s3website
2190 << " s->info.domain=" << s->info.domain
2191 << " s->info.request_uri=" << s->info.request_uri
2192 << dendl;
2193 }
2194
2195 if (s->info.domain.empty()) {
2196 s->info.domain = s->cct->_conf->rgw_dns_name;
2197 }
2198
31f18b77 2199 s->decoded_uri = url_decode(s->info.request_uri);
224ce89b
WB
2200 /* Validate for being free of the '\0' buried in the middle of the string. */
2201 if (std::strlen(s->decoded_uri.c_str()) != s->decoded_uri.length()) {
2202 return -ERR_ZERO_IN_URL;
2203 }
7c673cae
FG
2204
2205 /* FastCGI specification, section 6.3
2206 * http://www.fastcgi.com/devkit/doc/fcgi-spec.html#S6.3
2207 * ===
2208 * The Authorizer application receives HTTP request information from the Web
2209 * server on the FCGI_PARAMS stream, in the same format as a Responder. The
2210 * Web server does not send CONTENT_LENGTH, PATH_INFO, PATH_TRANSLATED, and
2211 * SCRIPT_NAME headers.
2212 * ===
2213 * Ergo if we are in Authorizer role, we MUST look at HTTP_CONTENT_LENGTH
2214 * instead of CONTENT_LENGTH for the Content-Length.
2215 *
2216 * There is one slight wrinkle in this, and that's older versions of
2217 * nginx/lighttpd/apache setting BOTH headers. As a result, we have to check
2218 * both headers and can't always simply pick A or B.
2219 */
2220 const char* content_length = info.env->get("CONTENT_LENGTH");
2221 const char* http_content_length = info.env->get("HTTP_CONTENT_LENGTH");
2222 if (!http_content_length != !content_length) {
2223 /* Easy case: one or the other is missing */
2224 s->length = (content_length ? content_length : http_content_length);
2225 } else if (s->cct->_conf->rgw_content_length_compat &&
2226 content_length && http_content_length) {
2227 /* Hard case: Both are set, we have to disambiguate */
2228 int64_t content_length_i, http_content_length_i;
2229
2230 content_length_i = parse_content_length(content_length);
2231 http_content_length_i = parse_content_length(http_content_length);
2232
2233 // Now check them:
2234 if (http_content_length_i < 0) {
2235 // HTTP_CONTENT_LENGTH is invalid, ignore it
2236 } else if (content_length_i < 0) {
2237 // CONTENT_LENGTH is invalid, and HTTP_CONTENT_LENGTH is valid
2238 // Swap entries
2239 content_length = http_content_length;
2240 } else {
2241 // both CONTENT_LENGTH and HTTP_CONTENT_LENGTH are valid
2242 // Let's pick the larger size
2243 if (content_length_i < http_content_length_i) {
2244 // prefer the larger value
2245 content_length = http_content_length;
2246 }
2247 }
2248 s->length = content_length;
2249 // End of: else if (s->cct->_conf->rgw_content_length_compat &&
2250 // content_length &&
2251 // http_content_length)
2252 } else {
2253 /* no content length was defined */
2254 s->length = NULL;
2255 }
2256
2257 if (s->length) {
2258 if (*s->length == '\0') {
2259 s->content_length = 0;
2260 } else {
2261 string err;
2262 s->content_length = strict_strtoll(s->length, 10, &err);
2263 if (!err.empty()) {
b3b6e05e 2264 ldpp_dout(s, 10) << "bad content length, aborting" << dendl;
7c673cae
FG
2265 return -EINVAL;
2266 }
2267 }
2268 }
2269
2270 if (s->content_length < 0) {
b3b6e05e 2271 ldpp_dout(s, 10) << "negative content length, aborting" << dendl;
7c673cae
FG
2272 return -EINVAL;
2273 }
2274
2275 map<string, string>::iterator giter;
2276 for (giter = generic_attrs_map.begin(); giter != generic_attrs_map.end();
2277 ++giter) {
2278 const char *env = info.env->get(giter->first.c_str());
2279 if (env) {
2280 s->generic_attrs[giter->second] = env;
2281 }
2282 }
2283
11fdf7f2 2284 if (g_conf()->rgw_print_continue) {
7c673cae
FG
2285 const char *expect = info.env->get("HTTP_EXPECT");
2286 s->expect_cont = (expect && !strcasecmp(expect, "100-continue"));
2287 }
2288 s->op = op_from_method(info.method);
2289
b3b6e05e 2290 info.init_meta_info(s, &s->has_bad_meta);
7c673cae
FG
2291
2292 return 0;
2293}
2294
2295RGWHandler_REST* RGWREST::get_handler(
1e59de90
TL
2296 rgw::sal::Driver* const driver,
2297 req_state* const s,
7c673cae
FG
2298 const rgw::auth::StrategyRegistry& auth_registry,
2299 const std::string& frontend_prefix,
2300 RGWRestfulIO* const rio,
2301 RGWRESTMgr** const pmgr,
2302 int* const init_error
2303) {
2304 *init_error = preprocess(s, rio);
2305 if (*init_error < 0) {
2306 return nullptr;
2307 }
2308
2309 RGWRESTMgr *m = mgr.get_manager(s, frontend_prefix, s->decoded_uri,
2310 &s->relative_uri);
2311 if (! m) {
2312 *init_error = -ERR_METHOD_NOT_ALLOWED;
2313 return nullptr;
2314 }
2315
2316 if (pmgr) {
2317 *pmgr = m;
2318 }
2319
1e59de90 2320 RGWHandler_REST* handler = m->get_handler(driver, s, auth_registry, frontend_prefix);
7c673cae
FG
2321 if (! handler) {
2322 *init_error = -ERR_METHOD_NOT_ALLOWED;
2323 return NULL;
2324 }
1e59de90
TL
2325
2326 ldpp_dout(s, 20) << __func__ << " handler=" << typeid(*handler).name() << dendl;
2327
2328 *init_error = handler->init(driver, s, rio);
7c673cae
FG
2329 if (*init_error < 0) {
2330 m->put_handler(handler);
2331 return nullptr;
2332 }
2333
2334 return handler;
2335} /* get stream handler */