}; //!< Stringify the cookie
};
+ /*! Implements a Cookie jar for storing multiple cookies */
class CookieJar {
public:
- std::map<std::string, Cookie> cookies;
+ std::map<std::string, Cookie> cookies; //<! cookie container
- CookieJar() {};
+ CookieJar() {}; //<! constructs empty cookie jar
CookieJar(const CookieJar & rhs) {
this->cookies = rhs.cookies;
- }
+ } //<! copy cookies from another cookie jar
void keyValuePair(const std::string &keyvalue, std::string &key, std::string &value) {
size_t pos;
if (pos == std::string::npos) throw "Not a Key-Value pair (cookie)";
key = std::string(keyvalue.begin(), keyvalue.begin()+pos);
value = std::string(keyvalue.begin()+pos+1, keyvalue.end());
- }
+ } //<! key value pair parser
void parseCookieHeader(const std::string &cookiestr) {
std::list<Cookie> cookies;
for(std::list<Cookie>::iterator i = cookies.begin(); i != cookies.end(); i++) {
this->cookies[i->name] = *i;
}
- };
+ }; //<! Parse multiple cookies from header
};
};
#include <exception>
namespace YaHTTP {
+ /*! Generic error class */
class Error: public std::exception {
public:
Error() {};
{
return reason.c_str();
}
- const std::string reason;
+ const std::string reason; //<! Cause of the error
};
+ /*! Parse error class */
class ParseError: public YaHTTP::Error {
public:
ParseError() {};
std::string getparms;
// prepare URL
for(strstr_map_t::const_iterator i = getvars.begin(); i != getvars.end(); i++) {
- getparmbuf << Utility::encodeURL(i->first) << "=" << Utility::encodeURL(i->second) << "&";
+ getparmbuf << Utility::encodeURL(i->first, false) << "=" << Utility::encodeURL(i->second, false) << "&";
}
if (getparmbuf.str().length() > 0)
getparms = "?" + std::string(getparmbuf.str().begin(), getparmbuf.str().end() - 1);
#define YAHTTP_TYPE_RESPONSE 2
namespace YaHTTP {
- typedef std::map<std::string,std::string> strstr_map_t;
- typedef std::map<std::string,Cookie> strcookie_map_t;
+ typedef std::map<std::string,std::string> strstr_map_t; //<! String to String map
+ typedef std::map<std::string,Cookie> strcookie_map_t; //<! String to Cookie map
typedef enum {
urlencoded,
multipart
- } postformat_t;
+ } postformat_t; //<! Enumeration of possible post encodings, url encoding or multipart
+ /*! Base class for request and response */
class HTTPBase {
public:
#ifdef HAVE_CPP_FUNC_PTR
+ /*! Default renderer for request/response, simply copies body to response */
class SendBodyRender {
public:
SendBodyRender() {};
size_t operator()(const HTTPBase *doc, std::ostream& os) const {
os << doc->body;
return doc->body.length();
- };
+ }; //<! writes body to ostream and returns length
};
+ /* Simple sendfile renderer which streams file to ostream */
class SendFileRender {
public:
SendFileRender(const std::string& path) {
this->path = path;
};
- size_t operator()(const HTTPBase *doc, std::ostream& os) const {
+ size_t operator()(const HTTPBase *doc __attribute__((unused)), std::ostream& os) const {
char buf[4096];
size_t n,k;
#ifdef HAVE_CXX11
}
return n;
- };
+ }; //<! writes file to ostream and returns length
- std::string path;
+ std::string path; //<! File to send
};
#endif
HTTPBase() {
+ kind = 0;
+ status = 0;
#ifdef HAVE_CPP_FUNC_PTR
renderer = SendBodyRender();
#endif
return *this;
};
public:
- URL url;
- int kind;
- int status;
- std::string statusText;
- std::string method;
- strstr_map_t headers;
- CookieJar jar;
- strstr_map_t postvars;
- strstr_map_t getvars;
+ URL url; //<! URL of this request/response
+ int kind; //<! Type of object (1 = request, 2 = response)
+ int status; //<! status code
+ std::string statusText; //<! textual representation of status code
+ std::string method; //<! http verb
+ strstr_map_t headers; //<! map of header(s)
+ CookieJar jar; //<! cookies
+ strstr_map_t postvars; //<! map of POST variables (from POST body)
+ strstr_map_t getvars; //<! map of GET variables (from URL)
// these two are for Router
- strstr_map_t parameters;
- std::string routeName;
+ strstr_map_t parameters; //<! map of route parameters (only if you use YaHTTP::Router)
+ std::string routeName; //<! name of the current route (only if you use YaHTTP::Router)
- std::string body;
+ std::string body; //<! the actual content
#ifdef HAVE_CPP_FUNC_PTR
- funcptr::function<size_t(const HTTPBase*,std::ostream&)> renderer;
+ funcptr::function<size_t(const HTTPBase*,std::ostream&)> renderer; //<! rendering function
#endif
- void write(std::ostream& os) const;
+ void write(std::ostream& os) const; //<! writes request to the given output stream
- strstr_map_t& GET() { return getvars; };
- strstr_map_t& POST() { return postvars; };
- strcookie_map_t& COOKIES() { return jar.cookies; };
+ strstr_map_t& GET() { return getvars; }; //<! acccessor for getvars
+ strstr_map_t& POST() { return postvars; }; //<! accessor for postvars
+ strcookie_map_t& COOKIES() { return jar.cookies; }; //<! accessor for cookies
+
+ std::string str() const {
+ std::ostringstream oss;
+ write(oss);
+ return oss.str();
+ }; //<! return string representation of this object
};
+ /*! Response class, represents a HTTP Response document */
class Response: public HTTPBase {
public:
Response() { this->kind = YAHTTP_TYPE_RESPONSE; };
HTTPBase::operator=(rhs);
this->kind = YAHTTP_TYPE_RESPONSE;
return *this;
- }
+ };
friend std::ostream& operator<<(std::ostream& os, const Response &resp);
friend std::istream& operator>>(std::istream& is, Response &resp);
};
+ /* Request class, represents a HTTP Request document */
class Request: public HTTPBase {
public:
Request() { this->kind = YAHTTP_TYPE_REQUEST; };
HTTPBase::operator=(rhs);
this->kind = YAHTTP_TYPE_REQUEST;
return *this;
- }
+ };
void setup(const std::string& method, const std::string& url) {
this->url.parse(url);
this->method = method;
std::transform(this->method.begin(), this->method.end(), this->method.begin(), ::toupper);
this->headers["user-agent"] = "YaHTTP v1.0";
- }
+ }; //<! Set some initial things for a request
void preparePost(postformat_t format = urlencoded) {
std::ostringstream postbuf;
if (format == urlencoded) {
for(strstr_map_t::const_iterator i = POST().begin(); i != POST().end(); i++) {
- postbuf << Utility::encodeURL(i->first) << "=" << Utility::encodeURL(i->second) << "&";
+ postbuf << Utility::encodeURL(i->first, false) << "=" << Utility::encodeURL(i->second, false) << "&";
}
// remove last bit
if (postbuf.str().length()>0)
- body = std::string(postbuf.str().begin(), postbuf.str().end()-1);
+ body = postbuf.str().substr(0, postbuf.str().length()-1);
else
body = "";
headers["content-type"] = "application/x-www-form-urlencoded; charset=utf-8";
} else if (format == multipart) {
headers["content-type"] = "multipart/form-data; boundary=YaHTTP-12ca543";
for(strstr_map_t::const_iterator i = POST().begin(); i != POST().end(); i++) {
- postbuf << "--YaHTTP-12ca543\r\nContent-Disposition: form-data; name=\"" << Utility::encodeURL(i->first) << "; charset=UTF-8\r\n\r\n"
- << Utility::encodeURL(i->second) << "\r\n";
+ postbuf << "--YaHTTP-12ca543\r\nContent-Disposition: form-data; name=\"" << Utility::encodeURL(i->first, false) << "; charset=UTF-8\r\n\r\n"
+ << Utility::encodeURL(i->second, false) << "\r\n";
}
}
+ postbuf.str("");
+ postbuf << body.length();
// set method and change headers
method = "POST";
- headers["content-length"] = body.length();
- };
+ headers["content-length"] = postbuf.str();
+ }; //<! convert all postvars into string and stuff it into body
friend std::ostream& operator<<(std::ostream& os, const Request &resp);
friend std::istream& operator>>(std::istream& is, Request &resp);
};
+ /*! Asynchronous HTTP document loader */
template <class T>
class AsyncLoader {
public:
- T* target;
- int state;
- size_t pos;
+ T* target; //<! target to populate
+ int state; //<! reader state
+ size_t pos; //<! reader position
- std::string buffer;
- bool chunked;
- int chunk_size;
- std::ostringstream bodybuf;
- size_t maxbody;
- size_t minbody;
- bool hasBody;
+ std::string buffer; //<! read buffer
+ bool chunked; //<! whether we are parsing chunked data
+ int chunk_size; //<! expected size of next chunk
+ std::ostringstream bodybuf; //<! buffer for body
+ size_t maxbody; //<! maximum size of body
+ size_t minbody; //<! minimum size of body
+ bool hasBody; //<! are we expecting body
- void keyValuePair(const std::string &keyvalue, std::string &key, std::string &value);
+ void keyValuePair(const std::string &keyvalue, std::string &key, std::string &value); //<! key value pair parser helper
void initialize(T* target) {
chunked = false; chunk_size = 0;
bodybuf.str(""); maxbody = 0;
pos = 0; state = 0; this->target = target;
hasBody = false;
- };
- int feed(const std::string& somedata);
+ }; //<! Initialize the parser for target and clear state
+ int feed(const std::string& somedata); //<! Feed data to the parser
bool ready() { return state > 1 &&
(!hasBody ||
(bodybuf.str().size() <= maxbody &&
bodybuf.str().size() >= minbody)
);
- };
+ }; //<! whether we have received enough data
void finalize() {
bodybuf.flush();
if (ready()) {
}
bodybuf.str("");
this->target = NULL;
- };
+ }; //<! finalize and release target
};
+ /*! Asynchronous HTTP response loader */
class AsyncResponseLoader: public AsyncLoader<Response> {
};
+ /*! Asynchronous HTTP request loader */
class AsyncRequestLoader: public AsyncLoader<Request> {
};
#endif
namespace YaHTTP {
+ /*! URL parser and container */
class URL {
private:
bool parseSchema(const std::string& url, size_t &pos) {
pos += 2;
}
return true;
- }
+ }; //<! parse schema/protocol part
bool parseHost(const std::string& url, size_t &pos) {
size_t pos1;
host = url.substr(pos, pos1-pos);
pos = pos1;
}
+ if ( (pos1 = host.find_first_of(":")) != std::string::npos ) {
+ std::istringstream tmp(host.substr(pos1+1));
+ tmp >> port;
+ host = host.substr(0, pos1);
+ }
return true;
- }
+ }; //<! parse host and port
bool parseUserPass(const std::string& url, size_t &pos) {
size_t pos1,pos2;
pos = pos1+1;
username = Utility::decodeURL(username);
return true;
- };
+ }; //<! parse possible username and password
bool parsePath(const std::string& url, size_t &pos) {
size_t pos1;
pos = pos1;
}
return true;
- }
+ }; //<! parse path component
bool parseParameters(const std::string& url, size_t &pos) {
size_t pos1;
}
if (parameters.size()>0 && *(parameters.end()-1) == '&') parameters.resize(parameters.size()-1);
return true;
- }
+ }; //<! parse url parameters
bool parseAnchor(const std::string& url, size_t &pos) {
if (pos >= url.size()) return true; // no data
if (url[pos] != '#') return false; // not anchor
anchor = url.substr(pos+1);
return true;
- }
+ }; //<! parse anchor
void initialize() {
protocol = ""; host = ""; port = 0; username = ""; password = ""; path = ""; parameters = ""; anchor =""; pathless = true;
- }
+ }; //<! initialize to empty URL
public:
std::string to_string() const {
if (anchor.empty() == false)
oss << "#" << anchor;
return oss.str();
- }
-
- std::string protocol;
- std::string host;
- int port;
- std::string username;
- std::string password;
- std::string path;
- std::string parameters;
- std::string anchor;
- bool pathless;
-
- URL() { initialize(); };
+ }; //<! convert this URL to string
+
+ std::string protocol; //<! schema/protocol
+ std::string host; //<! host
+ int port; //<! port
+ std::string username; //<! username
+ std::string password; //<! password
+ std::string path; //<! path
+ std::string parameters; //<! url parameters
+ std::string anchor; //<! anchor
+ bool pathless; //<! whether this url has no path
+
+ URL() { initialize(); }; //<! construct empty url
URL(const std::string& url) {
parse(url);
- };
+ }; //<! calls parse with url
URL(const char *url) {
parse(std::string(url));
- };
+ }; //<! calls parse with url
bool parse(const std::string& url) {
// setup
if (parsePath(url, pos) == false) return false;
if (parseParameters(url, pos) == false) return false;
return parseAnchor(url, pos);
- };
+ }; /*! parse various formats of urls ranging from http://example.com/foo?bar=baz into data:base64:d089swt64wt... */
friend std::ostream & operator<<(std::ostream& os, const URL& url) {
os<<url.to_string();
return os;
- }
+ };
};
};
#endif
#define _YAHTTP_UTILITY_HPP 1
namespace YaHTTP {
- static const char *MONTHS[] = {0,"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",0};
- static const char *DAYS[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat",0};
+ static const char *MONTHS[] = {0,"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",0}; //<! List of months
+ static const char *DAYS[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat",0}; //<! List of days
+ /*! Represents a date/time with utc offset */
class DateTime {
public:
- bool isSet;
+ bool isSet; //<! if this is initialized yet
- int year;
+ int year; //<! year, 0 is year 0, not 1900
- int month;
- int day;
- int wday;
+ int month; //<! month, range 1-12
+ int day; //<! day, range 1-31
+ int wday; //<! week day, range 1-7
- int hours;
- int minutes;
- int seconds;
+ int hours; //<! hours, range 0-23
+ int minutes; //<! minutes, range 0-59
+ int seconds; //<! seconds, range 0-60
- int utc_offset;
+ int utc_offset; //<! UTC offset with minutes (hhmm)
DateTime() {
initialize();
- };
+ }; //<! Construct and initialize
void initialize() {
isSet = false;
year = month = day = wday = hours = minutes = seconds = utc_offset = 0;
month = 1; // it's invalid otherwise
- };
+ }; //<! Creates year 0 date
void setLocal() {
fromLocaltime(time((time_t*)NULL));
- };
+ }; //<! sets current local time
void setGm() {
fromGmtime(time((time_t*)NULL));
- }
+ }; //<! sets current gmtime (almost UTC)
void fromLocaltime(time_t t) {
#ifdef HAVE_LOCALTIME_R
tm = localtime(&t);
fromTm(tm);
#endif
- };
+ }; //<! uses localtime for time
void fromGmtime(time_t t) {
#ifdef HAVE_GMTIME_R
tm = gmtime(&t);
fromTm(tm);
#endif
- };
+ }; //<! uses gmtime for time
void fromTm(const struct tm *tm) {
year = tm->tm_year + 1900;
wday = tm->tm_wday;
utc_offset = tm->tm_gmtoff;
isSet = true;
- };
+ }; //<! parses date from struct tm
void validate() const {
if (wday < 0 || wday > 6) throw "Invalid date";
if (hours < 0 || hours > 23 ||
minutes < 0 || minutes > 59 ||
seconds < 0 || seconds > 60) throw "Invalid date";
- }
+ }; //<! make sure we are within ranges (not a *REAL* validation, just range check)
std::string rfc_str() const {
std::ostringstream oss;
oss << std::setfill('0') << std::setw(2) << (tmp_off%3600)/60;
return oss.str();
- };
+ }; //<! converts this date into a RFC-822 format
std::string cookie_str() const {
std::ostringstream oss;
std::setfill('0') << std::setw(2) << minutes << ":" <<
std::setfill('0') << std::setw(2) << seconds << " GMT";
return oss.str();
- }
+ }; //<! converts this date into a HTTP Cookie date
void parse822(const std::string &rfc822_date) {
- char *pos;
struct tm tm;
- if ( (pos = strptime(rfc822_date.c_str(), "%a, %d %b %Y %T %z", &tm)) != NULL) {
+ if ( (strptime(rfc822_date.c_str(), "%a, %d %b %Y %T %z", &tm)) != NULL) {
fromTm(&tm);
} else {
throw "Unparseable date";
}
- };
+ }; //<! parses RFC-822 date
void parseCookie(const std::string &cookie_date) {
- char *pos;
struct tm tm;
- if ( (pos = strptime(cookie_date.c_str(), "%d-%b-%Y %T %Z", &tm)) != NULL) {
+ if ( (strptime(cookie_date.c_str(), "%d-%b-%Y %T %Z", &tm)) != NULL) {
fromTm(&tm);
} else {
throw "Unparseable date";
}
- };
+ }; //<! parses HTTP Cookie date
int unixtime() const {
struct tm tm;
tm.tm_sec = seconds;
tm.tm_gmtoff = utc_offset;
return mktime(&tm);
- }
-
+ }; //<! returns this datetime as unixtime. will not work for dates before 1970/1/1 00:00:00 GMT
};
-
+
+ /*! Various helpers needed in the code */
class Utility {
public:
static std::string decodeURL(const std::string& component) {
pos2=pos1;
}
return result;
- };
+ }; //<! Decodes %xx from string into bytes
static std::string encodeURL(const std::string& component, bool asUrl = true) {
std::string result = component;
}
}
return result;
- };
+ }; //<! Escapes any characters into %xx representation when necessary, set asUrl to false to fully encode the url
static std::string encodeURL(const std::wstring& component, bool asUrl = true) {
unsigned char const *p = reinterpret_cast<unsigned char const*>(&component[0]);
} else result << (char)*iter;
}
return result.str();
- };
-
-
+ }; //<! Escapes any characters into %xx representation when necessary, set asUrl to false to fully encode the url, for wide strings, returns ordinary string
static std::string status2text(int status) {
switch(status) {
default:
return "Unknown Status";
}
- };
+ }; //<! static HTTP codes to text mappings
static std::map<std::string,std::string> parseUrlParameters(std::string parameters) {
std::string::size_type pos = 0;
pos = nextpos+1;
}
return parameter_map;
- };
+ }; //<! parses URL parameters into string map
static bool iequals(const std::string& a, const std::string& b, size_t length) {
std::string::const_iterator ai, bi;
(ai != a.end() && bi == b.end())) return false;
return ::toupper(*ai) == ::toupper(*bi);
- }
+ }; //<! case-insensitive comparison with length
static bool iequals(const std::string& a, const std::string& b) {
if (a.size() != b.size()) return false;
return iequals(a,b,a.size());
- }
+ }; //<! case-insensitive comparison
static void trimRight(std::string &str) {
const std::locale &loc = std::locale::classic();
std::string::reverse_iterator iter = str.rbegin();
while(iter != str.rend() && std::isspace(*iter, loc)) iter++;
str.erase(iter.base(), str.end());
- };
+ }; //<! removes whitespace from right
static std::string camelizeHeader(const std::string &str) {
std::string::const_iterator iter = str.begin();
}
return result;
- };
- };
+ }; //<! camelizes headers, such as, content-type => Content-Type
+ };
};
#endif