From f53eda26a3160972908f15c2427a60b0b82fbe87 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sat, 8 Aug 2009 21:41:01 +0000 Subject: [PATCH] Add functions for parsing and building URIs and query strings Use the new parse_url function in Request::from_url Add urlencode_plus function Support POST method Three different encoding levels for urlencode Add Message::has_header to check for header existence without exceptions --- source/message.cpp | 5 +++ source/message.h | 1 + source/request.cpp | 29 +++++++------ source/server.cpp | 2 +- source/utils.cpp | 100 ++++++++++++++++++++++++++++++++++++++++++--- source/utils.h | 26 +++++++++++- 6 files changed, 143 insertions(+), 20 deletions(-) diff --git a/source/message.cpp b/source/message.cpp index 93937c9..4aaa8bf 100644 --- a/source/message.cpp +++ b/source/message.cpp @@ -26,6 +26,11 @@ void Message::set_header(const string &hdr, const string &val) headers[normalize_header_name(hdr)]=val; } +bool Message::has_header(const string &hdr) const +{ + return headers.count(normalize_header_name(hdr)); +} + const string &Message::get_header(const string &hdr) const { HeaderMap::const_iterator i=headers.find(normalize_header_name(hdr)); diff --git a/source/message.h b/source/message.h index ae38ffa..6e139bd 100644 --- a/source/message.h +++ b/source/message.h @@ -33,6 +33,7 @@ public: virtual ~Message() { } void set_header(const std::string &, const std::string &); + bool has_header(const std::string &) const; const std::string &get_header(const std::string &) const; void add_content(const std::string &); const std::string &get_content() const { return content; } diff --git a/source/request.cpp b/source/request.cpp index bcf36c9..6bad370 100644 --- a/source/request.cpp +++ b/source/request.cpp @@ -9,6 +9,7 @@ Distributed under the LGPL #include #include #include "request.h" +#include "utils.h" using namespace std; @@ -45,23 +46,25 @@ Request Request::parse(const string &str) return result; } -Request Request::from_url(const string &url) +Request Request::from_url(const string &str) { - if(RegMatch match=Regex("^http://([a-zA-Z0-9.-]+(:[0-9]+)?)(/[^ #]*)?$").match(url)) + Url url=parse_url(str); + if(url.scheme!="http") + throw InvalidParameterValue("Only http scheme is supported"); + string path=url.path; + if(path.empty()) + path="/"; + if(!url.query.empty()) { - string host=match[1].str; - string path=match[3].str; - if(path.empty()) - path="/"; + path+='?'; + path+=url.query; + } - Request result("GET", path); - result.set_header("Host", host); - result.set_header("Connection", "close"); + Request result("GET", path); + result.set_header("Host", url.host); + result.set_header("Connection", "close"); - return result; - } - else - throw InvalidParameterValue("Invalid URL"); + return result; } } // namespace Http diff --git a/source/server.cpp b/source/server.cpp index 04eea52..2e427b9 100644 --- a/source/server.cpp +++ b/source/server.cpp @@ -103,7 +103,7 @@ void Server::client_data_available(Client &cl) unsigned colon=addr_str.find(':'); cl.request->set_header("-Client-Host", addr_str.substr(0, colon)); - if(cl.request->get_method()!="GET") + if(cl.request->get_method()!="GET" && cl.request->get_method()!="POST") { response=new Response(NOT_IMPLEMENTED); response->add_content("Method not implemented"); diff --git a/source/utils.cpp b/source/utils.cpp index d550259..b6340db 100644 --- a/source/utils.cpp +++ b/source/utils.cpp @@ -5,18 +5,26 @@ Copyright © 2009 Mikko Rasa, Mikkosoft Productions Distributed under the LGPL */ +#include #include +#include +#include #include "utils.h" using namespace std; namespace { -const char reserved[]=" :/?#[]@!$&'()*+,;=%"; +const char *reserved[]= +{ + " #%&+=?", + " #%&*+:;=?@[]", + " !#$%&'()*+,/:;=?@[]", +}; -bool is_reserved(char c) +bool is_reserved(char c, unsigned level) { - for(const char *r=reserved; *r; ++r) + for(const char *r=reserved[level]; *r; ++r) if(c==*r) return true; return false; @@ -27,12 +35,27 @@ bool is_reserved(char c) namespace Msp { namespace Http { -string urlencode(const string &str) +string urlencode(const string &str, EncodeLevel level) { string result; for(string::const_iterator i=str.begin(); i!=str.end(); ++i) { - if(is_reserved(*i)) + if(is_reserved(*i, level)) + result+=format("%%%02X", *i); + else + result+=*i; + } + return result; +} + +string urlencode_plus(const string &str, EncodeLevel level) +{ + string result; + for(string::const_iterator i=str.begin(); i!=str.end(); ++i) + { + if(*i==' ') + result+='+'; + else if(is_reserved(*i, level)) result+=format("%%%02X", *i); else result+=*i; @@ -61,5 +84,72 @@ string urldecode(const string &str) return result; } +Url parse_url(const string &str) +{ + static Regex r_url("(([a-z]+)://)?([a-zA-Z0-9-]+(\\.[a-zA-Z0-9-]+)*(:[0-9])?)?(/[^?#]*)?(\\?([^#]+))?(#(.*))?"); + if(RegMatch m=r_url.match(str)) + { + Url url; + url.scheme=m[2].str; + url.host=m[3].str; + url.path=urldecode(m[6].str); + url.query=m[8].str; + url.fragment=m[10].str; + return url; + } + else + throw InvalidParameterValue("Invalid URL"); +} + +string build_url(const Url &url) +{ + if(!url.path.empty() && url.path[0]!='/') + throw InvalidParameterValue("Only absolute paths are supported"); + string str; + if(!url.scheme.empty()) + str+=url.scheme+"://"; + str+=url.host; + str+=urlencode(url.path); + if(!url.query.empty()) + { + str+='?'; + str+=url.query; + } + if(!url.fragment.empty()) + { + str+='#'; + str+=url.fragment; + } + return str; +} + +Query parse_query(const std::string &str) +{ + vector parts=split(str, '&'); + Query query; + for(vector::const_iterator i=parts.begin(); i!=parts.end(); ++i) + { + unsigned equals=i->find('='); + string &value=query[urldecode(i->substr(0, equals))]; + if(equals!=string::npos) + value=urldecode(i->substr(equals+1)); + } + return query; +} + +string build_query(const Query &query) +{ + string str; + for(Query::const_iterator i=query.begin(); i!=query.end(); ++i) + { + if(i!=query.begin()) + str+='&'; + str+=urlencode_plus(i->first); + str+='='; + str+=urlencode_plus(i->second); + } + return str; +} + } // namespace Http } // namespace Msp diff --git a/source/utils.h b/source/utils.h index 76127a4..559638a 100644 --- a/source/utils.h +++ b/source/utils.h @@ -8,13 +8,37 @@ Distributed under the LGPL #ifndef MSP_HTTP_UTILS_H_ #define MSP_HTTP_UTILS_H_ +#include #include namespace Msp { namespace Http { -std::string urlencode(const std::string &); +enum EncodeLevel +{ + MINIMAL, + SAFE, + PARANOID +}; + +struct Url +{ + std::string scheme; + std::string host; + std::string path; + std::string query; + std::string fragment; +}; + +typedef std::map Query; + +std::string urlencode(const std::string &, EncodeLevel =SAFE); +std::string urlencode_plus(const std::string &, EncodeLevel =SAFE); std::string urldecode(const std::string &); +Url parse_url(const std::string &); +std::string build_url(const Url &); +Query parse_query(const std::string &); +std::string build_query(const Query &); } // namespace Http } // namespace Msp -- 2.45.2