Many improvements and fixes to HTTP::URL class
This commit is contained in:
parent
f4242f23bf
commit
0afbaaaa41
2 changed files with 114 additions and 5 deletions
|
@ -27,14 +27,46 @@ HTTP::URL::URL(const std::string & url){
|
||||||
}
|
}
|
||||||
//proto_sep now points to the start of the host, guaranteed
|
//proto_sep now points to the start of the host, guaranteed
|
||||||
//continue by finding the path, if any
|
//continue by finding the path, if any
|
||||||
size_t first_slash = url.find('/', proto_sep);
|
size_t first_slash = url.find_first_of("/?#", proto_sep);
|
||||||
if (first_slash != std::string::npos){
|
if (first_slash != std::string::npos){
|
||||||
path = url.substr(first_slash+1);
|
if (url[first_slash] == '/'){
|
||||||
|
path = url.substr(first_slash+1);
|
||||||
|
}else{
|
||||||
|
path = url.substr(first_slash);
|
||||||
|
}
|
||||||
|
size_t hmark = path.find('#');
|
||||||
|
if (hmark != std::string::npos){
|
||||||
|
frag = path.substr(hmark+1);
|
||||||
|
path.erase(hmark);
|
||||||
|
}
|
||||||
size_t qmark = path.find('?');
|
size_t qmark = path.find('?');
|
||||||
if (qmark != std::string::npos){
|
if (qmark != std::string::npos){
|
||||||
args = path.substr(qmark+1);
|
args = path.substr(qmark+1);
|
||||||
path.erase(qmark);
|
path.erase(qmark);
|
||||||
}
|
}
|
||||||
|
if (path.size()){
|
||||||
|
size_t dots = path.find("/./");
|
||||||
|
while (dots != std::string::npos){
|
||||||
|
path.erase(dots, 2);
|
||||||
|
dots = path.find("/./");
|
||||||
|
}
|
||||||
|
dots = path.find("/../");
|
||||||
|
while (dots != std::string::npos){
|
||||||
|
size_t prevslash = path.rfind('/', dots-1);
|
||||||
|
if (prevslash == std::string::npos){
|
||||||
|
path.erase(0, dots+4);
|
||||||
|
}else{
|
||||||
|
path.erase(prevslash+1, dots-prevslash+3);
|
||||||
|
}
|
||||||
|
dots = path.find("/../");
|
||||||
|
}
|
||||||
|
if (path.substr(0, 2) == "./"){
|
||||||
|
path.erase(0, 2);
|
||||||
|
}
|
||||||
|
if (path.substr(0, 3) == "../"){
|
||||||
|
path.erase(0, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//host and port are now definitely between proto_sep and first_slash
|
//host and port are now definitely between proto_sep and first_slash
|
||||||
//we check for [ at the start because we may have an IPv6 address as host
|
//we check for [ at the start because we may have an IPv6 address as host
|
||||||
|
@ -60,8 +92,8 @@ HTTP::URL::URL(const std::string & url){
|
||||||
//"normal" host - first find port, if any
|
//"normal" host - first find port, if any
|
||||||
size_t colon = url.rfind(':', first_slash);
|
size_t colon = url.rfind(':', first_slash);
|
||||||
if (colon == std::string::npos || colon < proto_sep){
|
if (colon == std::string::npos || colon < proto_sep){
|
||||||
//no port. Assume 80
|
//no port. Assume default
|
||||||
port = "80";
|
port = "";
|
||||||
host = url.substr(proto_sep, first_slash-proto_sep);
|
host = url.substr(proto_sep, first_slash-proto_sep);
|
||||||
}else{
|
}else{
|
||||||
//we have a port number, read it
|
//we have a port number, read it
|
||||||
|
@ -83,10 +115,82 @@ HTTP::URL::URL(const std::string & url){
|
||||||
|
|
||||||
///Returns the port in numeric format
|
///Returns the port in numeric format
|
||||||
uint32_t HTTP::URL::getPort() const{
|
uint32_t HTTP::URL::getPort() const{
|
||||||
if (!port.size()){return 80;}
|
if (!port.size()){return getDefaultPort();}
|
||||||
return atoi(port.c_str());
|
return atoi(port.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Returns the default port for the protocol in numeric format
|
||||||
|
uint32_t HTTP::URL::getDefaultPort() const{
|
||||||
|
if (protocol == "https"){return 443;}
|
||||||
|
if (protocol == "rtmp"){return 1935;}
|
||||||
|
if (protocol == "dtsc"){return 4200;}
|
||||||
|
return 80;
|
||||||
|
}
|
||||||
|
|
||||||
|
///Returns the full URL in string format
|
||||||
|
std::string HTTP::URL::getUrl() const{
|
||||||
|
std::string ret;
|
||||||
|
if (protocol.size()){
|
||||||
|
ret = protocol + "://" + host;
|
||||||
|
}else{
|
||||||
|
ret = "//" + host;
|
||||||
|
}
|
||||||
|
if (port.size() && getPort() != getDefaultPort()){ret += ":" + port;}
|
||||||
|
ret += "/";
|
||||||
|
if (path.size()){ret += path;}
|
||||||
|
if (args.size()){ret += "?" + args;}
|
||||||
|
if (frag.size()){ret += "#" + frag;}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
///Returns the URL in string format without args and frag
|
||||||
|
std::string HTTP::URL::getBareUrl() const{
|
||||||
|
std::string ret;
|
||||||
|
if (protocol.size()){
|
||||||
|
ret = protocol + "://" + host;
|
||||||
|
}else{
|
||||||
|
ret = "//" + host;
|
||||||
|
}
|
||||||
|
if (port.size() && getPort() != getDefaultPort()){ret += ":" + port;}
|
||||||
|
ret += "/";
|
||||||
|
if (path.size()){ret += path;}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
///Returns a URL object for the given link, resolved relative to the current URL object.
|
||||||
|
HTTP::URL HTTP::URL::link(const std::string &l){
|
||||||
|
//Full link
|
||||||
|
if (l.find("://") < l.find('/') && l.find('/' != std::string::npos)){
|
||||||
|
DONTEVEN_MSG("Full link: %s", l.c_str());
|
||||||
|
return URL(l);
|
||||||
|
}
|
||||||
|
//Absolute link
|
||||||
|
if (l[0] == '/'){
|
||||||
|
DONTEVEN_MSG("Absolute link: %s", l.c_str());
|
||||||
|
if (l.size() > 1 && l[1] == '/'){
|
||||||
|
//Same-protocol full link
|
||||||
|
return URL(protocol+":"+l);
|
||||||
|
}else{
|
||||||
|
//Same-domain/port absolute link
|
||||||
|
URL tmp = *this;
|
||||||
|
tmp.args.clear();
|
||||||
|
tmp.path = l.substr(1);
|
||||||
|
//Abuse the fact that we don't check for arguments in getUrl()
|
||||||
|
return URL(tmp.getUrl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Relative link
|
||||||
|
std::string tmpUrl = getBareUrl();
|
||||||
|
size_t slashPos = tmpUrl.rfind('/');
|
||||||
|
if (slashPos == std::string::npos){
|
||||||
|
tmpUrl += "/";
|
||||||
|
}else{
|
||||||
|
tmpUrl.erase(slashPos+1);
|
||||||
|
}
|
||||||
|
DONTEVEN_MSG("Relative link: %s+%s", tmpUrl.c_str(), l.c_str());
|
||||||
|
return URL(tmpUrl+l);
|
||||||
|
}
|
||||||
|
|
||||||
/// This constructor creates an empty HTTP::Parser, ready for use for either reading or writing.
|
/// This constructor creates an empty HTTP::Parser, ready for use for either reading or writing.
|
||||||
/// All this constructor does is call HTTP::Parser::Clean().
|
/// All this constructor does is call HTTP::Parser::Clean().
|
||||||
HTTP::Parser::Parser() {
|
HTTP::Parser::Parser() {
|
||||||
|
|
|
@ -74,11 +74,16 @@ namespace HTTP {
|
||||||
public:
|
public:
|
||||||
URL(const std::string & url);
|
URL(const std::string & url);
|
||||||
uint32_t getPort() const;
|
uint32_t getPort() const;
|
||||||
|
uint32_t getDefaultPort() const;
|
||||||
|
std::string getUrl() const;
|
||||||
|
std::string getBareUrl() const;
|
||||||
std::string host;///< Hostname or IP address of URL
|
std::string host;///< Hostname or IP address of URL
|
||||||
std::string protocol;///<Protocol of URL
|
std::string protocol;///<Protocol of URL
|
||||||
std::string port;///<Port of URL
|
std::string port;///<Port of URL
|
||||||
std::string path;///<Path after the first slash (not inclusive) but before any question mark
|
std::string path;///<Path after the first slash (not inclusive) but before any question mark
|
||||||
std::string args;///<Everything after the question mark in the path, if it was present
|
std::string args;///<Everything after the question mark in the path, if it was present
|
||||||
|
std::string frag;///<Everything after the # in the path, if it was present
|
||||||
|
URL link(const std::string &l);
|
||||||
};
|
};
|
||||||
|
|
||||||
}//HTTP namespace
|
}//HTTP namespace
|
||||||
|
|
Loading…
Add table
Reference in a new issue