| /* |
| * \copyright Copyright 2013 Google Inc. All Rights Reserved. |
| * \license @{ |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| * @} |
| */ |
| |
| // Implementation of RFC 6570 based on (open source implementation) at |
| // java/com/google/api/client/http/UriTemplate.java |
| // The URI Template spec is at http://tools.ietf.org/html/rfc6570 |
| // Templates up to level 3 are supported. |
| |
| #include "net/third_party/uri_template/uri_template.h" |
| |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include "base/strings/string_split.h" |
| #include "net/base/escape.h" |
| |
| using std::string; |
| |
| namespace uri_template { |
| |
| namespace { |
| |
| // The UriTemplateConfig is used to represent variable sections and to construct |
| // the expanded url. |
| struct UriTemplateConfig { |
| public: |
| UriTemplateConfig(const char* prefix, |
| const char* joiner, |
| bool requires_variable_assignment, |
| bool allow_reserved_expansion, |
| bool no_variable_assignment_if_empty = false) |
| : prefix_(prefix), |
| joiner_(joiner), |
| requires_variable_assignment_(requires_variable_assignment), |
| no_variable_assignment_if_empty_(no_variable_assignment_if_empty), |
| allow_reserved_expansion_(allow_reserved_expansion) {} |
| |
| void AppendValue(const string& variable, |
| const string& value, |
| bool use_prefix, |
| string* target) const { |
| string joiner = use_prefix ? prefix_ : joiner_; |
| if (requires_variable_assignment_) { |
| if (value.empty() && no_variable_assignment_if_empty_) { |
| target->append(joiner + EscapedValue(variable)); |
| } else { |
| target->append(joiner + EscapedValue(variable) + "=" + |
| EscapedValue(value)); |
| } |
| } else { |
| target->append(joiner + EscapedValue(value)); |
| } |
| } |
| |
| private: |
| string EscapedValue(const string& value) const { |
| string escaped; |
| if (allow_reserved_expansion_) { |
| // Reserved expansion passes through reserved and pct-encoded characters. |
| escaped = net::EscapeExternalHandlerValue(value); |
| } else { |
| escaped = net::EscapeAllExceptUnreserved(value); |
| } |
| return escaped; |
| } |
| |
| const char* prefix_; |
| const char* joiner_; |
| bool requires_variable_assignment_; |
| bool no_variable_assignment_if_empty_; |
| bool allow_reserved_expansion_; |
| }; |
| |
| // variable is an in-out argument. On input it is the content between the |
| // '{}' in the source. On result the control parameters are stripped off |
| // leaving just the comma-separated variable name(s) that we should try to |
| // resolve. |
| UriTemplateConfig MakeConfig(string* variable) { |
| switch (*variable->data()) { |
| // Reserved expansion. |
| case '+': |
| *variable = variable->substr(1); |
| return UriTemplateConfig("", ",", false, true); |
| |
| // Fragment expansion. |
| case '#': |
| *variable = variable->substr(1); |
| return UriTemplateConfig("#", ",", false, true); |
| |
| // Label with dot-prefix. |
| case '.': |
| *variable = variable->substr(1); |
| return UriTemplateConfig(".", ".", false, false); |
| |
| // Path segment expansion. |
| case '/': |
| *variable = variable->substr(1); |
| return UriTemplateConfig("/", "/", false, false); |
| |
| // Path segment parameter expansion. |
| case ';': |
| *variable = variable->substr(1); |
| return UriTemplateConfig(";", ";", true, false, true); |
| |
| // Form-style query expansion. |
| case '?': |
| *variable = variable->substr(1); |
| return UriTemplateConfig("?", "&", true, false); |
| |
| // Form-style query continuation. |
| case '&': |
| *variable = variable->substr(1); |
| return UriTemplateConfig("&", "&", true, false); |
| |
| // Simple expansion. |
| default: |
| return UriTemplateConfig("", ",", false, false); |
| } |
| } |
| |
| void ProcessVariableSection( |
| string* variable_section, |
| const std::unordered_map<string, string>& parameters, |
| string* target, |
| std::set<string>* vars_found) { |
| // Note that this function will modify the variable_section string to remove |
| // the decorators, leaving just comma-separated variable name(s). |
| UriTemplateConfig config = MakeConfig(variable_section); |
| std::vector<string> variables = base::SplitString( |
| *variable_section, ",", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY); |
| bool first_var = true; |
| for (const string& variable : variables) { |
| auto found = parameters.find(variable); |
| if (found != parameters.end()) { |
| config.AppendValue(variable, found->second, first_var, target); |
| first_var = false; |
| if (vars_found) { |
| vars_found->insert(variable); |
| } |
| } |
| } |
| } |
| |
| } // namespace |
| |
| bool Expand(const string& path_uri, |
| const std::unordered_map<string, string>& parameters, |
| string* target, |
| std::set<string>* vars_found) { |
| size_t cur = 0; |
| size_t length = path_uri.length(); |
| while (cur < length) { |
| size_t open = path_uri.find('{', cur); |
| size_t close = path_uri.find('}', cur); |
| if (open == string::npos) { |
| if (close == string::npos) { |
| // No more variables to process. |
| target->append(path_uri.substr(cur).data(), path_uri.length() - cur); |
| return true; |
| } else { |
| // Template was malformed. Unexpected closing brace. |
| target->clear(); |
| return false; |
| } |
| } |
| target->append(path_uri, cur, open - cur); |
| size_t next_open = path_uri.find('{', open + 1); |
| if (close == string::npos || close < open || next_open < close) { |
| // Template was malformed. |
| target->clear(); |
| return false; |
| } |
| string variable_section(path_uri, open + 1, close - open - 1); |
| cur = close + 1; |
| |
| ProcessVariableSection(&variable_section, parameters, target, vars_found); |
| } |
| return true; |
| } |
| |
| } // namespace uri_template |