You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
202 lines
4.6 KiB
C++
202 lines
4.6 KiB
C++
#include "webcc/url.h"
|
|
|
|
#include <algorithm>
|
|
#include <sstream>
|
|
#include <utility> // for move()
|
|
|
|
namespace webcc {
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Helper functions to decode URL string.
|
|
|
|
// Convert a hex character digit to a decimal character value.
|
|
static bool HexToDecimal(char hex, int* decimal) {
|
|
if (hex >= '0' && hex <= '9') {
|
|
*decimal = hex - '0';
|
|
} else if (hex >= 'A' && hex <= 'F') {
|
|
*decimal = 10 + (hex - 'A');
|
|
} else if (hex >= 'a' && hex <= 'f') {
|
|
*decimal = 10 + (hex - 'a');
|
|
} else {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool Decode(const std::string& encoded, std::string* raw) {
|
|
for (auto iter = encoded.begin(); iter != encoded.end(); ++iter) {
|
|
if (*iter == '%') {
|
|
if (++iter == encoded.end()) {
|
|
// Invalid URI string, two hexadecimal digits must follow '%'.
|
|
return false;
|
|
}
|
|
|
|
int h_decimal = 0;
|
|
if (!HexToDecimal(*iter, &h_decimal)) {
|
|
return false;
|
|
}
|
|
|
|
if (++iter == encoded.end()) {
|
|
// Invalid URI string, two hexadecimal digits must follow '%'.
|
|
return false;
|
|
}
|
|
|
|
int l_decimal = 0;
|
|
if (!HexToDecimal(*iter, &l_decimal)) {
|
|
return false;
|
|
}
|
|
|
|
raw->push_back(static_cast<char>((h_decimal << 4) + l_decimal));
|
|
|
|
} else if (*iter > 127 || *iter < 0) {
|
|
// Invalid encoded URI string, must be entirely ASCII.
|
|
return false;
|
|
} else {
|
|
raw->push_back(*iter);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
UrlQuery::UrlQuery(const std::map<std::string, std::string>& map) {
|
|
for (auto& pair : map) {
|
|
Add(pair.first, pair.second);
|
|
}
|
|
}
|
|
|
|
void UrlQuery::Add(std::string&& key, std::string&& value) {
|
|
if (!Has(key)) {
|
|
parameters_.push_back({ std::move(key), std::move(value) });
|
|
}
|
|
}
|
|
|
|
void UrlQuery::Add(const std::string& key, const std::string& value) {
|
|
if (!Has(key)) {
|
|
parameters_.push_back({ key, value });
|
|
}
|
|
}
|
|
|
|
void UrlQuery::Remove(const std::string& key) {
|
|
auto it = Find(key);
|
|
if (it != parameters_.end()) {
|
|
parameters_.erase(it);
|
|
}
|
|
}
|
|
|
|
const std::string& UrlQuery::Get(const std::string& key) const {
|
|
auto it = Find(key);
|
|
if (it != parameters_.end()) {
|
|
return it->value();
|
|
}
|
|
|
|
static const std::string kEmptyValue;
|
|
return kEmptyValue;
|
|
}
|
|
|
|
std::string UrlQuery::ToString() const {
|
|
if (parameters_.empty()) {
|
|
return "";
|
|
}
|
|
|
|
std::string str = parameters_[0].ToString();
|
|
|
|
for (std::size_t i = 1; i < parameters_.size(); ++i) {
|
|
str += "&";
|
|
str += parameters_[i].ToString();
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
UrlQuery::ConstIterator UrlQuery::Find(const std::string& key) const {
|
|
return std::find_if(parameters_.begin(),
|
|
parameters_.end(),
|
|
[&key](const Parameter& p) { return p.key() == key; });
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
Url::Url(const std::string& str, bool decode) {
|
|
if (!decode || str.find('%') == std::string::npos) {
|
|
Init(str);
|
|
return;
|
|
}
|
|
|
|
std::string decoded;
|
|
if (Decode(str, &decoded)) {
|
|
Init(decoded);
|
|
} else {
|
|
// TODO(Adam): Exception?
|
|
Init(str);
|
|
}
|
|
}
|
|
|
|
bool Url::IsValid() const {
|
|
return !path_.empty();
|
|
}
|
|
|
|
std::vector<std::string> Url::SplitPath(const std::string& path) {
|
|
std::vector<std::string> results;
|
|
std::stringstream iss(path);
|
|
std::string s;
|
|
while (std::getline(iss, s, '/')) {
|
|
if (!s.empty()) {
|
|
results.push_back(s);
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
static bool SplitKeyValue(const std::string& kv,
|
|
std::string* key, std::string* value) {
|
|
std::size_t i = kv.find_first_of('=');
|
|
if (i == std::string::npos || i == 0) {
|
|
return false;
|
|
}
|
|
|
|
*key = kv.substr(0, i);
|
|
*value = kv.substr(i + 1);
|
|
return true;
|
|
}
|
|
|
|
// static
|
|
void Url::SplitQuery(const std::string& str, UrlQuery* query) {
|
|
const std::size_t NPOS = std::string::npos;
|
|
|
|
// Split into key value pairs separated by '&'.
|
|
std::size_t i = 0;
|
|
while (i != NPOS) {
|
|
std::size_t j = str.find_first_of('&', i);
|
|
|
|
std::string kv;
|
|
if (j == NPOS) {
|
|
kv = str.substr(i);
|
|
i = NPOS;
|
|
} else {
|
|
kv = str.substr(i, j - i);
|
|
i = j + 1;
|
|
}
|
|
|
|
std::string key;
|
|
std::string value;
|
|
if (SplitKeyValue(kv, &key, &value)) {
|
|
query->Add(std::move(key), std::move(value));
|
|
}
|
|
}
|
|
}
|
|
|
|
void Url::Init(const std::string& str) {
|
|
std::size_t pos = str.find('?');
|
|
if (pos == std::string::npos) {
|
|
path_ = str;
|
|
} else {
|
|
path_ = str.substr(0, pos);
|
|
query_ = str.substr(pos + 1);
|
|
}
|
|
}
|
|
|
|
} // namespace webcc
|