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