Permalink
Browse files

Sync with upstream

  • Loading branch information...
1 parent dcf0e97 commit c290975778942ed1082ca66918695a5bd2d6bac4 @cmouse cmouse committed Dec 23, 2014
@@ -46,13 +46,17 @@ namespace YaHTTP {
/*! Implements a Cookie jar for storing multiple cookies */
class CookieJar {
public:
- std::map<std::string, Cookie> cookies; //<! cookie container
+ std::map<std::string, Cookie, ASCIICINullSafeComparator> cookies; //<! cookie container
CookieJar() {}; //<! constructs empty cookie jar
CookieJar(const CookieJar & rhs) {
this->cookies = rhs.cookies;
} //<! copy cookies from another cookie jar
+ void clear() {
+ this->cookies.clear();
+ }
+
void keyValuePair(const std::string &keyvalue, std::string &key, std::string &value) {
size_t pos;
pos = keyvalue.find("=");
@@ -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
@@ -43,11 +44,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
@@ -157,14 +162,40 @@ namespace YaHTTP {
}
os << "\r\n";
+ bool cookieSent = false;
+ bool sendChunked = false;
+
+ 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,9 +207,10 @@ 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
};
@@ -193,7 +225,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
}
@@ -18,6 +18,8 @@ namespace funcptr = boost;
#include <unistd.h>
#endif
+#include <algorithm>
+
#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<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 std::map<std::string,Cookie,ASCIICINullSafeComparator> strcookie_map_t; //<! String to Cookie map
typedef enum {
urlencoded,
@@ -41,14 +42,23 @@ namespace YaHTTP {
/*! 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;
+ 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 << "\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
};
@@ -59,7 +69,7 @@ namespace YaHTTP {
this->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
@@ -71,25 +81,42 @@ namespace YaHTTP {
while(ifs && ifs.good()) {
ifs.read(buf, sizeof buf);
n += (k = ifs.gcount());
- if (k)
+ if (k) {
+ if (chunked) os << std::hex << k << "\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
};
-#endif
+
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 = "";
+ }
protected:
HTTPBase(const HTTPBase& rhs) {
this->url = rhs.url; this->kind = rhs.kind;
@@ -136,7 +163,7 @@ namespace YaHTTP {
ssize_t max_response_size; //<! maximum size of response
#ifdef HAVE_CPP_FUNC_PTR
- funcptr::function<size_t(const HTTPBase*,std::ostream&)> renderer; //<! rendering function
+ 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
@@ -154,7 +181,7 @@ namespace YaHTTP {
/*! Response class, represents a HTTP Response document */
class Response: public HTTPBase {
public:
- Response() { this->kind = YAHTTP_TYPE_RESPONSE; };
+ Response() { initialize(); };
Response(const HTTPBase& rhs): HTTPBase(rhs) {
this->kind = YAHTTP_TYPE_RESPONSE;
};
@@ -163,14 +190,18 @@ namespace YaHTTP {
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);
};
/* 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 +210,10 @@ namespace YaHTTP {
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 +276,8 @@ namespace YaHTTP {
bodybuf.str(""); maxbody = 0;
pos = 0; state = 0; this->target = target;
hasBody = false;
+ buffer = "";
+ this->target->initialize();
}; //<! Initialize the parser for target and clear state
int feed(const std::string& somedata); //<! Feed data to the parser
bool ready() {
@@ -155,7 +155,6 @@ namespace YaHTTP {
continue;
}
}
- std::cout << mask.substr(k3) << std::endl;
path << mask.substr(k3);
result = path.str();
return std::make_pair(method, result);
@@ -5,6 +5,20 @@ namespace YaHTTP {
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
+ /*! Case-Insensitive NULL safe comparator for string maps */
+ struct ASCIICINullSafeComparator {
+ bool operator() (const std::string& lhs, const std::string& rhs) const {
+ char v;
+ std::string::const_iterator lhi = lhs.begin();
+ std::string::const_iterator rhi = rhs.begin();
+ for(;lhi != lhs.end() && rhi != rhs.end(); lhi++, rhi++)
+ if ((v = ::tolower(*lhi) - ::tolower(*rhi)) != 0) return v<0;
+ return (::tolower(*lhi) - ::tolower(*rhi))<0;
+ }
+ };
+
+ typedef std::map<std::string,std::string,ASCIICINullSafeComparator> strstr_map_t; //<! String to String map
+
/*! Represents a date/time with utc offset */
class DateTime {
public:
@@ -158,7 +172,6 @@ namespace YaHTTP {
const char *ptr;
if ( (ptr = strptime(cookie_date.c_str(), "%d-%b-%Y %T", &tm)) != NULL) {
while(*ptr && ( ::isspace(*ptr) || ::isalnum(*ptr) )) ptr++;
- std::cerr << ptr << std::endl;
if (*ptr) throw "Unparseable date (non-final)"; // must be final.
fromTm(&tm);
this->utc_offset = 0;
@@ -331,9 +344,9 @@ namespace YaHTTP {
}
}; //<! static HTTP codes to text mappings
- static std::map<std::string,std::string> parseUrlParameters(std::string parameters) {
+ static strstr_map_t parseUrlParameters(std::string parameters) {
std::string::size_type pos = 0;
- std::map<std::string,std::string> 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,13 +402,25 @@ namespace YaHTTP {
return iequals(a,b,a.size());
}; //<! case-insensitive comparison
+ 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++;
+ str.erase(str.begin(), iter);
+ }; //<! removes whitespace from left
+
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 void trim(std::string &str) {
+ trimLeft(str);
+ trimRight(str);
+ }; //<! removes whitespace from left and right
+
static std::string camelizeHeader(const std::string &str) {
std::string::const_iterator iter = str.begin();
std::string result;

0 comments on commit c290975

Please sign in to comment.