56d66ef7db
Removed tabs (tabs to spaces). Remove the check_deps.sh. removed libwebsocketpp-dev from dependencies. BTW, this is in cmake.yml.
472 lines
11 KiB
C++
472 lines
11 KiB
C++
// Copyright (c) 2022 Robin Davies
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
// this software and associated documentation files (the "Software"), to deal in
|
|
// the Software without restriction, including without limitation the rights to
|
|
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
// the Software, and to permit persons to whom the Software is furnished to do so,
|
|
// subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
// copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
#include "pch.h"
|
|
#include "Uri.hpp"
|
|
#include "HtmlHelper.hpp"
|
|
#include "PiPedalException.hpp"
|
|
|
|
using namespace pipedal;
|
|
|
|
std::string uri::segment(int index) const
|
|
{
|
|
const char *p = path_start;
|
|
int segment = 0;
|
|
|
|
if (p != path_end && *p == '/') ++p;
|
|
|
|
while (p != path_end && *p != '?' && *p != '#')
|
|
{
|
|
if (segment == index)
|
|
{
|
|
const char*segmentStart = p;
|
|
while(p != path_end && *p != '/' && *p != '?' && *p != '#')
|
|
{
|
|
++p;
|
|
}
|
|
return HtmlHelper::decode_url_segment(segmentStart,p);
|
|
} else {
|
|
while(p != path_end && *p != '/' && *p != '?' && *p != '#')
|
|
{
|
|
++p;
|
|
}
|
|
}
|
|
if (p != path_end && *p == '/') ++p;
|
|
++segment;
|
|
}
|
|
throw std::invalid_argument("Invalid segement number.");
|
|
};
|
|
|
|
|
|
int uri::segment_count() const {
|
|
const char *p = path_start;
|
|
int segment = 0;
|
|
|
|
if (p != path_end && *p == '/') ++p;
|
|
|
|
while (p != path_end && *p != '?' && *p != '#')
|
|
{
|
|
while(p != path_end && *p != '/' && *p != '?' && *p != '#')
|
|
{
|
|
++p;
|
|
}
|
|
if (p != path_end && *p == '/') ++p;
|
|
++segment;
|
|
}
|
|
return segment;
|
|
}
|
|
|
|
void uri::set(const char*start, const char*end)
|
|
{
|
|
this->text = std::string(start,end);
|
|
set_();
|
|
}
|
|
void uri::set(const char*text)
|
|
{
|
|
this->text = text;
|
|
set_();
|
|
}
|
|
|
|
void uri::set_()
|
|
{
|
|
const char*start = this->text.c_str();
|
|
const char*end = start+this->text.size();
|
|
|
|
const char* p = start;
|
|
|
|
this->isRelative = true;
|
|
|
|
bool hasScheme = false;
|
|
while (p != end)
|
|
{
|
|
char c = *p;
|
|
if (c == ':') {
|
|
hasScheme = true;
|
|
break;
|
|
}
|
|
if (c == '/' || c == '?' || c == '#')
|
|
{
|
|
break;
|
|
}
|
|
++p;
|
|
}
|
|
if (hasScheme)
|
|
{
|
|
scheme_start = start;
|
|
scheme_end = p;
|
|
++p;
|
|
} else {
|
|
scheme_start = start;
|
|
scheme_end = start;
|
|
p = start;
|
|
}
|
|
if (p[0] == '/' && p[1] == '/')
|
|
{
|
|
this->isRelative = false;
|
|
// authority.
|
|
p += 2;
|
|
|
|
user_start = p;
|
|
user_end = p;
|
|
authority_start = p;
|
|
authority_end = nullptr;
|
|
|
|
const char*port_start = nullptr;
|
|
while (p != end)
|
|
{
|
|
char c = *p;
|
|
if (c == '@')
|
|
{
|
|
user_end = p;
|
|
authority_start = p+1;
|
|
port_start = nullptr;
|
|
} else if (c == ':')
|
|
{
|
|
authority_end = p;
|
|
port_start = p+1;
|
|
} else if (c == ']') // ignore colon inside ipv6 address.
|
|
{
|
|
port_start = nullptr;
|
|
}
|
|
if (c == '/' || c == '?' || c == '#')
|
|
{
|
|
break;
|
|
}
|
|
++p;
|
|
}
|
|
if (authority_end == nullptr) authority_end = p;
|
|
if (port_start != nullptr)
|
|
{
|
|
char*intEnd;
|
|
port_ = (int)std::strtol(port_start,&intEnd,10);
|
|
if (intEnd != p) throwInvalid();
|
|
} else {
|
|
port_ = -1;
|
|
}
|
|
} else {
|
|
user_start = p;
|
|
user_end = p;
|
|
authority_start = p;
|
|
authority_end = p;
|
|
port_ = -1;
|
|
}
|
|
|
|
path_start = p;
|
|
if (p != end && *p == '/') this->isRelative = false;
|
|
|
|
while (p != end && *p != '?' && *p != '#')
|
|
{
|
|
++p;
|
|
}
|
|
path_end = p;
|
|
|
|
if (p != end && *p == '?')
|
|
{
|
|
++p;
|
|
query_start = p;
|
|
while (p != end && *p != '#')
|
|
{
|
|
++p;
|
|
}
|
|
query_end = p;
|
|
} else {
|
|
query_start = query_end = p;
|
|
}
|
|
if (p != end && *p == '#')
|
|
{
|
|
++p;
|
|
fragment_start = p;
|
|
fragment_end = end;
|
|
|
|
} else {
|
|
fragment_start = fragment_end = p;
|
|
}
|
|
}
|
|
|
|
void uri::throwInvalid() {
|
|
throw std::invalid_argument("Invalid uri.");
|
|
|
|
}
|
|
|
|
static bool compare_name(const char*start, const char*end, const char*szName)
|
|
{
|
|
while (start != end)
|
|
{
|
|
if (*start != *szName) return false;
|
|
++start; ++szName;
|
|
}
|
|
return *szName == 0;
|
|
}
|
|
|
|
int uri::query_count() const
|
|
{
|
|
int count = 0;
|
|
const char*p = query_start;
|
|
while (p != query_end && *p != '#')
|
|
{
|
|
const char*nameStart = p;
|
|
while (p != query_end && *p != '&' && *p != '#')
|
|
{
|
|
++p;
|
|
}
|
|
if (p != query_end && *p == '&') ++p;
|
|
++count;
|
|
if (p == query_end || *p == '#')
|
|
{
|
|
break;
|
|
|
|
}
|
|
}
|
|
return count;
|
|
|
|
}
|
|
|
|
bool uri::has_query(const char*name) const
|
|
{
|
|
const char*p = query_start;
|
|
while (p != query_end && *p != '#')
|
|
{
|
|
const char*nameStart = p;
|
|
while (p != query_end && *p != '&' && *p != '=' && *p != '#')
|
|
{
|
|
++p;
|
|
}
|
|
// compare names.
|
|
if (compare_name(nameStart,p,name))
|
|
{
|
|
return true;
|
|
}
|
|
while (p != query_end && *p != '&' && *p != '#')
|
|
{
|
|
++p;
|
|
}
|
|
if (p != query_end && *p == '&') ++p;
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
query_segment uri::query(int index) const
|
|
{
|
|
const char*p = query_start;
|
|
int ix = 0;
|
|
while (p != query_end && *p != '#')
|
|
{
|
|
const char*nameStart = p;
|
|
while (p != query_end && *p != '&' && *p != '=' && *p != '#')
|
|
{
|
|
++p;
|
|
}
|
|
// compare names.
|
|
|
|
//if (compare_name(nameStart,p,name))
|
|
if (ix == index)
|
|
{
|
|
const char *nameEnd = p;
|
|
if (p != query_end && *p == '=')
|
|
{
|
|
++p;
|
|
const char*value_start = p;
|
|
while(p != query_end && *p != '&' && *p != '#')
|
|
{
|
|
++p;
|
|
}
|
|
return query_segment(
|
|
std::string(nameStart,nameEnd),
|
|
std::string(value_start,p)
|
|
);
|
|
|
|
} else {
|
|
return query_segment(
|
|
std::string(nameStart,nameEnd),
|
|
""
|
|
);
|
|
}
|
|
}
|
|
while (p != query_end && *p != '&' && *p != '#')
|
|
{
|
|
++p;
|
|
}
|
|
if (p != query_end && *p == '&')
|
|
{
|
|
++p;
|
|
}
|
|
++ix;
|
|
|
|
}
|
|
throw PiPedalArgumentException("Index out of range.");
|
|
}
|
|
|
|
std::string uri::query(const char*name) const
|
|
{
|
|
const char*p = query_start;
|
|
while (p != query_end && *p != '#')
|
|
{
|
|
const char*nameStart = p;
|
|
while (p != query_end && *p != '&' && *p != '=' && *p != '#')
|
|
{
|
|
++p;
|
|
}
|
|
// compare names.
|
|
if (compare_name(nameStart,p,name))
|
|
{
|
|
if (p != query_end && *p == '=')
|
|
{
|
|
++p;
|
|
const char*value_start = p;
|
|
while(p != query_end && *p != '&' && *p != '#')
|
|
{
|
|
++p;
|
|
}
|
|
return HtmlHelper::decode_url_segment(value_start,p);
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
while (p != query_end && *p != '&' && *p != '#')
|
|
{
|
|
++p;
|
|
}
|
|
if (p != query_end && *p == '&')
|
|
{
|
|
++p;
|
|
}
|
|
|
|
}
|
|
return "";
|
|
}
|
|
|
|
query_segment uri::query(int index)
|
|
{
|
|
const char*p = query_start;
|
|
int count = 0;
|
|
while (p != query_end && *p != '#')
|
|
{
|
|
if (count == index)
|
|
{
|
|
const char*nameStart = p;
|
|
while (p != query_end && *p != '&' && *p != '=' && *p != '#')
|
|
{
|
|
++p;
|
|
}
|
|
const char *nameEnd = p;
|
|
const char *valueStart, *valueEnd;
|
|
|
|
if(p == query_end || *p != '=')
|
|
{
|
|
valueStart = valueEnd = p;
|
|
} else {
|
|
++p;
|
|
valueStart = p;
|
|
|
|
while(p != query_end && *p != '&' && *p != '#')
|
|
{
|
|
++p;
|
|
}
|
|
valueEnd = p;
|
|
}
|
|
return query_segment(std::string(nameStart,nameEnd), HtmlHelper::decode_url_segment(valueStart,valueEnd));
|
|
} else {
|
|
while (p != query_end && *p != '&' && *p != '#')
|
|
{
|
|
++p;
|
|
}
|
|
|
|
}
|
|
if (p != query_end && *p == '&') ++p;
|
|
++count;
|
|
}
|
|
throw std::invalid_argument("Argument out of range.");
|
|
}
|
|
|
|
std::vector<std::string> uri::segments()
|
|
{
|
|
std::vector<std::string> result;
|
|
for (int i = 0; i < segment_count(); ++i)
|
|
{
|
|
result.push_back(segment(i));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::string uri::get_extension() const
|
|
{
|
|
const char*p = this->path_end;
|
|
while (p != this->path_start)
|
|
{
|
|
char c = p[-1];
|
|
if (c == '/') return "";
|
|
if (c == '.') {
|
|
--p;
|
|
return std::string(p,this->path_end-p);
|
|
}
|
|
--p;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
std::string uri::to_canonical_form() const {
|
|
uri_builder builder(*this);
|
|
return builder.str();
|
|
}
|
|
|
|
std::string uri_builder::str() const {
|
|
std::stringstream s;
|
|
if (scheme_.length() != 0)
|
|
{
|
|
s << scheme_ << ':';
|
|
}
|
|
if (authority_.length() != 0) {
|
|
s << "//";
|
|
if (user_.length() != 0)
|
|
{
|
|
s << user_ << '@';
|
|
}
|
|
s << authority_;
|
|
if (port_ != -1) {
|
|
s << ':' << (uint16_t)port_;
|
|
}
|
|
}
|
|
if ( authority_.length() != 0 || !isRelative_) { // canonical root form is http://xyz/ not httP://xyz
|
|
|
|
s << '/';
|
|
}
|
|
for (size_t i = 0; i < segments_.size(); ++i)
|
|
{
|
|
if (i != 0) s << '/';
|
|
HtmlHelper::encode_url_segment(s,segments_[i],false);
|
|
}
|
|
for (size_t i = 0; i < queries_.size(); ++i)
|
|
{
|
|
if (i == 0)
|
|
{
|
|
s << '?';
|
|
} else {
|
|
s << '&';
|
|
}
|
|
s << queries_[i].key << "=";
|
|
HtmlHelper::encode_url_segment(s,queries_[i].value,true);
|
|
}
|
|
if (fragment_.length() != 0)
|
|
{
|
|
s << "#" << fragment_;
|
|
}
|
|
return s.str();
|
|
|
|
} |