Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
223 changes: 0 additions & 223 deletions src/istio/api_spec/path_matcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,6 @@ class PathMatcher {
public:
~PathMatcher(){};

// TODO: Do not template VariableBinding
template <class VariableBinding>
Method Lookup(const std::string& http_method, const std::string& path,
const std::string& query_params,
std::vector<VariableBinding>* variable_bindings,
std::string* body_field_path) const;

Method Lookup(const std::string& http_method, const std::string& path) const;

private:
Expand Down Expand Up @@ -139,177 +132,6 @@ std::vector<std::string>& split(const std::string& s, char delim,
return elems;
}

inline bool IsReservedChar(char c) {
// Reserved characters according to RFC 6570
switch (c) {
case '!':
case '#':
case '$':
case '&':
case '\'':
case '(':
case ')':
case '*':
case '+':
case ',':
case '/':
case ':':
case ';':
case '=':
case '?':
case '@':
case '[':
case ']':
return true;
default:
return false;
}
}

// Check if an ASCII character is a hex digit. We can't use ctype's
// isxdigit() because it is affected by locale. This function is applied
// to the escaped characters in a url, not to natural-language
// strings, so locale should not be taken into account.
inline bool ascii_isxdigit(char c) {
return ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F') ||
('0' <= c && c <= '9');
}

inline int hex_digit_to_int(char c) {
/* Assume ASCII. */
int x = static_cast<unsigned char>(c);
if (x > '9') {
x += 9;
}
return x & 0xf;
}

// This is a helper function for UrlUnescapeString. It takes a string and
// the index of where we are within that string.
//
// The function returns true if the next three characters are of the format:
// "%[0-9A-Fa-f]{2}".
//
// If the next three characters are an escaped character then this function will
// also return what character is escaped.
bool GetEscapedChar(const std::string& src, size_t i,
bool unescape_reserved_chars, char* out) {
if (i + 2 < src.size() && src[i] == '%') {
if (ascii_isxdigit(src[i + 1]) && ascii_isxdigit(src[i + 2])) {
char c =
(hex_digit_to_int(src[i + 1]) << 4) | hex_digit_to_int(src[i + 2]);
if (!unescape_reserved_chars && IsReservedChar(c)) {
return false;
}
*out = c;
return true;
}
}
return false;
}

// Unescapes string 'part' and returns the unescaped string. Reserved characters
// (as specified in RFC 6570) are not escaped if unescape_reserved_chars is
// false.
std::string UrlUnescapeString(const std::string& part,
bool unescape_reserved_chars) {
std::string unescaped;
// Check whether we need to escape at all.
bool needs_unescaping = false;
char ch = '\0';
for (size_t i = 0; i < part.size(); ++i) {
if (GetEscapedChar(part, i, unescape_reserved_chars, &ch)) {
needs_unescaping = true;
break;
}
}
if (!needs_unescaping) {
unescaped = part;
return unescaped;
}

unescaped.resize(part.size());

char* begin = &(unescaped)[0];
char* p = begin;

for (size_t i = 0; i < part.size();) {
if (GetEscapedChar(part, i, unescape_reserved_chars, &ch)) {
*p++ = ch;
i += 3;
} else {
*p++ = part[i];
i += 1;
}
}

unescaped.resize(p - begin);
return unescaped;
}

template <class VariableBinding>
void ExtractBindingsFromPath(const std::vector<HttpTemplate::Variable>& vars,
const std::vector<std::string>& parts,
std::vector<VariableBinding>* bindings) {
for (const auto& var : vars) {
// Determine the subpath bound to the variable based on the
// [start_segment, end_segment) segment range of the variable.
//
// In case of matching "**" - end_segment is negative and is relative to
// the end such that end_segment = -1 will match all subsequent segments.
VariableBinding binding;
binding.field_path = var.field_path;
// Calculate the absolute index of the ending segment in case it's negative.
size_t end_segment = (var.end_segment >= 0)
? var.end_segment
: parts.size() + var.end_segment + 1;
// It is multi-part match if we have more than one segment. We also make
// sure that a single URL segment match with ** is also considered a
// multi-part match by checking if it->second.end_segment is negative.
bool is_multipart =
(end_segment - var.start_segment) > 1 || var.end_segment < 0;
// Joins parts with "/" to form a path string.
for (size_t i = var.start_segment; i < end_segment; ++i) {
// For multipart matches only unescape non-reserved characters.
binding.value += UrlUnescapeString(parts[i], !is_multipart);
if (i < end_segment - 1) {
binding.value += "/";
}
}
bindings->emplace_back(binding);
}
}

template <class VariableBinding>
void ExtractBindingsFromQueryParameters(
const std::string& query_params, const std::set<std::string>& system_params,
std::vector<VariableBinding>* bindings) {
// The bindings in URL the query parameters have the following form:
// <field_path1>=value1&<field_path2>=value2&...&<field_pathN>=valueN
// Query parameters may also contain system parameters such as `api_key`.
// We'll need to ignore these. Example:
// book.id=123&book.author=Neal%20Stephenson&api_key=AIzaSyAz7fhBkC35D2M
std::vector<std::string> params;
split(query_params, '&', params);
for (const auto& param : params) {
size_t pos = param.find('=');
if (pos != 0 && pos != std::string::npos) {
auto name = param.substr(0, pos);
// Make sure the query parameter is not a system parameter (e.g.
// `api_key`) before adding the binding.
if (system_params.find(name) == std::end(system_params)) {
// The name of the parameter is a field path, which is a dot-delimited
// sequence of field names that identify the (potentially deep) field
// in the request, e.g. `book.author.name`.
VariableBinding binding;
split(name, '.', binding.field_path);
binding.value = UrlUnescapeString(param.substr(pos + 1), true);
bindings->emplace_back(std::move(binding));
}
}
}
}

// Converts a request path into a format that can be used to perform a request
// lookup in the PathMatcher trie. This utility method sanitizes the request
// path and then splits the path into slash separated parts. Returns an empty
Expand Down Expand Up @@ -380,51 +202,6 @@ PathMatcher<Method>::PathMatcher(PathMatcherBuilder<Method>&& builder)
custom_verbs_(std::move(builder.custom_verbs_)),
methods_(std::move(builder.methods_)) {}

// Lookup is a wrapper method for the recursive node Lookup. First, the wrapper
// splits the request path into slash-separated path parts. Next, the method
// checks that the |http_method| is supported. If not, then it returns an empty
// WrapperGraph::SharedPtr. Next, this method invokes the node's Lookup on
// the extracted |parts|. Finally, it fills the mapping from variables to their
// values parsed from the path.
// TODO: cache results by adding get/put methods here (if profiling reveals
// benefit)
template <class Method>
template <class VariableBinding>
Method PathMatcher<Method>::Lookup(
const std::string& http_method, const std::string& path,
const std::string& query_params,
std::vector<VariableBinding>* variable_bindings,
std::string* body_field_path) const {
const std::vector<std::string> parts =
ExtractRequestParts(path, custom_verbs_);

// If service_name has not been registered to ESP and strict_service_matching_
// is set to false, tries to lookup the method in all registered services.
if (root_ptr_ == nullptr) {
return nullptr;
}

PathMatcherLookupResult lookup_result =
LookupInPathMatcherNode(*root_ptr_, parts, http_method);
// Return nullptr if nothing is found.
// Not need to check duplication. Only first item is stored for duplicated
if (lookup_result.data == nullptr) {
return nullptr;
}
MethodData* method_data = reinterpret_cast<MethodData*>(lookup_result.data);
if (variable_bindings != nullptr) {
variable_bindings->clear();
ExtractBindingsFromPath(method_data->variables, parts, variable_bindings);
ExtractBindingsFromQueryParameters(
query_params, method_data->method->system_query_parameter_names(),
variable_bindings);
}
if (body_field_path != nullptr) {
*body_field_path = method_data->body_field_path;
}
return method_data->method;
}

// TODO: refactor common code with method above
template <class Method>
Method PathMatcher<Method>::Lookup(const std::string& http_method,
Expand Down
Loading