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::string_view decoding,
                                                   absl::optional<size_t> max_length)
    : decoding_(decoding), 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;
  }

  bool decode_bool = false;
  if (decoding.empty()) {
    decode_bool = false;
  } else if (decoding == "ORIG") {
    decode_bool = false;
  } if (decoding == "DECODED") {
    decode_bool = true;
  } else {
    return absl::InvalidArgumentError(
        fmt::format("Invalid QUERY_PARAMS option: '{}', only 'ORIG'/'DECODED' are allowed", decoding));
  }

  // Gather query parameters substring from path
  absl::string_view path_view = request_headers->getPathValue();
  auto query_offset = path_view.find('?');
  absl::optional<string_view> query_params;
  if (query_offset != absl::string_view::npos) {
    query_params = path_view.substr(query_offset+1);
  }

  // Apply query param percent decoding on the query params if requested
  if (query_params.has_value() && decode_bool) {
    query_params = Http::Utility::PercentEncoding::urlDecodeQueryParameter(query_params);
  }

  if (query_params.has_value() && max_length_.has_value()) {

Review comment (refactor):
Can you move this logic into the constructor? It is better to parse the config at config time, than at runtime. E.g. save `decode_bool_` as a bool field rather than recompute it from `decoding_`.

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;