From 219b84c922ca5460845154931a051b7f1824f8fb Mon Sep 17 00:00:00 2001 From: Aki Tuomi Date: Wed, 21 Jan 2015 13:18:39 +0200 Subject: [PATCH] Update to v0.1.5 --- pdns/ext/yahttp/yahttp/cookie.hpp | 6 +- pdns/ext/yahttp/yahttp/reqresp.cpp | 118 +++++++++++++++++++++-------- pdns/ext/yahttp/yahttp/reqresp.hpp | 80 +++++++++++++++---- pdns/ext/yahttp/yahttp/router.cpp | 1 - pdns/ext/yahttp/yahttp/utility.hpp | 33 +++++++- 5 files changed, 187 insertions(+), 51 deletions(-) diff --git a/pdns/ext/yahttp/yahttp/cookie.hpp b/pdns/ext/yahttp/yahttp/cookie.hpp index 4b285aa76..b99b21e01 100644 --- a/pdns/ext/yahttp/yahttp/cookie.hpp +++ b/pdns/ext/yahttp/yahttp/cookie.hpp @@ -46,13 +46,17 @@ namespace YaHTTP { /*! Implements a Cookie jar for storing multiple cookies */ class CookieJar { public: - std::map cookies; // cookies; //cookies = rhs.cookies; } //cookies.clear(); + } + void keyValuePair(const std::string &keyvalue, std::string &key, std::string &value) { size_t pos; pos = keyvalue.find("="); diff --git a/pdns/ext/yahttp/yahttp/reqresp.cpp b/pdns/ext/yahttp/yahttp/reqresp.cpp index 14196b998..07a408a34 100644 --- a/pdns/ext/yahttp/yahttp/reqresp.cpp +++ b/pdns/ext/yahttp/yahttp/reqresp.cpp @@ -6,9 +6,10 @@ namespace YaHTTP { buffer.append(somedata); while(state < 2) { int cr=0; + pos = buffer.find_first_of("\n"); // need to find CRLF in buffer - if ((pos = buffer.find_first_of("\n")) == std::string::npos) return false; - if (buffer[pos-1]=='\r') + if (pos == std::string::npos) return false; + if (pos>0 && 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 @@ -19,8 +20,16 @@ namespace YaHTTP { 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"); + if (ver.size() == 0) + target->version = 9; + else if (ver.find("HTTP/0.9") == 0) + target->version = 9; + else if (ver.find("HTTP/1.0") == 0) + target->version = 10; + else if (ver.find("HTTP/1.1") == 0) + target->version = 11; + else + throw ParseError("HTTP version not supported"); // uppercase the target method std::transform(target->method.begin(), target->method.end(), target->method.begin(), ::toupper); target->url.parse(tmpurl); @@ -29,9 +38,25 @@ namespace YaHTTP { } 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"); + std::string::size_type pos1; + iss >> ver >> target->status; + std::getline(iss, target->statusText); + pos1=0; + while(pos1 < target->statusText.size() && ::isspace(target->statusText.at(pos1))) pos1++; + target->statusText = target->statusText.substr(pos1); + if ((pos1 = target->statusText.find("\r")) != std::string::npos) { + target->statusText = target->statusText.substr(0, pos1-1); + } + if (ver.size() == 0) { + target->version = 9; + } else if (ver.find("HTTP/0.9") == 0) + target->version = 9; + else if (ver.find("HTTP/1.0") == 0) + target->version = 10; + else if (ver.find("HTTP/1.1") == 0) + target->version = 11; + else + throw ParseError("HTTP version not supported"); state = 1; } } else if (state == 1) { @@ -43,11 +68,15 @@ namespace YaHTTP { break; } // split headers - if ((pos = line.find_first_of(": ")) == std::string::npos) + if ((pos = line.find(": ")) == std::string::npos) throw ParseError("Malformed header line"); key = line.substr(0, pos); value = line.substr(pos+2); - Utility::trimRight(value); + for(std::string::iterator it=key.begin(); it != key.end(); it++) + if (std::isspace(*it)) + throw ParseError("Header key contains whitespace which is not allowed by RFC"); + + Utility::trim(value); std::transform(key.begin(), key.end(), key.begin(), ::tolower); // is it already defined @@ -56,7 +85,7 @@ namespace YaHTTP { target->jar.parseCookieHeader(value); } else { if (key == "host" && target->kind == YAHTTP_TYPE_REQUEST) { - // maybe it contains port? + // maybe it contains port? if ((pos = value.find(":")) == std::string::npos) { target->url.host = value; } else { @@ -76,9 +105,9 @@ namespace YaHTTP { minbody = 0; // check for expected body size if (target->kind == YAHTTP_TYPE_REQUEST) maxbody = target->max_request_size; - else if (target->kind == YAHTTP_TYPE_RESPONSE) maxbody = target->max_response_size; + else if (target->kind == YAHTTP_TYPE_RESPONSE) maxbody = target->max_response_size; else maxbody = 0; - + if (!chunked) { if (target->headers.find("content-length") != target->headers.end()) { std::istringstream maxbodyS(target->headers["content-length"]); @@ -114,7 +143,7 @@ namespace YaHTTP { if (buffer.at(chunk_size) == '\r') { if (buffer.size() < static_cast(chunk_size+2) || buffer.at(chunk_size+1) != '\n') return false; // expect newline after carriage return crlf=2; - } else if (buffer.at(chunk_size) != '\n') return false; + } else if (buffer.at(chunk_size) != '\n') return false; std::string tmp = buffer.substr(0, chunk_size); buffer.erase(buffer.begin(), buffer.begin()+chunk_size+crlf); bodybuf << tmp; @@ -122,9 +151,9 @@ namespace YaHTTP { if (buffer.size() == 0) break; // just in case } } else { - if (bodybuf.str().length() + buffer.length() > maxbody) + if (bodybuf.str().length() + buffer.length() > maxbody) bodybuf << buffer.substr(0, maxbody - bodybuf.str().length()); - else + else bodybuf << buffer; buffer = ""; } @@ -134,37 +163,65 @@ namespace YaHTTP { return ready(); }; - + void HTTPBase::write(std::ostream& os) const { if (kind == YAHTTP_TYPE_REQUEST) { std::ostringstream getparmbuf; std::string getparms; - // prepare URL + // prepare URL for(strstr_map_t::const_iterator i = getvars.begin(); i != getvars.end(); i++) { getparmbuf << Utility::encodeURL(i->first, false) << "=" << Utility::encodeURL(i->second, false) << "&"; } - if (getparmbuf.str().length() > 0) + 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"; + os << method << " " << url.path << getparms << " HTTP/" << versionStr(this->version); } else if (kind == YAHTTP_TYPE_RESPONSE) { - os << "HTTP/1.1 " << status << " "; + os << "HTTP/" << versionStr(this->version) << " " << status << " "; if (statusText.empty()) os << Utility::status2text(status); else os << statusText; } os << "\r\n"; - + + bool cookieSent = false; + bool sendChunked = false; + + if (this->version > 10) { // 1.1 or better + if (headers.find("content-length") == headers.end()) { + // must use chunked on response + sendChunked = (kind == YAHTTP_TYPE_RESPONSE); + if ((headers.find("transfer-encoding") != headers.end() && headers.find("transfer-encoding")->second != "chunked")) { + throw YaHTTP::Error("Transfer-encoding must be chunked, or Content-Length defined"); + } + if ((headers.find("transfer-encoding") == headers.end() && kind == YAHTTP_TYPE_RESPONSE)) { + sendChunked = true; + // write the header now + os << "Transfer-Encoding: chunked" << "\r\n"; + } + } else { + if ((headers.find("transfer-encoding") == headers.end() && kind == YAHTTP_TYPE_RESPONSE)) { + sendChunked = true; + // write the header now + os << "Transfer-Encoding: chunked" << "\r\n"; + } else if (headers.find("transfer-encoding") != headers.end() && headers.find("transfer-encoding")->second == "chunked") { + sendChunked = true; + } + } + } + // write headers strstr_map_t::const_iterator iter = headers.begin(); while(iter != headers.end()) { if (iter->first == "host" && kind != YAHTTP_TYPE_REQUEST) { iter++; continue; } + std::string header = Utility::camelizeHeader(iter->first); + if (header == "Cookie" || header == "Set-Cookie") cookieSent = true; os << Utility::camelizeHeader(iter->first) << ": " << iter->second << "\r\n"; iter++; } - if (jar.cookies.size() > 0) { // write cookies + if (!cookieSent && 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: "; @@ -176,40 +233,41 @@ namespace YaHTTP { } os << "\r\n"; #ifdef HAVE_CPP_FUNC_PTR - this->renderer(this, os); + this->renderer(this, os, sendChunked); #else - os << body; + SendbodyRenderer r; + r(this, os, chunked) #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 + if (is.gcount()>0) { // 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) + 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); diff --git a/pdns/ext/yahttp/yahttp/reqresp.hpp b/pdns/ext/yahttp/yahttp/reqresp.hpp index 29718806b..8916c8e71 100644 --- a/pdns/ext/yahttp/yahttp/reqresp.hpp +++ b/pdns/ext/yahttp/yahttp/reqresp.hpp @@ -18,6 +18,8 @@ namespace funcptr = boost; #include #endif +#include + #ifndef YAHTTP_MAX_REQUEST_SIZE #define YAHTTP_MAX_REQUEST_SIZE 2097152 #endif @@ -30,8 +32,7 @@ namespace funcptr = boost; #define YAHTTP_TYPE_RESPONSE 2 namespace YaHTTP { - typedef std::map strstr_map_t; // strcookie_map_t; // strcookie_map_t; //body; + size_t operator()(const HTTPBase *doc, std::ostream& os, bool chunked) const { + if (chunked) { + std::string::size_type i,cl; + for(i=0;ibody.length();i+=1024) { + cl = std::min(static_cast(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(); }; //path = path; }; - size_t operator()(const HTTPBase *doc __attribute__((unused)), std::ostream& os) const { + size_t operator()(const HTTPBase *doc __attribute__((unused)), std::ostream& os, bool chunked) const { char buf[4096]; size_t n,k; #ifdef HAVE_CXX11 @@ -68,20 +78,28 @@ namespace YaHTTP { 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) + if (k) { + 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; }; //url = rhs.url; this->kind = rhs.kind; @@ -98,7 +127,7 @@ protected: 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->max_response_size = rhs.max_response_size; this->version = rhs.version; #ifdef HAVE_CPP_FUNC_PTR this->renderer = rhs.renderer; #endif @@ -110,7 +139,7 @@ protected: 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->max_response_size = rhs.max_response_size; this->version = rhs.version; #ifdef HAVE_CPP_FUNC_PTR this->renderer = rhs.renderer; #endif @@ -120,6 +149,7 @@ public: URL url; // renderer; // renderer; //kind = YAHTTP_TYPE_RESPONSE; }; + Response() { initialize(); }; Response(const HTTPBase& rhs): HTTPBase(rhs) { this->kind = YAHTTP_TYPE_RESPONSE; }; @@ -163,6 +202,10 @@ public: this->kind = YAHTTP_TYPE_RESPONSE; return *this; }; + void initialize() { + HTTPBase::initialize(); + this->kind = YAHTTP_TYPE_RESPONSE; + } friend std::ostream& operator<<(std::ostream& os, const Response &resp); friend std::istream& operator>>(std::istream& is, Response &resp); }; @@ -170,7 +213,7 @@ public: /* Request class, represents a HTTP Request document */ class Request: public HTTPBase { public: - Request() { this->kind = YAHTTP_TYPE_REQUEST; }; + Request() { initialize(); }; Request(const HTTPBase& rhs): HTTPBase(rhs) { this->kind = YAHTTP_TYPE_REQUEST; }; @@ -179,7 +222,10 @@ public: this->kind = YAHTTP_TYPE_REQUEST; return *this; }; - + void initialize() { + HTTPBase::initialize(); + this->kind = YAHTTP_TYPE_REQUEST; + } void setup(const std::string& method, const std::string& url) { this->url.parse(url); this->headers["host"] = this->url.host; @@ -242,6 +288,8 @@ public: bodybuf.str(""); maxbody = 0; pos = 0; state = 0; this->target = target; hasBody = false; + buffer = ""; + this->target->initialize(); }; // strstr_map_t; //utc_offset = 0; @@ -331,9 +346,9 @@ namespace YaHTTP { } }; // parseUrlParameters(std::string parameters) { + static strstr_map_t parseUrlParameters(std::string parameters) { std::string::size_type pos = 0; - std::map parameter_map; + strstr_map_t parameter_map; while (pos != std::string::npos) { // find next parameter start std::string::size_type nextpos = parameters.find("&", pos); @@ -389,6 +404,13 @@ namespace YaHTTP { return iequals(a,b,a.size()); }; //