int RGWREST::preprocess(struct req_state *s, rgw::io::BasicClient* cio)
{
  req_info& info = s->info;

  /* save the request uri used to hash on the client side. request_uri may suffer
     modifications as part of the bucket encoding in the subdomain calling format.
     request_uri_aws4 will be used under aws4 auth */
  s->info.request_uri_aws4 = s->info.request_uri;

  s->cio = cio;

  // We need to know if this RGW instance is running the s3website API with a
  // higher priority than regular S3 API, or possibly in place of the regular
  // S3 API.
  // Map the listing of rgw_enable_apis in REVERSE order, so that items near
  // the front of the list have a higher number assigned (and -1 for items not in the list).
  list<string> apis;
  get_str_list(g_conf->rgw_enable_apis, apis);
  int api_priority_s3 = -1;
  int api_priority_s3website = -1;
  auto api_s3website_priority_rawpos = std::find(apis.begin(), apis.end(), "s3website");
  auto api_s3_priority_rawpos = std::find(apis.begin(), apis.end(), "s3");
  if (api_s3_priority_rawpos != apis.end()) {
    api_priority_s3 = apis.size() - std::distance(apis.begin(), api_s3_priority_rawpos);
  }
  if (api_s3website_priority_rawpos != apis.end()) {
    api_priority_s3website = apis.size() - std::distance(apis.begin(), api_s3website_priority_rawpos);
  }
  ldout(s->cct, 10) << "rgw api priority: s3=" << api_priority_s3 << " s3website=" << api_priority_s3website << dendl;
  bool s3website_enabled = api_priority_s3website >= 0;

  if (info.host.size()) {
    ssize_t pos = info.host.find(':');
    if (pos >= 0) {
      info.host = info.host.substr(0, pos);
    }
    ldout(s->cct, 10) << "host=" << info.host << dendl;
    string domain;
    string subdomain;
    bool in_hosted_domain_s3website = false;
    bool in_hosted_domain = rgw_find_host_in_domains(info.host, &domain, &subdomain, hostnames_set);

    string s3website_domain;
    string s3website_subdomain;

    if (s3website_enabled) {
      in_hosted_domain_s3website = rgw_find_host_in_domains(info.host, &s3website_domain, &s3website_subdomain, hostnames_s3website_set);
      if (in_hosted_domain_s3website) {
	in_hosted_domain = true; // TODO: should hostnames be a strict superset of hostnames_s3website?
        domain = s3website_domain;
        subdomain = s3website_subdomain;
      }
    }

    ldout(s->cct, 20)
      << "subdomain=" << subdomain 
      << " domain=" << domain 
      << " in_hosted_domain=" << in_hosted_domain 
      << " in_hosted_domain_s3website=" << in_hosted_domain_s3website 
      << dendl;

    if (g_conf->rgw_resolve_cname
	&& !in_hosted_domain
	&& !in_hosted_domain_s3website) {
      string cname;
      bool found;
      int r = rgw_resolver->resolve_cname(info.host, cname, &found);
      if (r < 0) {
	ldout(s->cct, 0)
	  << "WARNING: rgw_resolver->resolve_cname() returned r=" << r
	  << dendl;
      }

      if (found) {
	ldout(s->cct, 5) << "resolved host cname " << info.host << " -> "
			 << cname << dendl;
	in_hosted_domain =
	  rgw_find_host_in_domains(cname, &domain, &subdomain, hostnames_set);

        if (s3website_enabled
	    && !in_hosted_domain_s3website) {
	  in_hosted_domain_s3website =
	    rgw_find_host_in_domains(cname, &s3website_domain,
				     &s3website_subdomain,
				     hostnames_s3website_set);
	  if (in_hosted_domain_s3website) {
	    in_hosted_domain = true; // TODO: should hostnames be a
				     // strict superset of hostnames_s3website?
	    domain = s3website_domain;
	    subdomain = s3website_subdomain;
	  }
        }

        ldout(s->cct, 20)
          << "subdomain=" << subdomain 
          << " domain=" << domain 
          << " in_hosted_domain=" << in_hosted_domain 
          << " in_hosted_domain_s3website=" << in_hosted_domain_s3website 
          << dendl;
      }
    }

    // Handle A/CNAME records that point to the RGW storage, but do match the
    // CNAME test above, per issue http://tracker.ceph.com/issues/15975
    // If BOTH domain & subdomain variables are empty, then none of the above
    // cases matched anything, and we should fall back to using the Host header
    // directly as the bucket name.
    // As additional checks:
    // - if the Host header is an IP, we're using path-style access without DNS
    // - Also check that the Host header is a valid bucket name before using it.
    // - Don't enable virtual hosting if no hostnames are configured
    if (subdomain.empty()
        && (domain.empty() || domain != info.host)
        && !looks_like_ip_address(info.host.c_str())
        && RGWHandler_REST::validate_bucket_name(info.host) == 0
        && !(hostnames_set.empty() && hostnames_s3website_set.empty())) {
      subdomain.append(info.host);
      in_hosted_domain = 1;
    }

    if (s3website_enabled && api_priority_s3website > api_priority_s3) {
      in_hosted_domain_s3website = 1;
    }

    if (in_hosted_domain_s3website) {
      s->prot_flags |= RGW_REST_WEBSITE;
    }


    if (in_hosted_domain && !subdomain.empty()) {
      string encoded_bucket = "/";
      encoded_bucket.append(subdomain);
      if (s->info.request_uri[0] != '/')
        encoded_bucket.append("/");
      encoded_bucket.append(s->info.request_uri);
      s->info.request_uri = encoded_bucket;
    }

    if (!domain.empty()) {
      s->info.domain = domain;
    }

    ldout(s->cct, 20)
      << "final domain/bucket"
      << " subdomain=" << subdomain
      << " domain=" << domain
      << " in_hosted_domain=" << in_hosted_domain
      << " in_hosted_domain_s3website=" << in_hosted_domain_s3website
      << " s->info.domain=" << s->info.domain
      << " s->info.request_uri=" << s->info.request_uri
      << dendl;
  }

  if (s->info.domain.empty()) {
    s->info.domain = s->cct->_conf->rgw_dns_name;
  }

  s->decoded_uri = url_decode(s->info.request_uri);
  /* Validate for being free of the '\0' buried in the middle of the string. */
  if (std::strlen(s->decoded_uri.c_str()) != s->decoded_uri.length()) {
    return -ERR_ZERO_IN_URL;
  }

  /* FastCGI specification, section 6.3
   * http://www.fastcgi.com/devkit/doc/fcgi-spec.html#S6.3
   * ===
   * The Authorizer application receives HTTP request information from the Web
   * server on the FCGI_PARAMS stream, in the same format as a Responder. The
   * Web server does not send CONTENT_LENGTH, PATH_INFO, PATH_TRANSLATED, and
   * SCRIPT_NAME headers.
   * ===
   * Ergo if we are in Authorizer role, we MUST look at HTTP_CONTENT_LENGTH
   * instead of CONTENT_LENGTH for the Content-Length.
   *
   * There is one slight wrinkle in this, and that's older versions of
   * nginx/lighttpd/apache setting BOTH headers. As a result, we have to check
   * both headers and can't always simply pick A or B.
   */
  const char* content_length = info.env->get("CONTENT_LENGTH");
  const char* http_content_length = info.env->get("HTTP_CONTENT_LENGTH");
  if (!http_content_length != !content_length) {
    /* Easy case: one or the other is missing */
    s->length = (content_length ? content_length : http_content_length);
  } else if (s->cct->_conf->rgw_content_length_compat &&
	     content_length && http_content_length) {
    /* Hard case: Both are set, we have to disambiguate */
    int64_t content_length_i, http_content_length_i;

    content_length_i = parse_content_length(content_length);
    http_content_length_i = parse_content_length(http_content_length);

    // Now check them:
    if (http_content_length_i < 0) {
      // HTTP_CONTENT_LENGTH is invalid, ignore it
    } else if (content_length_i < 0) {
      // CONTENT_LENGTH is invalid, and HTTP_CONTENT_LENGTH is valid
      // Swap entries
      content_length = http_content_length;
    } else {
      // both CONTENT_LENGTH and HTTP_CONTENT_LENGTH are valid
      // Let's pick the larger size
      if (content_length_i < http_content_length_i) {
	// prefer the larger value
	content_length = http_content_length;
      }
    }
    s->length = content_length;
    // End of: else if (s->cct->_conf->rgw_content_length_compat &&
    //   content_length &&
    // http_content_length)
  } else {
    /* no content length was defined */
    s->length = NULL;
  }

  if (s->length) {
    if (*s->length == '\0') {
      s->content_length = 0;
    } else {
      string err;
      s->content_length = strict_strtoll(s->length, 10, &err);
      if (!err.empty()) {
	ldout(s->cct, 10) << "bad content length, aborting" << dendl;
	return -EINVAL;
      }
    }
  }

  if (s->content_length < 0) {
    ldout(s->cct, 10) << "negative content length, aborting" << dendl;
    return -EINVAL;
  }

  map<string, string>::iterator giter;
  for (giter = generic_attrs_map.begin(); giter != generic_attrs_map.end();
       ++giter) {
    const char *env = info.env->get(giter->first.c_str());
    if (env) {
      s->generic_attrs[giter->second] = env;
    }
  }

  if (g_conf->rgw_print_continue) {
    const char *expect = info.env->get("HTTP_EXPECT");
    s->expect_cont = (expect && !strcasecmp(expect, "100-continue"));
  }
  s->op = op_from_method(info.method);

  info.init_meta_info(&s->has_bad_meta);

  return 0;
}