123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- /*
- * Copyright 2013-present Facebook, Inc.
- *
- * 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.
- */
- #include <folly/Uri.h>
- #include <algorithm>
- #include <cctype>
- #include <boost/regex.hpp>
- namespace folly {
- namespace {
- std::string submatch(const boost::cmatch& m, int idx) {
- const auto& sub = m[idx];
- return std::string(sub.first, sub.second);
- }
- } // namespace
- Uri::Uri(StringPiece str) : hasAuthority_(false), port_(0) {
- static const boost::regex uriRegex(
- "([a-zA-Z][a-zA-Z0-9+.-]*):" // scheme:
- "([^?#]*)" // authority and path
- "(?:\\?([^#]*))?" // ?query
- "(?:#(.*))?"); // #fragment
- static const boost::regex authorityAndPathRegex("//([^/]*)(/.*)?");
- boost::cmatch match;
- if (UNLIKELY(!boost::regex_match(str.begin(), str.end(), match, uriRegex))) {
- throw std::invalid_argument(to<std::string>("invalid URI ", str));
- }
- scheme_ = submatch(match, 1);
- std::transform(scheme_.begin(), scheme_.end(), scheme_.begin(), ::tolower);
- StringPiece authorityAndPath(match[2].first, match[2].second);
- boost::cmatch authorityAndPathMatch;
- if (!boost::regex_match(
- authorityAndPath.begin(),
- authorityAndPath.end(),
- authorityAndPathMatch,
- authorityAndPathRegex)) {
- // Does not start with //, doesn't have authority
- hasAuthority_ = false;
- path_ = authorityAndPath.str();
- } else {
- static const boost::regex authorityRegex(
- "(?:([^@:]*)(?::([^@]*))?@)?" // username, password
- "(\\[[^\\]]*\\]|[^\\[:]*)" // host (IP-literal (e.g. '['+IPv6+']',
- // dotted-IPv4, or named host)
- "(?::(\\d*))?"); // port
- const auto authority = authorityAndPathMatch[1];
- boost::cmatch authorityMatch;
- if (!boost::regex_match(
- authority.first,
- authority.second,
- authorityMatch,
- authorityRegex)) {
- throw std::invalid_argument(to<std::string>(
- "invalid URI authority ",
- StringPiece(authority.first, authority.second)));
- }
- StringPiece port(authorityMatch[4].first, authorityMatch[4].second);
- if (!port.empty()) {
- port_ = to<uint16_t>(port);
- }
- hasAuthority_ = true;
- username_ = submatch(authorityMatch, 1);
- password_ = submatch(authorityMatch, 2);
- host_ = submatch(authorityMatch, 3);
- path_ = submatch(authorityAndPathMatch, 2);
- }
- query_ = submatch(match, 3);
- fragment_ = submatch(match, 4);
- }
- std::string Uri::authority() const {
- std::string result;
- // Port is 5 characters max and we have up to 3 delimiters.
- result.reserve(host().size() + username().size() + password().size() + 8);
- if (!username().empty() || !password().empty()) {
- result.append(username());
- if (!password().empty()) {
- result.push_back(':');
- result.append(password());
- }
- result.push_back('@');
- }
- result.append(host());
- if (port() != 0) {
- result.push_back(':');
- toAppend(port(), &result);
- }
- return result;
- }
- std::string Uri::hostname() const {
- if (host_.size() > 0 && host_[0] == '[') {
- // If it starts with '[', then it should end with ']', this is ensured by
- // regex
- return host_.substr(1, host_.size() - 2);
- }
- return host_;
- }
- const std::vector<std::pair<std::string, std::string>>& Uri::getQueryParams() {
- if (!query_.empty() && queryParams_.empty()) {
- // Parse query string
- static const boost::regex queryParamRegex(
- "(^|&)" /*start of query or start of parameter "&"*/
- "([^=&]*)=?" /*parameter name and "=" if value is expected*/
- "([^=&]*)" /*parameter value*/
- "(?=(&|$))" /*forward reference, next should be end of query or
- start of next parameter*/);
- const boost::cregex_iterator paramBeginItr(
- query_.data(), query_.data() + query_.size(), queryParamRegex);
- boost::cregex_iterator paramEndItr;
- for (auto itr = paramBeginItr; itr != paramEndItr; ++itr) {
- if (itr->length(2) == 0) {
- // key is empty, ignore it
- continue;
- }
- queryParams_.emplace_back(
- std::string((*itr)[2].first, (*itr)[2].second), // parameter name
- std::string((*itr)[3].first, (*itr)[3].second) // parameter value
- );
- }
- }
- return queryParams_;
- }
- } // namespace folly
|