pdns/Makefile
codedocs/Makefile
pdns/pdns
+ pdns/ext/yahttp/Makefile
+ pdns/ext/yahttp/yahttp/Makefile
modules/bindbackend/Makefile
modules/db2backend/Makefile
modules/geobackend/Makefile
dns_random.o ext/polarssl-1.3.2/library/aes.o ext/polarssl-1.3.2/library/padlock.o dnslabeltext.o \
lua-pdns.o lua-recursor.o randomhelper.o recpacketcache.o dns.o \
reczones.o base32.o nsecrecords.o json.o ws-recursor.o ws-api.o \
-version.o responsestats.o webserver.o ext/yahttp/yahttp/reqresp.o \
+version.o responsestats.o webserver.o ext/yahttp/yahttp/reqresp.o ext/yahttp/yahttp/router.o \
rec-carbon.o
REC_CONTROL_OBJECTS=rec_channel.o rec_control.o arguments.o misc.o \
yahttp/libyahttp.a
*.o
+Makefile
+Makefile.in
+++ /dev/null
-.SILENT:
-
-all:
- $(MAKE) -C yahttp all
-
-install:
-
-uninstall:
-
-distclean: clean
-
-clean:
- $(MAKE) -C yahttp clean
-
-check:
-
-distdir:
- mkdir -p $(distdir)
- cp LICENSE Makefile $(distdir)
- mkdir $(distdir)/yahttp
- cp yahttp/Makefile yahttp/*.cpp yahttp/*.hpp $(distdir)/yahttp
-
--- /dev/null
+SUBDIRS=yahttp
+++ /dev/null
-
-all: libyahttp.a
-
-
-clean:
- -rm -f *.o *.a
-
-
-reqresp.o: *.cpp *.hpp
-
-
-libyahttp.a: reqresp.o
- echo AR $@
- $(AR) rcs $@ $<
--- /dev/null
+noinst_LTLIBRARIES=libyahttp.la
+
+libyahttp_la_SOURCES=cookie.hpp exception.hpp reqresp.cpp reqresp.hpp router.cpp router.hpp url.hpp utility.hpp yahttp-config.h yahttp.hpp
--- /dev/null
+namespace YaHTTP {
+ /*! Implements a single cookie */
+ class Cookie {
+ public:
+ Cookie() {
+ secure = false;
+ httponly = false;
+ name = value = "";
+ }; //!< Set the cookie to empty value
+
+ Cookie(const Cookie &rhs) {
+ domain = rhs.domain;
+ path = rhs.path;
+ secure = rhs.secure;
+ httponly = rhs.httponly;
+ name = rhs.name;
+ value = rhs.value;
+ }; //<! Copy cookie values
+
+ DateTime expires; /*!< Expiration date */
+ std::string domain; /*!< Domain where cookie is valid */
+ std::string path; /*!< Path where the cookie is valid */
+ bool httponly; /*!< Whether the cookie is for server use only */
+ bool secure; /*!< Whether the cookie is for HTTPS only */
+
+ std::string name; /*!< Cookie name */
+ std::string value; /*!< Cookie value */
+
+ std::string str() const {
+ std::ostringstream oss;
+ oss << YaHTTP::Utility::encodeURL(name) << "=" << YaHTTP::Utility::encodeURL(value);
+ if (expires.isSet)
+ oss << "; expires=" << expires.cookie_str();
+ if (domain.size()>0)
+ oss << "; domain=" << domain;
+ if (path.size()>0)
+ oss << "; path=" << path;
+ if (secure)
+ oss << "; secure";
+ if (httponly)
+ oss << "; httpOnly";
+ return oss.str();
+ }; //!< Stringify the cookie
+ };
+
+ class CookieJar {
+ public:
+ std::map<std::string, Cookie> cookies;
+
+ CookieJar() {};
+ CookieJar(const CookieJar & rhs) {
+ this->cookies = rhs.cookies;
+ }
+
+ void keyValuePair(const std::string &keyvalue, std::string &key, std::string &value) {
+ size_t pos;
+ pos = keyvalue.find("=");
+ 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());
+ }
+
+ void parseCookieHeader(const std::string &cookiestr) {
+ std::list<Cookie> cookies;
+ int cstate = 0; //cookiestate
+ size_t pos,npos;
+ pos = 0;
+ cstate = 0;
+ while(pos < cookiestr.size()) {
+ if (cookiestr.compare(pos, 7, "expires") ==0 ||
+ cookiestr.compare(pos, 6, "domain") ==0 ||
+ cookiestr.compare(pos, 4, "path") ==0) {
+ cstate = 1;
+ // get the date
+ std::string key, value, s;
+ npos = cookiestr.find("; ", pos);
+ if (npos == std::string::npos) {
+ // last value
+ s = std::string(cookiestr.begin() + pos + 1, cookiestr.end());
+ pos = cookiestr.size();
+ } else {
+ s = std::string(cookiestr.begin() + pos + 1, cookiestr.begin() + npos - 1);
+ pos = npos+2;
+ }
+ keyValuePair(s, key, value);
+ if (s == "expires") {
+ DateTime dt;
+ dt.parseCookie(value);
+ for(std::list<Cookie>::iterator i = cookies.begin(); i != cookies.end(); i++)
+ i->expires = dt;
+ } else if (s == "domain") {
+ for(std::list<Cookie>::iterator i = cookies.begin(); i != cookies.end(); i++)
+ i->domain = value;
+ } else if (s == "path") {
+ for(std::list<Cookie>::iterator i = cookies.begin(); i != cookies.end(); i++)
+ i->path = value;
+ }
+ } else if (cookiestr.compare(pos, 8, "httpOnly")==0) {
+ cstate = 1;
+ for(std::list<Cookie>::iterator i = cookies.begin(); i != cookies.end(); i++)
+ i->httponly = true;
+ } else if (cookiestr.compare(pos, 6, "secure") ==0) {
+ cstate = 1;
+ for(std::list<Cookie>::iterator i = cookies.begin(); i != cookies.end(); i++)
+ i->secure = true;
+ } else if (cstate == 0) { // expect cookie
+ Cookie c;
+ std::string s;
+ npos = cookiestr.find("; ", pos);
+ if (npos == std::string::npos) {
+ // last value
+ s = std::string(cookiestr.begin() + pos, cookiestr.end());
+ pos = cookiestr.size();
+ } else {
+ s = std::string(cookiestr.begin() + pos, cookiestr.begin() + npos);
+ pos = npos+2;
+ }
+ keyValuePair(s, c.name, c.value);
+ c.name = YaHTTP::Utility::decodeURL(c.name);
+ c.value = YaHTTP::Utility::decodeURL(c.value);
+ cookies.push_back(c);
+ } else if (cstate == 1) {
+ // ignore crap
+ break;
+ }
+ }
+
+ // store cookies
+ for(std::list<Cookie>::iterator i = cookies.begin(); i != cookies.end(); i++) {
+ this->cookies[i->name] = *i;
+ }
+ };
+ };
+};
#include <exception>
namespace YaHTTP {
- class ParseError: public std::exception {
+ class Error: public std::exception {
public:
- ParseError() {};
- ParseError(const std::string& reason): reason(reason) {};
- virtual ~ParseError() throw() {};
+ Error() {};
+ Error(const std::string& reason): reason(reason) {};
+ virtual ~Error() throw() {};
virtual const char* what() const throw()
{
}
const std::string reason;
};
+ class ParseError: public YaHTTP::Error {
+ public:
+ ParseError() {};
+ ParseError(const std::string& reason): Error(reason) {};
+ };
};
#endif
-#include "reqresp.hpp"
+#include "yahttp.hpp"
namespace YaHTTP {
-
- void Request::build(const std::string &method, const std::string &url, const std::string ¶ms) {
- this->method = method;
- std::transform(this->method.begin(), this->method.end(), this->method.begin(), ::toupper);
- this->url.parse(url);
- this->headers["host"] = this->url.host;
- this->headers["connection"] = "close";
- this->headers["user-agent"] = "yahttp 0.1";
- this->headers["accept"] = "*/*";
- if (params.empty() == false) {
- this->headers["content-type"] = "application/x-www-form-urlencoded; charset=utf-8";
- this->headers["content-length"] = params.size();
- this->body = params;
- } else {
- this->body = "";
- }
- };
-
- Request::Request() {};
- Request::Request(const Response &resp) {
- method = resp.method;
- url = resp.url;
- cookies = resp.cookies;
- };
- Request::Request(const Request &req) {
- method = req.method;
- url = req.url;
- parameters = req.parameters;
- headers = req.headers;
- cookies = req.cookies;
- body = req.body;
- };
- Request::~Request() {};
-
- Response::Response() : status(0) {};
- Response::Response(const Request &req) {
- headers["connection"] = "close";
- method = req.method;
- url = req.url;
- cookies = req.cookies;
- status = 200;
- };
- Response::Response(const Response &resp) {
- method = resp.method;
- url = resp.url;
- parameters = resp.parameters;
- headers = resp.headers;
- cookies = resp.cookies;
- body = resp.body;
- status = resp.status;
- statusText = resp.statusText;
- };
- Response::~Response() {};
-
- void Response::load(std::istream &is) {
- AsyncResponseLoader arl(this);
- while(is.good()) {
- char buf[1024];
- is.read(buf, 1024);
- if (is.gcount()) { // did we actually read anything
- is.clear();
- if (arl.feed(std::string(buf, is.gcount())) == true) return; // completed
- }
- }
- // FIXME: parse cookies
- };
-
- void Response::write(std::ostream &os) const {
- os << "HTTP/1.1 " << status << " ";
- if (statusText.empty())
- os << Utility::status2text(status);
- else
- os << statusText;
- os << "\r\n";
-
- // write headers
- strstr_map_t::const_iterator iter = headers.begin();
- while(iter != headers.end()) {
- os << Utility::camelizeHeader(iter->first) << ": " << iter->second << "\r\n";
- iter++;
- }
- os << "\r\n";
- os << body;
- };
-
- void Request::load(std::istream &is) {
- AsyncRequestLoader arl(this);
- while(is.good()) {
- char buf[1024];
- is.read(buf, 1024);
- if (is.gcount()) { // did we actually read anything
- is.clear();
- if (arl.feed(std::string(buf, is.gcount())) == true) return; // completed
- }
- }
- };
-
- void Request::write(std::ostream &os) const {
- os << method << " " << url.path << " HTTP/1.1" << "\r\n";
- strstr_map_t::const_iterator iter = headers.begin();
- while(iter != headers.end()) {
- os << Utility::camelizeHeader(iter->first) << ": " << iter->second << "\r\n";
- iter++;
- }
- os << "\r\n";
- if (body.size()>0) {
- os << body;
- }
- };
-
- std::ostream& operator<<(std::ostream& os, const Response &resp) {
- resp.write(os);
- return os;
- };
-
- std::istream& operator>>(std::istream& is, Response &resp) {
- resp.load(is);
- return is;
- };
-
- std::ostream& operator<<(std::ostream& os, const Request &req) {
- req.write(os);
- return os;
- };
-
- std::istream& operator>>(std::istream& is, Request &req) {
- req.load(is);
- return is;
- };
-
- bool AsyncRequestLoader::feed(const std::string &somedata) {
- size_t pos;
-
+ template <class T>
+ int AsyncLoader<T>::feed(const std::string& somedata) {
buffer.append(somedata);
while(state < 2) {
- // need to find newline in buffer
- if ((pos = buffer.find("\r\n")) == std::string::npos) return false;
- std::string line(buffer.begin(), buffer.begin()+pos); // exclude CRLF
- buffer.erase(buffer.begin(), buffer.begin()+pos+2); // remove line from buffer including CRLF
+ int cr=0;
+ // need to find CRLF in buffer
+ if ((pos = buffer.find_first_of("\n")) == std::string::npos) return false;
+ if (buffer[pos-1]=='\r')
+ cr=1;
+ std::string line(buffer.begin(), buffer.begin()+pos-cr); // exclude CRLF
+ buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer including CRLF
+
if (state == 0) { // startup line
- std::string ver;
- std::string tmpurl;
- std::istringstream iss(line);
- iss >> request->method >> tmpurl >> ver;
- if (ver.find("HTTP/1.") != 0)
- throw ParseError("Not a HTTP 1.x request");
- // uppercase the request method
- std::transform(request->method.begin(), request->method.end(), request->method.begin(), ::toupper);
- request->url.parse(tmpurl);
- request->parameters = Utility::parseUrlParameters(request->url.parameters);
- state = 1;
+ if (target->kind == YAHTTP_TYPE_REQUEST) {
+ std::string ver;
+ std::string tmpurl;
+ std::istringstream iss(line);
+ iss >> target->method >> tmpurl >> ver;
+ if (ver.find("HTTP/1.") != 0)
+ throw ParseError("Not a HTTP 1.x request");
+ // uppercase the target method
+ std::transform(target->method.begin(), target->method.end(), target->method.begin(), ::toupper);
+ target->url.parse(tmpurl);
+ target->getvars = Utility::parseUrlParameters(target->url.parameters);
+ state = 1;
+ } else if(target->kind == YAHTTP_TYPE_RESPONSE) {
+ std::string ver;
+ std::istringstream iss(line);
+ iss >> ver >> target->status >> target->statusText;
+ if (ver.find("HTTP/1.") != 0)
+ throw ParseError("Not a HTTP 1.x response");
+ state = 1;
+ }
} else if (state == 1) {
std::string key,value;
size_t pos;
if (line.empty()) {
- chunked = (request->headers.find("transfer-encoding") != request->headers.end() && request->headers["transfer-encoding"] == "chunked");
- // host header is optional
- if (request->headers.find("host") != request->headers.end())
- request->url.host = request->headers["host"];
-
+ chunked = (target->headers.find("transfer-encoding") != target->headers.end() && target->headers["transfer-encoding"] == "chunked");
state = 2;
break;
}
// split headers
if ((pos = line.find_first_of(": ")) == std::string::npos)
- throw ParseError("Malformed line");
+ throw ParseError("Malformed header line");
key = line.substr(0, pos);
value = line.substr(pos+2);
+ Utility::trimRight(value);
std::transform(key.begin(), key.end(), key.begin(), ::tolower);
- request->headers[key] = value;
+ // is it already defined
+
+ if ((key == "set-cookie" && target->kind == YAHTTP_TYPE_RESPONSE) ||
+ (key == "cookie" && target->kind == YAHTTP_TYPE_REQUEST)) {
+ target->jar.parseCookieHeader(value);
+ } else {
+ if (key == "host" && target->kind == YAHTTP_TYPE_REQUEST) {
+ // maybe it contains port?
+ if ((pos = value.find(":")) == std::string::npos) {
+ target->url.host = value;
+ } else {
+ target->url.host = value.substr(0, pos);
+ target->url.port = ::atoi(value.substr(pos).c_str());
+ }
+ }
+ if (target->headers.find(key) != target->headers.end()) {
+ target->headers[key] = target->headers[key] + ";" + value;
+ } else {
+ target->headers[key] = value;
+ }
+ }
}
}
- // skip body for GET
- if (request->method == "GET")
- return true;
-
- // do we have content-length?
+ minbody = 0;
+ // check for expected body size
+ if (target->kind == YAHTTP_TYPE_REQUEST) maxbody = YAHTTP_MAX_REQUEST_SIZE;
+ else if (target->kind == YAHTTP_TYPE_RESPONSE) maxbody = YAHTTP_MAX_RESPONSE_SIZE;
+ else maxbody = 0;
+
if (!chunked) {
- if (request->headers.find("content-length") != request->headers.end()) {
- std::istringstream maxbodyS(request->headers["content-length"]);
- maxbodyS >> maxbody;
+ if (target->headers.find("content-length") != target->headers.end()) {
+ std::istringstream maxbodyS(target->headers["content-length"]);
+ maxbodyS >> minbody;
+ maxbody = minbody;
}
- if (maxbody < 1) return true; // guess there isn't anything left.
- if (maxbody > YAHTTP_MAX_REQUEST_SIZE)
- throw ParseError("Request size exceeded");
+ if (minbody < 1) return true; // guess there isn't anything left.
+ if (target->kind == YAHTTP_TYPE_REQUEST && minbody > YAHTTP_MAX_REQUEST_SIZE) throw ParseError("Max request body size exceeded");
+ else if (target->kind == YAHTTP_TYPE_RESPONSE && minbody > YAHTTP_MAX_RESPONSE_SIZE) throw ParseError("Max response body size exceeded");
}
- if (buffer.size() == 0) return false;
+ if (maxbody == 0) hasBody = false;
+ else hasBody = true;
- while(buffer.size() > 0 && bodybuf.str().size() < static_cast<size_t>(maxbody)) {
+ if (buffer.size() == 0) return ready();
+
+ while(buffer.size() > 0) {
char buf[1024] = {0};
if (chunked) {
buffer.erase(buffer.begin(), buffer.begin()+chunk_size+1);
bodybuf << buf;
chunk_size = 0;
- if (buffer.size() == 0) return false; // just in case
+ if (buffer.size() == 0) break; // just in case
}
} else {
- bodybuf << buffer;
+ if (bodybuf.str().length() + buffer.length() > maxbody)
+ bodybuf << buffer.substr(0, maxbody - bodybuf.str().length());
+ else
+ bodybuf << buffer;
buffer = "";
}
}
if (chunk_size!=0) return false; // need more data
- if (!chunked && bodybuf.str().size() < maxbody) {
- return false; // need more data
- }
-
- bodybuf.flush();
- request->body = bodybuf.str();
- bodybuf.str("");
- return true;
+ return ready();
};
-
- bool AsyncResponseLoader::feed(const std::string &somedata) {
- size_t pos;
- buffer.append(somedata);
- while(state < 2) {
- // need to find CRLF in buffer
- if ((pos = buffer.find("\r\n")) == std::string::npos) return false;
- std::string line(buffer.begin(), buffer.begin()+pos); // exclude CRLF
- buffer.erase(buffer.begin(), buffer.begin()+pos+2); // remove line from buffer including CRLF
- if (state == 0) { // startup line
- std::string ver;
- std::istringstream iss(line);
- iss >> ver >> response->status >> response->statusText;
- if (ver.find("HTTP/1.") != 0)
- throw ParseError("Not a HTTP 1.x response");
- state = 1;
- } else if (state == 1) {
- std::string key,value;
- size_t pos;
- if (line.empty()) {
- chunked = (response->headers.find("transfer-encoding") != response->headers.end() && response->headers["transfer-encoding"] == "chunked");
- state = 2;
- break;
- }
- // split headers
- if ((pos = line.find_first_of(": ")) == std::string::npos)
- throw ParseError("Malformed header line");
- key = line.substr(0, pos);
- value = line.substr(pos+2);
- std::transform(key.begin(), key.end(), key.begin(), ::tolower);
- response->headers[key] = value;
+
+ void HTTPBase::write(std::ostream& os) const {
+ if (kind == YAHTTP_TYPE_REQUEST) {
+ std::ostringstream getparmbuf;
+ 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) << "&";
}
+ if (getparmbuf.str().length() > 0)
+ getparms = "?" + std::string(getparmbuf.str().begin(), getparmbuf.str().end() - 1);
+ else
+ getparms = "";
+ os << method << " " << url.path << getparms << " HTTP/1.1";
+ } else if (kind == YAHTTP_TYPE_RESPONSE) {
+ os << "HTTP/1.1 " << status << " ";
+ if (statusText.empty())
+ os << Utility::status2text(status);
+ else
+ os << statusText;
}
-
- if (buffer.size() == 0) return false;
-
- while(buffer.size() > 0) {
- char buf[1024] = {0};
-
- if (chunked) {
- if (chunk_size == 0) {
- // read chunk length
- if ((pos = buffer.find('\n')) == std::string::npos) return false;
- if (pos > 1023)
- throw ParseError("Impossible chunk_size");
- buffer.copy(buf, pos);
- buf[pos]=0; // just in case...
- buffer.erase(buffer.begin(), buffer.begin()+pos+1); // remove line from buffer
- sscanf(buf, "%x", &chunk_size);
- if (!chunk_size) break; // last chunk
+ os << "\r\n";
+
+ // write headers
+ strstr_map_t::const_iterator iter = headers.begin();
+ while(iter != headers.end()) {
+ if (iter->first == "host" && kind != YAHTTP_TYPE_REQUEST) { iter++; continue; }
+ os << Utility::camelizeHeader(iter->first) << ": " << iter->second << "\r\n";
+ iter++;
+ }
+ if (jar.cookies.size() > 0) { // write cookies
+ for(strcookie_map_t::const_iterator i = jar.cookies.begin(); i != jar.cookies.end(); i++) {
+ if (kind == YAHTTP_TYPE_REQUEST) {
+ os << "Cookie: ";
} else {
- if (buffer.size() < static_cast<size_t>(chunk_size+1)) return false; // expect newline
- if (buffer.at(chunk_size) != '\n') return false; // there should be newline.
- buffer.copy(buf, chunk_size);
- buffer.erase(buffer.begin(), buffer.begin()+chunk_size+1);
- bodybuf << buf;
- chunk_size = 0;
- if (buffer.size() == 0) return false; // just in case
+ os << "Set-Cookie: ";
}
- } else {
- bodybuf << buffer;
- buffer = "";
+ os << i->second.str() << "\r\n";
}
}
-
- if (chunk_size!=0) return false; // need more data
-
- bodybuf.flush();
- response->body = bodybuf.str();
- bodybuf.str("");
- return true;
+ os << "\r\n";
+#ifdef HAVE_CPP_FUNC_PTR
+ this->renderer(this, os);
+#else
+ os << body;
+#endif
+ };
+
+ std::ostream& operator<<(std::ostream& os, const Response &resp) {
+ resp.write(os);
+ return os;
+ };
+
+ std::istream& operator>>(std::istream& is, Response &resp) {
+ YaHTTP::AsyncResponseLoader arl;
+ arl.initialize(&resp);
+ while(is.good()) {
+ char buf[1024];
+ is.read(buf, 1024);
+ if (is.gcount()) { // did we actually read anything
+ is.clear();
+ if (arl.feed(std::string(buf, is.gcount())) == true) break; // completed
+ }
+ }
+ // throw unless ready
+ if (arl.ready() == false)
+ throw ParseError("Was not able to extract a valid Response from stream");
+ arl.finalize();
+ return is;
+ };
+
+ std::ostream& operator<<(std::ostream& os, const Request &req) {
+ req.write(os);
+ return os;
+ };
+
+ std::istream& operator>>(std::istream& is, Request &req) {
+ YaHTTP::AsyncRequestLoader arl;
+ arl.initialize(&req);
+ while(is.good()) {
+ char buf[1024];
+ is.read(buf, 1024);
+ if (is.gcount()) { // did we actually read anything
+ is.clear();
+ if (arl.feed(std::string(buf, is.gcount())) == true) break; // completed
+ }
+ }
+ if (arl.ready() == false)
+ throw ParseError("Was not able to extract a valid Request from stream");
+ arl.finalize();
+ return is;
};
};
-#include <map>
-#include <iostream>
-#include <locale>
-#include <algorithm>
+#ifdef HAVE_CXX11
+#include <functional>
+#define HAVE_CPP_FUNC_PTR
+namespace funcptr = std;
+#else
+#ifdef HAVE_BOOST
+#include <boost/function.hpp>
+namespace funcptr = boost;
+#define HAVE_CPP_FUNC_PTR
+#endif
+#endif
-#include "url.hpp"
-#include "utility.hpp"
-#include "exception.hpp"
+#include <fstream>
+#include <cctype>
-#ifndef YAHTTP_MAX_REQUEST_SIZE
+#ifndef WIN32
+#include <cstdio>
+#include <unistd.h>
+#endif
+
+#ifndef YAHTTP_MAX_REQUEST_SIZE
#define YAHTTP_MAX_REQUEST_SIZE 2097152
#endif
+#ifndef YAHTTP_MAX_RESPONSE_SIZE
+#define YAHTTP_MAX_RESPONSE_SIZE 2097152
+#endif
+
+#define YAHTTP_TYPE_REQUEST 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;
- class Response;
- class Request;
- class AsyncResponseLoader;
- class AsyncRequestLoader;
+ typedef enum {
+ urlencoded,
+ multipart
+ } postformat_t;
- class Request {
+ class HTTPBase {
public:
- Request();
- Request(const Response &resp);
- Request(const Request &req);
- ~Request();
-
- void build(const std::string &method, const std::string &url, const std::string ¶ms);
-
- void load(std::istream &is);
- void write(std::ostream &os) const;
-
- strstr_map_t headers;
- strstr_map_t parameters;
- strstr_map_t cookies;
-
- URL url;
- std::string method;
- std::string body;
+#ifdef HAVE_CPP_FUNC_PTR
+ class SendBodyRender {
+ public:
+ SendBodyRender() {};
+
+ size_t operator()(const HTTPBase *doc, std::ostream& os) const {
+ os << doc->body;
+ return doc->body.length();
+ };
+ };
+ class SendFileRender {
+ public:
+ SendFileRender(const std::string& path) {
+ this->path = path;
+ };
+
+ size_t operator()(const HTTPBase *doc, std::ostream& os) const {
+ char buf[4096];
+ size_t n,k;
+#ifdef HAVE_CXX11
+ std::ifstream ifs(path, std::ifstream::binary);
+#else
+ std::ifstream ifs(path.c_str(), std::ifstream::binary);
+#endif
+ n = 0;
+ while(ifs && ifs.good()) {
+ ifs.read(buf, sizeof buf);
+ n += (k = ifs.gcount());
+ if (k)
+ os.write(buf, k);
+ }
+
+ return n;
+ };
+
+ std::string path;
+ };
+#endif
+ HTTPBase() {
+#ifdef HAVE_CPP_FUNC_PTR
+ renderer = SendBodyRender();
+#endif
+ };
+protected:
+ HTTPBase(const HTTPBase& rhs) {
+ this->url = rhs.url; this->kind = rhs.kind;
+ this->status = rhs.status; this->statusText = rhs.statusText;
+ this->method = rhs.method; this->headers = rhs.headers;
+ this->jar = rhs.jar; this->postvars = rhs.postvars;
+ this->parameters = rhs.parameters; this->getvars = rhs.getvars;
+ this->body = rhs.body;
+#ifdef HAVE_CPP_FUNC_PTR
+ this->renderer = rhs.renderer;
+#endif
+ };
+ HTTPBase& operator=(const HTTPBase& rhs) {
+ this->url = rhs.url; this->kind = rhs.kind;
+ this->status = rhs.status; this->statusText = rhs.statusText;
+ this->method = rhs.method; this->headers = rhs.headers;
+ this->jar = rhs.jar; this->postvars = rhs.postvars;
+ this->parameters = rhs.parameters; this->getvars = rhs.getvars;
+ this->body = rhs.body;
+#ifdef HAVE_CPP_FUNC_PTR
+ this->renderer = rhs.renderer;
+#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;
+// these two are for Router
+ strstr_map_t parameters;
+ std::string routeName;
+
+ std::string body;
+
+#ifdef HAVE_CPP_FUNC_PTR
+ funcptr::function<size_t(const HTTPBase*,std::ostream&)> renderer;
+#endif
+ void write(std::ostream& os) const;
- friend std::istream& operator>>(std::istream& os, const Request &req);
- friend std::ostream& operator<<(std::ostream& os, const Request &req);
- friend class AsyncRequestLoader;
+ strstr_map_t& GET() { return getvars; };
+ strstr_map_t& POST() { return postvars; };
+ strcookie_map_t& COOKIES() { return jar.cookies; };
};
- class Response {
+ class Response: public HTTPBase {
public:
- Response();
- Response(const Request &req);
- Response(const Response &resp);
- ~Response();
- void load(std::istream &is);
- void write(std::ostream &os) const;
-
- strstr_map_t headers;
- strstr_map_t parameters;
- strstr_map_t cookies;
-
- URL url;
- int status;
- std::string statusText;
- std::string method;
- std::string body;
-
- friend std::istream& operator>>(std::istream& is, Response &resp);
- friend std::ostream& operator<<(std::ostream& os, const Response &resp);
- friend class AsyncResponseLoader;
+ Response() { this->kind = YAHTTP_TYPE_RESPONSE; };
+ Response(const HTTPBase& rhs): HTTPBase(rhs) {
+ this->kind = YAHTTP_TYPE_RESPONSE;
+ };
+ Response& operator=(const HTTPBase& rhs) {
+ 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);
};
- class AsyncResponseLoader {
+ class Request: public HTTPBase {
public:
- AsyncResponseLoader(Response *response) {
- state = 0;
- chunked = false;
- chunk_size = 0;
- this->response = response;
+ Request() { this->kind = YAHTTP_TYPE_REQUEST; };
+ Request(const HTTPBase& rhs): HTTPBase(rhs) {
+ this->kind = YAHTTP_TYPE_REQUEST;
};
- bool feed(const std::string &somedata);
- private:
- Response *response;
- int state;
- std::string buffer;
- bool chunked;
- int chunk_size;
- std::ostringstream bodybuf;
+ Request& operator=(const HTTPBase& rhs) {
+ 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->headers["host"] = this->url.host;
+ this->method = method;
+ std::transform(this->method.begin(), this->method.end(), this->method.begin(), ::toupper);
+ this->headers["user-agent"] = "YaHTTP v1.0";
+ }
+
+ 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) << "&";
+ }
+ // remove last bit
+ if (postbuf.str().length()>0)
+ body = std::string(postbuf.str().begin(), postbuf.str().end()-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";
+ }
+ }
+
+ // set method and change headers
+ method = "POST";
+ headers["content-length"] = body.length();
+ };
+
+ friend std::ostream& operator<<(std::ostream& os, const Request &resp);
+ friend std::istream& operator>>(std::istream& is, Request &resp);
};
- class AsyncRequestLoader {
+ template <class T>
+ class AsyncLoader {
public:
- AsyncRequestLoader(Request *request) {
- state = 0;
- chunked = false;
- chunk_size = 0;
- maxbody = 0;
- this->request = request;
- };
- bool feed(const std::string &somedata);
- private:
- Request *request;
+ T* target;
int state;
+ size_t pos;
+
std::string buffer;
bool chunked;
int chunk_size;
std::ostringstream bodybuf;
- long maxbody;
+ size_t maxbody;
+ size_t minbody;
+ bool hasBody;
+
+ void keyValuePair(const std::string &keyvalue, std::string &key, std::string &value);
+
+ 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);
+ bool ready() { return state > 1 &&
+ (!hasBody ||
+ (bodybuf.str().size() <= maxbody &&
+ bodybuf.str().size() >= minbody)
+ );
+ };
+ void finalize() {
+ bodybuf.flush();
+ if (ready()) {
+ strstr_map_t::iterator pos = target->headers.find("content-type");
+ if (pos != target->headers.end() && Utility::iequals(pos->second, "application/x-www-form-urlencoded", 32)) {
+ target->postvars = Utility::parseUrlParameters(bodybuf.str());
+ }
+ target->body = bodybuf.str();
+ }
+ bodybuf.str("");
+ this->target = NULL;
+ };
};
+
+ class AsyncResponseLoader: public AsyncLoader<Response> {
+ };
+
+ class AsyncRequestLoader: public AsyncLoader<Request> {
+ };
+
};
--- /dev/null
+/* @file
+ * @brief Concrete implementation of Router
+ */
+#include "yahttp.hpp"
+#include "router.hpp"
+
+namespace YaHTTP {
+ typedef funcptr::tuple<int,int> TDelim;
+
+ // router is defined here.
+ YaHTTP::Router Router::router;
+
+ void Router::map(const std::string& method, const std::string& url, THandlerFunction handler, const std::string& name) {
+ std::string method2 = method;
+ bool isopen=false;
+ // add into vector
+ for(std::string::const_iterator i = url.begin(); i != url.end(); i++) {
+ if (*i == '<' && isopen) throw Error("Invalid URL mask, cannot have < after <");
+ if (*i == '<') isopen = true;
+ if (*i == '>' && !isopen) throw Error("Invalid URL mask, cannot have > without < first");
+ if (*i == '>') isopen = false;
+ }
+ std::transform(method2.begin(), method2.end(), method2.begin(), ::toupper);
+ routes.push_back(funcptr::make_tuple(method2, url, handler, name));
+ };
+
+ bool Router::route(Request *req, THandlerFunction& handler) {
+ std::map<std::string, TDelim> params;
+ int pos1,pos2;
+ std::string pname;
+ bool matched = false;
+ std::string rname;
+
+ // iterate routes
+ for(TRouteList::iterator i = routes.begin(); !matched && i != routes.end(); i++) {
+ int k1,k2,k3;
+ std::string pname;
+ std::string method, url;
+ funcptr::tie(method, url, handler, rname) = *i;
+
+ if (method.empty() == false && req->method != method) continue; // no match on method
+ // see if we can't match the url
+ params.clear();
+ // simple matcher func
+ for(k1=0, k2=0; k1 < static_cast<int>(url.size()) && k2 < static_cast<int>(req->url.path.size()); ) {
+ if (url[k1] == '<') {
+ pos1 = k2;
+ k3 = k1+1;
+ // start of parameter
+ while(k1 < static_cast<int>(url.size()) && url[k1] != '>') k1++;
+ pname = std::string(url.begin()+k3, url.begin()+k1);
+ // then we also look it on the url
+ if (pname[0]=='*') {
+ pname = pname.substr(1);
+ // this matches whatever comes after it, basically end of string
+ pos2 = req->url.path.size();
+ matched = true;
+ if (pname != "")
+ params[pname] = funcptr::tie(pos1,pos2);
+ k1 = url.size();
+ k2 = req->url.path.size();
+ break;
+ } else {
+ // match until url[k1]
+ while(k2 < static_cast<int>(req->url.path.size()) && req->url.path[k2] != url[k1+1]) k2++;
+ pos2 = k2;
+ params[pname] = funcptr::tie(pos1,pos2);
+ }
+ k2--;
+ }
+ else if (url[k1] != req->url.path[k2]) {
+ break;
+ }
+
+ k1++; k2++;
+ }
+
+ // ensure.
+ if (url[k1] != req->url.path[k2])
+ matched = false;
+ else
+ matched = true;
+ }
+
+ if (!matched) { return false; } // no route
+ req->parameters.clear();
+
+ for(std::map<std::string, TDelim>::iterator i = params.begin(); i != params.end(); i++) {
+ int p1,p2;
+ funcptr::tie(p1,p2) = i->second;
+ std::string value(req->url.path.begin() + p1, req->url.path.begin() + p2);
+ value = Utility::decodeURL(value);
+ req->parameters[i->first] = value;
+ }
+
+ req->routeName = rname;
+
+ return true;
+ };
+
+ void Router::printRoutes(std::ostream &os) {
+ for(TRouteList::iterator i = routes.begin(); i != routes.end(); i++) {
+#ifdef HAVE_CXX11
+ std::streamsize ss = os.width();
+ std::ios::fmtflags ff = os.setf(std::ios::left);
+ os.width(10);
+ os << std::get<0>(*i);
+ os.width(50);
+ os << std::get<1>(*i);
+ os.width(ss);
+ os.setf(ff);
+ os << " " << std::get<3>(*i);
+ os << std::endl;
+#else
+ os << i->get<0>() << " " << i->get<1>() << " " << i->get<3>() << std::endl;
+#endif
+ }
+ };
+
+ std::pair<std::string,std::string> Router::urlFor(const std::string &name, const strstr_map_t& arguments) {
+ std::ostringstream path;
+ std::string mask,method,result;
+ int k1,k2,k3;
+
+ bool found = false;
+ for(TRouteList::iterator i = routes.begin(); !found && i != routes.end(); i++) {
+#ifdef HAVE_CXX11
+ if (std::get<3>(*i) == name) { mask = std::get<1>(*i); method = std::get<0>(*i); found = true; }
+#else
+ if (i->get<3>() == name) { mask = i->get<1>(); method = i->get<0>(); found = true; }
+#endif
+ }
+
+ if (!found)
+ throw Error("Route not found");
+
+ for(k1=0,k3=0;k1<static_cast<int>(mask.size());k1++) {
+ if (mask[k1] == '<') {
+ std::string pname;
+ strstr_map_t::const_iterator pptr;
+ k2=k1;
+ while(k1<static_cast<int>(mask.size()) && mask[k1]!='>') k1++;
+ path << mask.substr(k3,k2-k3);
+ if (mask[k2+1] == '*')
+ pname = std::string(mask.begin() + k2 + 2, mask.begin() + k1);
+ else
+ pname = std::string(mask.begin() + k2 + 1, mask.begin() + k1);
+ if ((pptr = arguments.find(pname)) != arguments.end())
+ path << Utility::encodeURL(pptr->second);
+ k3 = k1+1;
+ }
+ else if (mask[k1] == '*') {
+ // ready
+ k3++;
+ continue;
+ }
+ }
+ std::cout << mask.substr(k3) << std::endl;
+ path << mask.substr(k3);
+ result = path.str();
+ return std::make_pair(method, result);
+ }
+};
--- /dev/null
+#ifndef _YAHTTP_ROUTER_HPP
+#define _YAHTTP_ROUTER_HPP 1
+/* @file
+ * @brief Defines router class and support structures
+ */
+#ifdef HAVE_CXX11
+#include <functional>
+#include <tuple>
+#define HAVE_CPP_FUNC_PTR
+#define IGNORE std::ignore
+namespace funcptr = std;
+#else
+#ifdef HAVE_BOOST
+#include <boost/function.hpp>
+#include <boost/tuple/tuple.hpp>
+#define IGNORE boost::tuples::ignore
+namespace funcptr = boost;
+#define HAVE_CPP_FUNC_PTR
+#else
+#warn "You need to configure with boost or have C++11 capable compiler for router"
+#endif
+#endif
+
+#ifdef HAVE_CPP_FUNC_PTR
+#include <vector>
+#include <utility>
+
+namespace YaHTTP {
+ typedef funcptr::function <void(Request* req, Response* resp)> THandlerFunction; //!< Handler function pointer
+ typedef funcptr::tuple<std::string, std::string, THandlerFunction, std::string> TRoute; //!< Route tuple (method, urlmask, handler, name)
+ typedef std::vector<TRoute> TRouteList; //!< List of routes in order of evaluation
+
+ /*! Implements simple router.
+
+This class implements a router for masked urls. The URL mask syntax is as of follows
+
+/<masked>/url<number>/<hi>.<format>
+
+You can use <*param> to denote that everything will be matched and consumed into the parameter, including slash (/). Use <*> to denote that URL
+is consumed but not stored. Note that only path is matched, scheme, host and url parameters are ignored.
+ */
+ class Router {
+ private:
+ Router() {};
+ static Router router; //<! Singleton instance of Router
+ public:
+ void map(const std::string& method, const std::string& url, THandlerFunction handler, const std::string& name); //<! Instance method for mapping urls
+ bool route(Request *req, THandlerFunction& handler); //<! Instance method for performing routing
+ void printRoutes(std::ostream &os); //<! Instance method for printing routes
+ std::pair<std::string, std::string> urlFor(const std::string &name, const strstr_map_t& arguments); //<! Instance method for generating paths
+
+/*! Map an URL.
+If method is left empty, it will match any method. Name is also optional, but needed if you want to find it for making URLs
+*/
+ static void Map(const std::string& method, const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map(method, url, handler, name); };
+ static void Get(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("GET", url, handler, name); }; //<! Helper for mapping GET
+ static void Post(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("POST", url, handler, name); }; //<! Helper for mapping POST
+ static void Put(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("PUT", url, handler, name); }; //<! Helper for mapping PUT
+ static void Patch(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("PATCH", url, handler, name); }; //<! Helper for mapping PATCH
+ static void Delete(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("DELETE", url, handler, name); }; //<! Helper for mapping DELETE
+ static void Any(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("", url, handler, name); }; //<! Helper for mapping any method
+
+ static bool Route(Request *req, THandlerFunction& handler) { return router.route(req, handler); }; //<! Performs routing based on req->url.path
+ static void PrintRoutes(std::ostream &os) { router.printRoutes(os); }; //<! Prints all known routes to given output stream
+
+ static std::pair<std::string, std::string> URLFor(const std::string &name, const strstr_map_t& arguments) { return router.urlFor(name,arguments); }; //<! Generates url from named route and arguments. Missing arguments are assumed empty
+ static const TRouteList& GetRoutes() { return router.routes; } //<! Reference to route list
+
+ TRouteList routes; //<! Instance variable for routes
+ };
+};
+#endif
+
+#endif
if (pos >= url.size()) return true; // no data
if (url[pos] != '/') return false; // not an url
if ( (pos1 = url.find_first_of("?", pos)) == std::string::npos ) {
- path = url;
+ path = url.substr(pos);
pos = url.size();
} else {
path = url.substr(pos, pos1-pos);
pos = pos1;
}
- path = Utility::decodeURL(path);
return true;
}
parameters = url.substr(pos+1, pos1-pos-1);
pos = pos1;
}
+ if (parameters.size()>0 && *(parameters.end()-1) == '&') parameters.resize(parameters.size()-1);
return true;
}
return true;
}
+ void initialize() {
+ protocol = ""; host = ""; port = 0; username = ""; password = ""; path = ""; parameters = ""; anchor =""; pathless = true;
+ }
+
public:
std::string to_string() const {
std::string tmp;
port > 0)
oss << ":" << port;
- oss << Utility::encodeURL(path, true);
+ oss << path;
if (parameters.empty() == false) {
if (!pathless)
oss << "?";
std::string anchor;
bool pathless;
- URL() { protocol = ""; host = ""; port = 0; username = ""; password = ""; path = ""; parameters = ""; anchor =""; pathless = true; };
+ URL() { initialize(); };
URL(const std::string& url) {
parse(url);
};
bool parse(const std::string& url) {
// setup
- protocol = ""; host = ""; port = 0;
- username = ""; password = ""; path = "";
- parameters = ""; anchor =""; pathless = true;
+ initialize();
if (url.size() > YAHTTP_MAX_URL_LENGTH) return false;
size_t pos = 0;
#ifndef _YAHTTP_UTILITY_HPP
#define _YAHTTP_UTILITY_HPP 1
-#include <string>
-#include <algorithm>
-#include <cstdio>
-
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};
+
+ class DateTime {
+ public:
+ bool isSet;
+
+ int year;
+
+ int month;
+ int day;
+ int wday;
+
+ int hours;
+ int minutes;
+ int seconds;
+
+ int utc_offset;
+
+ DateTime() {
+ initialize();
+ };
+
+ void initialize() {
+ isSet = false;
+ year = month = day = wday = hours = minutes = seconds = utc_offset = 0;
+ month = 1; // it's invalid otherwise
+ };
+
+ void setLocal() {
+ fromLocaltime(time((time_t*)NULL));
+ };
+
+ void setGm() {
+ fromGmtime(time((time_t*)NULL));
+ }
+
+ void fromLocaltime(time_t t) {
+#ifdef HAVE_LOCALTIME_R
+ struct tm tm;
+ localtime_r(&t, &tm);
+ fromTm(&tm);
+#else
+ struct tm *tm;
+ tm = localtime(&t);
+ fromTm(tm);
+#endif
+ };
+
+ void fromGmtime(time_t t) {
+#ifdef HAVE_GMTIME_R
+ struct tm tm;
+ gmtime_r(&t, &tm);
+ fromTm(&tm);
+#else
+ struct tm *tm;
+ tm = gmtime(&t);
+ fromTm(tm);
+#endif
+ };
+
+ void fromTm(const struct tm *tm) {
+ year = tm->tm_year + 1900;
+ month = tm->tm_mon + 1;
+ day = tm->tm_mday;
+ hours = tm->tm_hour;
+ minutes = tm->tm_min;
+ seconds = tm->tm_sec;
+ wday = tm->tm_wday;
+ utc_offset = tm->tm_gmtoff;
+ isSet = true;
+ };
+
+ void validate() const {
+ if (wday < 0 || wday > 6) throw "Invalid date";
+ if (month < 1 || month > 12) throw "Invalid date";
+ if (year < 0) throw "Invalid date";
+ if (hours < 0 || hours > 23 ||
+ minutes < 0 || minutes > 59 ||
+ seconds < 0 || seconds > 60) throw "Invalid date";
+ }
+
+ std::string rfc_str() const {
+ std::ostringstream oss;
+ validate();
+ oss << DAYS[wday] << ", " << std::setfill('0') << std::setw(2) << day << " " << MONTHS[month] << " " <<
+ std::setfill('0') << std::setw(2) << year << " " <<
+ std::setfill('0') << std::setw(2) << hours << ":" <<
+ std::setfill('0') << std::setw(2) << minutes << ":" <<
+ std::setfill('0') << std::setw(2) << seconds << " ";
+ if (utc_offset>=0) oss << "+";
+ else oss << "-";
+ int tmp_off = ( utc_offset < 0 ? utc_offset*-1 : utc_offset );
+ oss << std::setfill('0') << std::setw(2) << (tmp_off/3600);
+ oss << std::setfill('0') << std::setw(2) << (tmp_off%3600)/60;
+
+ return oss.str();
+ };
+
+ std::string cookie_str() const {
+ std::ostringstream oss;
+ validate();
+ oss << std::setfill('0') << std::setw(2) << day << "-" << MONTHS[month] << "-" << year << " " <<
+ std::setfill('0') << std::setw(2) << hours << ":" <<
+ std::setfill('0') << std::setw(2) << minutes << ":" <<
+ std::setfill('0') << std::setw(2) << seconds << " GMT";
+ return oss.str();
+ }
+
+ 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) {
+ fromTm(&tm);
+ } else {
+ throw "Unparseable 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) {
+ fromTm(&tm);
+ } else {
+ throw "Unparseable date";
+ }
+ };
+
+ int unixtime() const {
+ struct tm tm;
+ tm.tm_year = year-1900;
+ tm.tm_mon = month-1;
+ tm.tm_mday = day;
+ tm.tm_hour = hours;
+ tm.tm_min = minutes;
+ tm.tm_sec = seconds;
+ tm.tm_gmtoff = utc_offset;
+ return mktime(&tm);
+ }
+
+ };
+
class Utility {
public:
static std::string decodeURL(const std::string& component) {
return result;
};
- static std::string encodeURL(const std::string& component, bool encodeSlash = true) {
+ static std::string encodeURL(const std::string& component, bool asUrl = true) {
std::string result = component;
+ std::string skip = "+-.:,&;_#%[]?/@(){}=";
char repl[3];
size_t pos;
for(std::string::iterator iter = result.begin(); iter != result.end(); iter++) {
- if (*iter != '+' && !(encodeSlash == false || *iter == '/') && !std::isalnum(*iter)) {
+ if (!std::isalnum(*iter) && (!asUrl || skip.find(*iter) == std::string::npos)) {
// replace with different thing
pos = std::distance(result.begin(), iter);
- ::snprintf(repl,3,"%02x", *iter);
+ std::snprintf(repl,3,"%02x", static_cast<unsigned char>(*iter));
result = result.replace(pos, 1, "%", 1).insert(pos+1, repl, 2);
iter = result.begin() + pos + 2;
}
return result;
};
+ static std::string encodeURL(const std::wstring& component, bool asUrl = true) {
+ unsigned char const *p = reinterpret_cast<unsigned char const*>(&component[0]);
+ std::size_t s = component.size() * sizeof((*component.begin()));
+ std::vector<unsigned char> vec(p, p+s);
+
+ std::ostringstream result;
+ std::string skip = "+-.,&;_#%[]?/@(){}=";
+ for(std::vector<unsigned char>::iterator iter = vec.begin(); iter != vec.end(); iter++) {
+ if (!std::isalnum((char)*iter) && (!asUrl || skip.find((char)*iter) == std::string::npos)) {
+ // bit more complex replace
+ result << "%" << std::hex << std::setw(2) << std::setfill('0') << static_cast<unsigned int>(*iter);
+ } else result << (char)*iter;
+ }
+ return result.str();
+ };
+
+
+
static std::string status2text(int status) {
switch(status) {
case 200:
return parameter_map;
};
- static void trim_right(std::string &str) {
+ static bool iequals(const std::string& a, const std::string& b, size_t length) {
+ std::string::const_iterator ai, bi;
+ size_t i;
+ for(ai = a.begin(), bi = b.begin(), i = 0; ai != a.end() && bi != b.end() && i < length; ai++,bi++,i++) {
+ if (::toupper(*ai) != ::toupper(*bi)) return false;
+ }
+
+ if (ai == a.end() && bi == b.end()) return true;
+ if ((ai == a.end() && bi != b.end()) ||
+ (ai != a.end() && bi == b.end())) return false;
+
+ return ::toupper(*ai) == ::toupper(*bi);
+ }
+
+ static bool iequals(const std::string& a, const std::string& b) {
+ if (a.size() != b.size()) return false;
+ return iequals(a,b,a.size());
+ }
+
+ 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++;
--- /dev/null
+/* config header for embedded libyahttp */
+#define HAVE_BOOST 1
+#include <map>
+#include <iostream>
+#include <locale>
+#include <algorithm>
+#include <string>
+#include <cstdio>
+#include <sys/time.h>
+#include <iomanip>
+#include <list>
+#include <vector>
+
+#include "yahttp-config.h"
+#include "url.hpp"
#include "utility.hpp"
+#include "exception.hpp"
#include "url.hpp"
+#include "cookie.hpp"
#include "reqresp.hpp"
+
+/*! \mainpage Yet Another HTTP Library Documentation
+\section sec_quick_start Quick start example
+
+@code
+#include <yahttp/yahttp.hpp>
+
+int main(void) {
+ std::ifstream ifs("request.txt");
+ YaHTTP::Request req;
+ ifs >> req;
+
+ std::cout << req.method " " << req.url.path << std::endl;
+ return 0;
+}
+@endcode
+\author Aki Tuomi
+*/