From fd09261a517a52b387ce1e1d3a201fb26fe6b5af Mon Sep 17 00:00:00 2001 From: Remi Gacogne Date: Mon, 15 May 2017 13:27:23 +0200 Subject: [PATCH] YaHTTP: Sync with upstream changes Backport changes from upstream up to c5b83288a4c2f8ec07cb8cb7bd150f2210db67b6 "Add missing `YaHTTP::isdigit()`, fix locale-enabled versions" --- ext/yahttp/yahttp/cookie.hpp | 126 ++++++++++++++++++---------------- ext/yahttp/yahttp/reqresp.cpp | 72 +++++++++++++++---- ext/yahttp/yahttp/reqresp.hpp | 17 +++-- ext/yahttp/yahttp/url.hpp | 2 +- ext/yahttp/yahttp/utility.hpp | 51 +++++++++----- ext/yahttp/yahttp/yahttp.hpp | 3 +- 6 files changed, 169 insertions(+), 102 deletions(-) diff --git a/ext/yahttp/yahttp/cookie.hpp b/ext/yahttp/yahttp/cookie.hpp index dd5433580d..c597c7c811 100644 --- a/ext/yahttp/yahttp/cookie.hpp +++ b/ext/yahttp/yahttp/cookie.hpp @@ -6,16 +6,29 @@ namespace YaHTTP { secure = false; httponly = false; name = value = ""; + expires = DateTime(); }; //!< Set the cookie to empty value Cookie(const Cookie &rhs) { + name = rhs.name; + value = rhs.value; domain = rhs.domain; path = rhs.path; secure = rhs.secure; httponly = rhs.httponly; + expires = rhs.expires; + }; //0) @@ -66,74 +80,64 @@ namespace YaHTTP { } // lcookies; - int cstate = 0; //cookiestate - size_t pos,npos; + Cookie c; 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::iterator i = lcookies.begin(); i != lcookies.end(); i++) - i->expires = dt; - } else if (s == "domain") { - for(std::list::iterator i = lcookies.begin(); i != lcookies.end(); i++) - i->domain = value; - } else if (s == "path") { - for(std::list::iterator i = lcookies.begin(); i != lcookies.end(); i++) - i->path = value; - } - } else if (cookiestr.compare(pos, 8, "httpOnly")==0) { - cstate = 1; - for(std::list::iterator i = lcookies.begin(); i != lcookies.end(); i++) - i->httponly = true; - } else if (cookiestr.compare(pos, 6, "secure") ==0) { - cstate = 1; - for(std::list::iterator i = lcookies.begin(); i != lcookies.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); - lcookies.push_back(c); - } else if (cstate == 1) { + if ((npos = cookiestr.find("; ", pos)) == std::string::npos) + npos = cookiestr.size(); + keyValuePair(cookiestr.substr(pos, npos-pos), c.name, c.value); + c.name = YaHTTP::Utility::decodeURL(c.name); + c.value = YaHTTP::Utility::decodeURL(c.value); + lcookies.push_back(c); + pos = npos+2; + } + for(std::list::iterator i = lcookies.begin(); i != lcookies.end(); i++) { + this->cookies[i->name] = *i; + } + } + + void parseSetCookieHeader(const std::string &cookiestr) { + Cookie c; + size_t pos,npos; + std::string k, v; + + if ((pos = cookiestr.find("; ", 0)) == std::string::npos) + pos = cookiestr.size(); + keyValuePair(cookiestr.substr(0, pos), c.name, c.value); + c.name = YaHTTP::Utility::decodeURL(c.name); + c.value = YaHTTP::Utility::decodeURL(c.value); + if (pos < cookiestr.size()) pos+=2; + + while(pos < cookiestr.size()) { + if ((npos = cookiestr.find("; ", pos)) == std::string::npos) + npos = cookiestr.size(); + std::string s = cookiestr.substr(pos, npos-pos); + if (s.find("=") != std::string::npos) + keyValuePair(s, k, v); + else + k = s; + if (k == "expires") { + DateTime dt; + dt.parseCookie(v); + c.expires = dt; + } else if (k == "domain") { + c.domain = v; + } else if (k == "path") { + c.path = v; + } else if (k == "httpOnly") { + c.httponly = true; + } else if (k == "secure") { + c.secure = true; + } else { // ignore crap break; } + pos = npos+2; } - // store cookies - for(std::list::iterator i = lcookies.begin(); i != lcookies.end(); i++) { - this->cookies[i->name] = *i; - } + this->cookies[c.name] = c; }; // - int AsyncLoader::feed(const std::string& somedata) { + bool AsyncLoader::feed(const std::string& somedata) { buffer.append(somedata); while(state < 2) { int cr=0; @@ -42,7 +75,7 @@ namespace YaHTTP { iss >> ver >> target->status; std::getline(iss, target->statusText); pos1=0; - while(pos1 < target->statusText.size() && ::isspace(target->statusText.at(pos1))) pos1++; + while(pos1 < target->statusText.size() && YaHTTP::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); @@ -73,15 +106,16 @@ namespace YaHTTP { key = line.substr(0, pos1); value = line.substr(pos1+2); for(std::string::iterator it=key.begin(); it != key.end(); it++) - if (std::isspace(*it)) + if (YaHTTP::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 - if ((key == "set-cookie" && target->kind == YAHTTP_TYPE_RESPONSE) || - (key == "cookie" && target->kind == YAHTTP_TYPE_REQUEST)) { + if (key == "set-cookie" && target->kind == YAHTTP_TYPE_RESPONSE) { + target->jar.parseSetCookieHeader(value); + } else if (key == "cookie" && target->kind == YAHTTP_TYPE_REQUEST) { target->jar.parseCookieHeader(value); } else { if (key == "host" && target->kind == YAHTTP_TYPE_REQUEST) { @@ -136,7 +170,7 @@ namespace YaHTTP { 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) { state = 3; break; } // last chunk + if (chunk_size == 0) { state = 3; break; } // last chunk } else { int crlf=1; if (buffer.size() < static_cast(chunk_size+1)) return false; // expect newline @@ -192,7 +226,7 @@ namespace YaHTTP { bool sendChunked = false; if (this->version > 10) { // 1.1 or better - if (headers.find("content-length") == headers.end()) { + if (headers.find("content-length") == headers.end() && !this->is_multipart) { // must use chunked on response sendChunked = (kind == YAHTTP_TYPE_RESPONSE); if ((headers.find("transfer-encoding") != headers.end() && headers.find("transfer-encoding")->second != "chunked")) { @@ -210,21 +244,29 @@ namespace YaHTTP { // write headers strstr_map_t::const_iterator iter = headers.begin(); while(iter != headers.end()) { - if (iter->first == "host" && kind != YAHTTP_TYPE_REQUEST) { iter++; continue; } + if (iter->first == "host" && (kind != YAHTTP_TYPE_REQUEST || version < 10)) { iter++; continue; } if (iter->first == "transfer-encoding" && sendChunked) { 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 (!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: "; - } else { + if (version > 9 && !cookieSent && jar.cookies.size() > 0) { // write cookies + if (kind == YAHTTP_TYPE_REQUEST) { + bool first = true; + os << "Cookie: "; + for(strcookie_map_t::const_iterator i = jar.cookies.begin(); i != jar.cookies.end(); i++) { + if (first) + first = false; + else + os << "; "; + os << Utility::encodeURL(i->second.name) << "=" << Utility::encodeURL(i->second.value); + } + } else if (kind == YAHTTP_TYPE_REQUEST) { + for(strcookie_map_t::const_iterator i = jar.cookies.begin(); i != jar.cookies.end(); i++) { os << "Set-Cookie: "; + os << i->second.str() << "\r\n"; } - os << i->second.str() << "\r\n"; } } os << "\r\n"; @@ -270,7 +312,7 @@ namespace YaHTTP { 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 } diff --git a/ext/yahttp/yahttp/reqresp.hpp b/ext/yahttp/yahttp/reqresp.hpp index 5637e44421..2e2b609cb5 100644 --- a/ext/yahttp/yahttp/reqresp.hpp +++ b/ext/yahttp/yahttp/reqresp.hpp @@ -79,10 +79,10 @@ namespace YaHTTP { #endif n = 0; - while(ifs && ifs.good()) { + while(ifs.good()) { ifs.read(buf, sizeof buf); n += (k = ifs.gcount()); - if (k) { + if (k > 0) { if (chunked) os << std::hex << k << std::dec << "\r\n"; os.write(buf, k); if (chunked) os << "\r\n"; @@ -118,6 +118,7 @@ namespace YaHTTP { body = ""; routeName = ""; version = 11; // default to version 1.1 + is_multipart = false; } protected: HTTPBase(const HTTPBase& rhs) { @@ -164,7 +165,7 @@ public: ssize_t max_request_size; // renderer; //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\n\r\n" + 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"; - headers["content-length"] = postbuf.str(); + if (!this->is_multipart) + headers["content-length"] = postbuf.str(); }; //target->initialize(); }; // 1 && diff --git a/ext/yahttp/yahttp/url.hpp b/ext/yahttp/yahttp/url.hpp index 8d46276791..e3225ea4f6 100644 --- a/ext/yahttp/yahttp/url.hpp +++ b/ext/yahttp/yahttp/url.hpp @@ -59,7 +59,7 @@ namespace YaHTTP { password = url.substr(pos2+1, pos1 - pos2 - 1); password = Utility::decodeURL(password); } else { - username = url.substr(pos+1, pos1 - pos); + username = url.substr(pos, pos1 - pos); } pos = pos1+1; username = Utility::decodeURL(username); diff --git a/ext/yahttp/yahttp/utility.hpp b/ext/yahttp/yahttp/utility.hpp index 647b023fcd..7d485359e0 100644 --- a/ext/yahttp/yahttp/utility.hpp +++ b/ext/yahttp/yahttp/utility.hpp @@ -5,6 +5,15 @@ namespace YaHTTP { static const char *MONTHS[] = {0,"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",0}; // 6) throw "Invalid date"; - if (month < 1 || month > 12) throw "Invalid date"; - if (year < 0) throw "Invalid date"; + if (wday < 0 || wday > 6) throw std::range_error("Invalid date"); + if (month < 1 || month > 12) throw std::range_error("Invalid date"); + if (year < 0) throw std::range_error("Invalid date"); if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59 || - seconds < 0 || seconds > 60) throw "Invalid date"; + seconds < 0 || seconds > 60) throw std::range_error("Invalid date"); }; //utc_offset = 0; } else { - throw "Unparseable date (did not match pattern cookie)"; + std::cout << cookie_date << std::endl; + throw YaHTTP::ParseError("Unparseable date (did not match pattern cookie)"); } }; //(*iter)); @@ -255,7 +270,7 @@ namespace YaHTTP { std::ostringstream result; std::string skip = "+-.,&;_#%[]?/@(){}="; for(std::vector::iterator iter = vec.begin(); iter != vec.end(); iter++) { - if (!std::isalnum((char)*iter) && (!asUrl || skip.find((char)*iter) == std::string::npos)) { + if (!YaHTTP::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(*iter); } else result << (char)*iter; @@ -409,14 +424,14 @@ namespace YaHTTP { static void trimLeft(std::string &str) { const std::locale &loc = std::locale::classic(); std::string::iterator iter = str.begin(); - while(iter != str.end() && std::isspace(*iter, loc)) iter++; + while(iter != str.end() && YaHTTP::isspace(*iter, loc)) iter++; str.erase(str.begin(), iter); }; // #include #include +#include #include #include #include #include #include "yahttp-config.h" +#include "exception.hpp" #include "url.hpp" #include "utility.hpp" -#include "exception.hpp" #include "url.hpp" #include "cookie.hpp" #include "reqresp.hpp" -- 2.47.2