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