Repository: envoyproxy/envoy
File: source/common/formatter/http_specific_formatter.cc (C++)

Code before:
  absl::optional<std::string> value = query_params.getFirstValue(parameter_key_);
  if (value.has_value() && max_length_.has_value()) {
    SubstitutionFormatUtils::truncate(value.value(), max_length_.value());
  }
  return value;
}

Protobuf::Value
QueryParameterFormatter::formatValue(const Context& context,
                                     const StreamInfo::StreamInfo& stream_info) const {
  return ValueUtil::optionalStringValue(format(context, stream_info));
}

QueryParametersFormatter::QueryParametersFormatter(absl::optional<size_t> max_length)
    : max_length_(max_length) {}

// FormatterProvider
absl::optional<std::string> QueryParametersFormatter::format(const Context& context,
                                                             const StreamInfo::StreamInfo&) const {
  const auto request_headers = context.requestHeaders();
  if (!request_headers.has_value()) {
    return absl::nullopt;
  }

  const auto query_params =
      Http::Utility::QueryParamsMulti::parseAndDecodeQueryString(request_headers->getPathValue());

  absl::optional<std::string> value = query_params.toString();
  if (value.has_value() && max_length_.has_value()) {
    SubstitutionFormatUtils::truncate(value.value(), max_length_.value());
  }
  return value;
}

Protobuf::Value
QueryParametersFormatter::formatValue(const Context& context,
                                      const StreamInfo::StreamInfo& stream_info) const {
  return ValueUtil::optionalStringValue(format(context, stream_info));
}

absl::optional<std::string> PathFormatter::format(const Context& context,
                                                  const StreamInfo::StreamInfo&) const {

  absl::string_view path_view;
  const auto headers = context.requestHeaders();
  if (!headers.has_value()) {
    return absl::nullopt;
  }
  switch (option_) {
  case OriginalPathOrPath:
    path_view = headers->getEnvoyOriginalPathValue();

Review comment (suggestion):
I don't think you need to parse query parameters here? If you look into this method it's just searching for the first `?` and returns everything after it. That's cheap, but doesn't normalize it in any way.

Code after:
  absl::optional<std::string> value = query_params.getFirstValue(parameter_key_);
  if (value.has_value() && max_length_.has_value()) {
    SubstitutionFormatUtils::truncate(value.value(), max_length_.value());
  }
  return value;
}

Protobuf::Value
QueryParameterFormatter::formatValue(const Context& context,
                                     const StreamInfo::StreamInfo& stream_info) const {
  return ValueUtil::optionalStringValue(format(context, stream_info));
}

QueryParametersFormatter::DecodeOption
QueryParametersFormatter::parseDecodeOption(absl::string_view decoding) {

  if (decoding.empty() || decoding == "ORIG") {
    return DecodeOption::Original;
  } else if (decoding == "DECODED") {
    return DecodeOption::Decoded;
  } else {
    throw EnvoyException(fmt::format(
        "Invalid QUERY_PARAMS option: '{}', only 'ORIG'/'DECODED' are allowed", decoding));
  }
}

// FormatterProvider
absl::optional<std::string> QueryParametersFormatter::format(const Context& context,
                                                             const StreamInfo::StreamInfo&) const {
  const auto request_headers = context.requestHeaders();
  if (!request_headers.has_value()) {
    return absl::nullopt;
  }

  // Gather query parameters substring from path
  absl::string_view path_view = request_headers->getPathValue();
  auto query_offset = path_view.find('?');

  if (query_offset == absl::string_view::npos) {
    return absl::nullopt;
  }

  std::string query_params = std::string(path_view.substr(query_offset + 1));

  // Apply percent decoding on the query params if requested
  if (option_ == DecodeOption::Decoded) {
    query_params = Http::Utility::PercentEncoding::urlDecodeQueryParameter(query_params);
  }

  SubstitutionFormatUtils::truncate(query_params, max_length_);
  return query_params;