absl::StatusOr extractIPFromForwardedHeader()

in src/envoy/http/service_control/handler_utils.cc [371:424]


absl::StatusOr<std::string> extractIPFromForwardedHeader(
    const Envoy::Http::RequestHeaderMap& headers) {
  const auto values = headers.get(kForwardedHeader);
  if (values.size() == 0) {
    return Envoy::EMPTY_STRING;
  }

  // Only support one header value.
  // Need to extract the last one,  could not define the last one for multiple
  // header values.
  if (values.size() > 1) {
    return absl::InvalidArgumentError("more than one forwarded headers.");
  }

  absl::string_view source = values[0]->value().getStringView();

  // Multiple proxy sections are separated by comma. Use the last one.
  const absl::string_view::size_type pos = source.find_last_of(',');
  if (pos != absl::string_view::npos) {
    source.remove_prefix(pos + 1);
  }

  // Each section may have multiple fields, they are separated by semicolon.
  // For example: by=abc;for=1.2.3.4;host=abc;proto=http
  // Extract ip from the "for=" field.
  for (absl::string_view s : absl::StrSplit(source, ';')) {
    absl::string_view token = absl::StripAsciiWhitespace(s);
    if (absl::StartsWith(token, kForwardedIPTokenPrefix)) {
      absl::string_view ip = token.substr(kForwardedIPTokenPrefix.size());

      // IPv2 address is wrapped with \"[]\".
      // Remove double quote.
      if (ip[0] == '"' && ip[ip.size() - 1] == '"') {
        ip = ip.substr(1, ip.size() - 2);
      }
      // Remove [].
      if (ip[0] == '[' && ip[ip.size() - 1] == ']') {
        ip = ip.substr(1, ip.size() - 2);
      }
      std::string ip_str(ip);

      // Verify it is a valid IP.
      const auto address =
          Envoy::Network::Utility::parseInternetAddressNoThrow(ip_str);
      if (address == nullptr) {
        return absl::InvalidArgumentError(
            absl::StrCat(ip_str, "is not a valid ip address"));
      }
      return ip_str;
    }
  }
  return absl::InvalidArgumentError(
      absl::StrCat("could not find IP from: ", source));
}