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);
} 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) {
key = line.substr(0, pos);
value = line.substr(pos+2);
for(std::string::iterator it=key.begin(); it != key.end(); it++)
- if (std::isspace(*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
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 {
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"]);
if (buffer.at(chunk_size) == '\r') {
if (buffer.size() < static_cast<size_t>(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;
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 = "";
}
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 (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;
+
+ 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;
+ }
}
}
#ifdef HAVE_CPP_FUNC_PTR
this->renderer(this, os, sendChunked);
#else
- SendbodyRenderer r;
+ 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);
}
}
// 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);
std::ifstream ifs(path.c_str(), std::ifstream::binary);
#endif
n = 0;
+
+ std::cerr << "Sending file " << path << std::endl;
while(ifs && ifs.good()) {
ifs.read(buf, sizeof buf);
n += (k = ifs.gcount());
postvars.clear();
body = "";
routeName = "";
+ version = 11; // default to version 1.1
}
protected:
HTTPBase(const HTTPBase& rhs) {
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
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
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)
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);