FilterHeadersStatus Filter::decodeHeaders()

in src/envoy/http/path_rewrite/filter.cc [44:126]


FilterHeadersStatus Filter::decodeHeaders(RequestHeaderMap& headers, bool) {
  if (headers.Path() == nullptr) {
    // NOTE: this shouldn't happen in practice because ServiceControl filter
    // would have already rejected the request.
    config_->stats().denied_by_no_path_.inc();
    rejectRequest(Envoy::Http::Code::BadRequest, "No path in request headers",
                  utils::generateRcDetails(utils::kRcDetailFilterPathRewrite,
                                           utils::kRcDetailErrorTypeBadRequest,
                                           utils::kRcDetailErrorMissingPath));
    return FilterHeadersStatus::StopIteration;
  } else if (headers.Path()->value().size() > PathMaxSize) {
    config_->stats().denied_by_oversize_path_.inc();
    rejectRequest(Envoy::Http::Code::BadRequest,
                  absl::StrCat("Path is too long, max allowed size is ",
                               PathMaxSize, "."),
                  utils::generateRcDetails(utils::kRcDetailFilterPathRewrite,
                                           utils::kRcDetailErrorTypeBadRequest,
                                           utils::kRcDetailErrorOversizePath));
    return Envoy::Http::FilterHeadersStatus::StopIteration;
  }

  absl::string_view original_path = headers.Path()->value().getStringView();
  // Reject requests with fragment identifiers. They should never be sent to
  // servers, and it breaks how we handle path translation (query params
  // appended incorrectly).
  if (absl::StrContains(original_path, "#")) {
    config_->stats().denied_by_invalid_path_.inc();
    rejectRequest(
        Envoy::Http::Code::BadRequest,
        "Path cannot contain fragment identifier (#)",
        utils::generateRcDetails(utils::kRcDetailFilterPathRewrite,
                                 utils::kRcDetailErrorTypeBadRequest,
                                 utils::kRcDetailErrorFragmentIdentifier));
    return FilterHeadersStatus::StopIteration;
  }

  // Make sure route is calculated
  auto route = decoder_callbacks_->route();

  // `route` shouldn't be nullptr as the catch-all route match should catch all
  // the undefined requests.
  if (route == nullptr || route->routeEntry() == nullptr) {
    return Envoy::Http::FilterHeadersStatus::Continue;
  }

  const auto* per_route =
      ::Envoy::Http::Utility::resolveMostSpecificPerFilterConfig<
          PerRouteFilterConfig>(decoder_callbacks_);
  if (per_route == nullptr) {
    ENVOY_LOG(debug,
              "no per-route config, request is passed through unmodified");
    config_->stats().path_not_changed_.inc();
    return FilterHeadersStatus::Continue;
  }

  std::string new_path;

  // It should be a bug in Envoy RouteMatch generated by control plane if
  // url_template is mismatched with the request path
  if (!per_route->config_parser().rewrite(original_path, new_path)) {
    config_->stats().denied_by_url_template_mismatch_.inc();
    rejectRequest(
        Envoy::Http::Code::InternalServerError,
        absl::StrCat("Request `", utils::readHeaderEntry(headers.Method()), " ",
                     utils::readHeaderEntry(headers.Path()),
                     "` is getting wrong route config"),
        utils::generateRcDetails(
            utils::kRcDetailFilterPathRewrite,
            utils::kRcDetailErrorTypeWrongRouteConfig,
            absl::StrCat("request_path(",
                         utils::readHeaderEntry(headers.Path()),
                         ")-mismatched-url_template(",
                         per_route->config_parser().url_template(), ")")));
    return FilterHeadersStatus::StopIteration;
  }

  config_->stats().path_changed_.inc();
  if (!headers.EnvoyOriginalPath()) {
    headers.setEnvoyOriginalPath(headers.getPathValue());
  }
  headers.setPath(new_path);
  return FilterHeadersStatus::Continue;
}