summaryrefslogtreecommitdiffstats
path: root/ext/yahttp/yahttp/reqresp.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'ext/yahttp/yahttp/reqresp.hpp')
-rw-r--r--ext/yahttp/yahttp/reqresp.hpp351
1 files changed, 351 insertions, 0 deletions
diff --git a/ext/yahttp/yahttp/reqresp.hpp b/ext/yahttp/yahttp/reqresp.hpp
new file mode 100644
index 0000000..00ba545
--- /dev/null
+++ b/ext/yahttp/yahttp/reqresp.hpp
@@ -0,0 +1,351 @@
+#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 <fstream>
+#include <cctype>
+
+#ifndef WIN32
+#include <cstdio>
+#include <unistd.h>
+#endif
+
+#include <algorithm>
+
+#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,Cookie,ASCIICINullSafeComparator> strcookie_map_t; //<! String to Cookie map
+
+ typedef enum {
+ urlencoded,
+ multipart
+ } postformat_t; //<! Enumeration of possible post encodings, url encoding or multipart
+
+ /*! Base class for request and response */
+ class HTTPBase {
+ public:
+ /*! Default renderer for request/response, simply copies body to response */
+ class SendBodyRender {
+ public:
+ SendBodyRender() {};
+
+ size_t operator()(const HTTPBase *doc, std::ostream& os, bool chunked) const {
+ if (chunked) {
+ std::string::size_type i,cl;
+ for(i=0;i<doc->body.length();i+=1024) {
+ cl = std::min(static_cast<std::string::size_type>(1024), doc->body.length()-i); // for less than 1k blocks
+ os << std::hex << cl << std::dec << "\r\n";
+ os << doc->body.substr(i, cl) << "\r\n";
+ }
+ os << 0 << "\r\n\r\n"; // last chunk
+ } else {
+ 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 __attribute__((unused)), std::ostream& os, bool chunked) 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.good()) {
+ ifs.read(buf, sizeof buf);
+ n += (k = ifs.gcount());
+ if (k > 0) {
+ if (chunked) os << std::hex << k << std::dec << "\r\n";
+ os.write(buf, k);
+ if (chunked) os << "\r\n";
+ }
+ }
+ if (chunked) os << 0 << "\r\n\r\n";
+ return n;
+ }; //<! writes file to ostream and returns length
+
+ std::string path; //<! File to send
+ };
+
+ HTTPBase() {
+ HTTPBase::initialize();
+ };
+
+ virtual void initialize() {
+ kind = 0;
+ status = 0;
+#ifdef HAVE_CPP_FUNC_PTR
+ renderer = SendBodyRender();
+#endif
+ max_request_size = YAHTTP_MAX_REQUEST_SIZE;
+ max_response_size = YAHTTP_MAX_RESPONSE_SIZE;
+ url = "";
+ method = "";
+ statusText = "";
+ jar.clear();
+ headers.clear();
+ parameters.clear();
+ getvars.clear();
+ postvars.clear();
+ body = "";
+ routeName = "";
+ version = 11; // default to version 1.1
+ is_multipart = false;
+ }
+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; this->max_request_size = rhs.max_request_size;
+ this->max_response_size = rhs.max_response_size; this->version = rhs.version;
+#ifdef HAVE_CPP_FUNC_PTR
+ this->renderer = rhs.renderer;
+#endif
+ this->is_multipart = rhs.is_multipart;
+ };
+ virtual 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; this->max_request_size = rhs.max_request_size;
+ this->max_response_size = rhs.max_response_size; this->version = rhs.version;
+#ifdef HAVE_CPP_FUNC_PTR
+ this->renderer = rhs.renderer;
+#endif
+ this->is_multipart = rhs.is_multipart;
+ return *this;
+ };
+public:
+ URL url; //<! URL of this request/response
+ int kind; //<! Type of object (1 = request, 2 = response)
+ int status; //<! status code
+ int version; //<! http version 9 = 0.9, 10 = 1.0, 11 = 1.1
+ 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; //<! 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; //<! the actual content
+
+ ssize_t max_request_size; //<! maximum size of request
+ ssize_t max_response_size; //<! maximum size of response
+ bool is_multipart; //<! if the request is multipart, prevents Content-Length header
+#ifdef HAVE_CPP_FUNC_PTR
+ funcptr::function<size_t(const HTTPBase*,std::ostream&,bool)> renderer; //<! rendering function
+#endif
+ void write(std::ostream& os) const; //<! writes request to the given output stream
+
+ 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 versionStr(int version_) const {
+ switch(version_) {
+ case 9: return "0.9";
+ case 10: return "1.0";
+ case 11: return "1.1";
+ default: throw YaHTTP::Error("Unsupported version");
+ }
+ };
+
+ 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() { Response::initialize(); };
+ Response(const HTTPBase& rhs): HTTPBase(rhs) {
+ this->kind = YAHTTP_TYPE_RESPONSE;
+ };
+ Response& operator=(const HTTPBase& rhs) override {
+ HTTPBase::operator=(rhs);
+ this->kind = YAHTTP_TYPE_RESPONSE;
+ return *this;
+ };
+ void initialize() override {
+ HTTPBase::initialize();
+ this->kind = YAHTTP_TYPE_RESPONSE;
+ }
+ void initialize(const HTTPBase& rhs) {
+ HTTPBase::initialize();
+ this->kind = YAHTTP_TYPE_RESPONSE;
+ // copy SOME attributes
+ this->url = rhs.url;
+ this->method = rhs.method;
+ this->jar = rhs.jar;
+ this->version = rhs.version;
+ }
+ 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() { Request::initialize(); };
+ Request(const HTTPBase& rhs): HTTPBase(rhs) {
+ this->kind = YAHTTP_TYPE_REQUEST;
+ };
+ Request& operator=(const HTTPBase& rhs) override {
+ HTTPBase::operator=(rhs);
+ this->kind = YAHTTP_TYPE_REQUEST;
+ return *this;
+ };
+ void initialize() override {
+ HTTPBase::initialize();
+ this->kind = YAHTTP_TYPE_REQUEST;
+ }
+ void initialize(const HTTPBase& rhs) {
+ HTTPBase::initialize();
+ this->kind = YAHTTP_TYPE_REQUEST;
+ // copy SOME attributes
+ this->url = rhs.url;
+ this->method = rhs.method;
+ this->jar = rhs.jar;
+ this->version = rhs.version;
+ }
+ void setup(const std::string& method_, const std::string& url_) {
+ this->url.parse(url_);
+ this->headers["host"] = this->url.host.find(":") == std::string::npos ? this->url.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";
+ }; //<! 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, false) << "=" << Utility::encodeURL(i->second, false) << "&";
+ }
+ // remove last bit
+ if (postbuf.str().length()>0)
+ 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";
+ this->is_multipart = true;
+ 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, false) << "\"; charset=UTF-8\r\nContent-Length: " << i->second.size() << "\r\n\r\n"
+ << Utility::encodeURL(i->second, false) << "\r\n";
+ }
+ postbuf << "--";
+ body = postbuf.str();
+ }
+
+ postbuf.str("");
+ postbuf << body.length();
+ // set method and change headers
+ method = "POST";
+ if (!this->is_multipart)
+ 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; //<! target to populate
+ int state; //<! reader state
+ size_t pos; //<! reader position
+
+ 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); //<! key value pair parser helper
+
+ void initialize(T* target_) {
+ chunked = false; chunk_size = 0;
+ bodybuf.str(""); minbody = 0; maxbody = 0;
+ pos = 0; state = 0; this->target = target_;
+ hasBody = false;
+ buffer = "";
+ this->target->initialize();
+ }; //<! Initialize the parser for target and clear state
+ bool feed(const std::string& somedata); //<! Feed data to the parser
+ bool ready() {
+ return (chunked == true && state == 3) || // if it's chunked we get end of data indication
+ (chunked == false && state > 1 &&
+ (!hasBody ||
+ (bodybuf.str().size() <= maxbody &&
+ bodybuf.str().size() >= minbody)
+ )
+ );
+ }; //<! whether we have received enough data
+ void finalize() {
+ bodybuf.flush();
+ if (ready()) {
+ strstr_map_t::iterator cpos = target->headers.find("content-type");
+ if (cpos != target->headers.end() && Utility::iequals(cpos->second, "application/x-www-form-urlencoded", 32)) {
+ target->postvars = Utility::parseUrlParameters(bodybuf.str());
+ }
+ target->body = bodybuf.str();
+ }
+ 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> {
+ };
+
+};