First commit.
This commit is contained in:
+449
@@ -0,0 +1,449 @@
|
||||
#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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
if (queries_.size() != 0)
|
||||
{
|
||||
s << '?';
|
||||
for (size_t i = 0; i < queries_.size(); ++i)
|
||||
{
|
||||
s << queries_[i].key << "=";
|
||||
HtmlHelper::encode_url_segment(s,queries_[i].value,true);
|
||||
}
|
||||
|
||||
}
|
||||
if (fragment_.length() != 0)
|
||||
{
|
||||
s << "#" << fragment_;
|
||||
}
|
||||
return s.str();
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user