Merge commit 'refs/merge-requests/22' of git://gitorious.org/fg/simgear into merge-requests/22
This commit is contained in:
parent
4abcf3215f
commit
69ef461e6d
@ -25,9 +25,6 @@ using std::string;
|
||||
using std::stringstream;
|
||||
using std::vector;
|
||||
|
||||
//#include <iostream>
|
||||
//using namespace std;
|
||||
|
||||
namespace simgear
|
||||
{
|
||||
|
||||
@ -35,6 +32,7 @@ namespace HTTP
|
||||
{
|
||||
|
||||
extern const int DEFAULT_HTTP_PORT = 80;
|
||||
const char* CONTENT_TYPE_URL_ENCODED = "application/x-www-form-urlencoded";
|
||||
|
||||
class Connection : public NetChat
|
||||
{
|
||||
@ -103,15 +101,27 @@ public:
|
||||
state = STATE_SENT_REQUEST;
|
||||
bodyTransferSize = -1;
|
||||
chunkedTransfer = false;
|
||||
noMessageBody = (r->method() == "HEAD");
|
||||
setTerminator("\r\n");
|
||||
|
||||
stringstream headerData;
|
||||
string path = r->path();
|
||||
string query = r->query();
|
||||
string bodyData;
|
||||
|
||||
if (!client->proxyHost().empty()) {
|
||||
path = r->url();
|
||||
path = r->scheme() + "://" + r->host() + r->path();
|
||||
}
|
||||
|
||||
headerData << r->method() << " " << path << " HTTP/1.1\r\n";
|
||||
if (r->method() == "POST") {
|
||||
headerData << r->method() << " " << path << " HTTP/1.1\r\n";
|
||||
bodyData = query.substr(1); // URL-encode, drop the leading '?'
|
||||
headerData << "Content-Type:" << CONTENT_TYPE_URL_ENCODED << "\r\n";
|
||||
headerData << "Content-Length:" << bodyData.size() << "\r\n";
|
||||
} else {
|
||||
headerData << r->method() << " " << path << query << " HTTP/1.1\r\n";
|
||||
}
|
||||
|
||||
headerData << "Host: " << r->hostAndPort() << "\r\n";
|
||||
headerData << "User-Agent:" << client->userAgent() << "\r\n";
|
||||
if (!client->proxyAuth().empty()) {
|
||||
@ -123,9 +133,10 @@ public:
|
||||
}
|
||||
|
||||
headerData << "\r\n"; // final CRLF to terminate the headers
|
||||
|
||||
// TODO - add request body support for PUT, etc operations
|
||||
|
||||
if (!bodyData.empty()) {
|
||||
headerData << bodyData;
|
||||
}
|
||||
|
||||
bool ok = push(headerData.str().c_str());
|
||||
if (!ok) {
|
||||
SG_LOG(SG_IO, SG_WARN, "HTTP writing to socket failed");
|
||||
@ -150,6 +161,10 @@ public:
|
||||
activeRequest->responseStart(buffer);
|
||||
state = STATE_GETTING_HEADERS;
|
||||
buffer.clear();
|
||||
if (activeRequest->responseCode() == 204) {
|
||||
noMessageBody = true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case STATE_GETTING_HEADERS:
|
||||
@ -233,6 +248,11 @@ private:
|
||||
|
||||
if (chunkedTransfer) {
|
||||
state = STATE_GETTING_CHUNKED;
|
||||
} else if (noMessageBody || (bodyTransferSize == 0)) {
|
||||
// force the state to GETTING_BODY, to simplify logic in
|
||||
// responseComplete and handleClose
|
||||
state = STATE_GETTING_BODY;
|
||||
responseComplete();
|
||||
} else {
|
||||
setByteCount(bodyTransferSize); // may be -1, that's fine
|
||||
state = STATE_GETTING_BODY;
|
||||
@ -327,7 +347,6 @@ private:
|
||||
{
|
||||
activeRequest->responseComplete();
|
||||
client->requestFinished(this);
|
||||
//cout << "response complete: " << activeRequest->url() << endl;
|
||||
|
||||
bool doClose = activeRequest->closeAfterComplete();
|
||||
activeRequest = NULL;
|
||||
@ -374,6 +393,7 @@ private:
|
||||
int bodyTransferSize;
|
||||
SGTimeStamp idleTime;
|
||||
bool chunkedTransfer;
|
||||
bool noMessageBody;
|
||||
|
||||
std::list<Request_ptr> queuedRequests;
|
||||
};
|
||||
|
@ -125,6 +125,20 @@ string Request::path() const
|
||||
return u.substr(hostEnd, query - hostEnd);
|
||||
}
|
||||
|
||||
|
||||
string Request::query() const
|
||||
{
|
||||
string u(url());
|
||||
int query = u.find('?');
|
||||
if (query < 0) {
|
||||
return ""; //no query string found
|
||||
}
|
||||
|
||||
return u.substr(query); //includes question mark
|
||||
}
|
||||
|
||||
|
||||
|
||||
string Request::host() const
|
||||
{
|
||||
string hp(hostAndPort());
|
||||
|
@ -30,6 +30,7 @@ public:
|
||||
virtual std::string host() const;
|
||||
virtual std::string hostAndPort() const;
|
||||
virtual unsigned short port() const;
|
||||
virtual std::string query() const;
|
||||
|
||||
virtual string_list requestHeaders() const;
|
||||
virtual std::string header(const std::string& name) const;
|
||||
|
@ -46,8 +46,8 @@ public:
|
||||
bool failed;
|
||||
string bodyData;
|
||||
|
||||
TestRequest(const std::string& url) :
|
||||
HTTP::Request(url),
|
||||
TestRequest(const std::string& url, const std::string method = "GET") :
|
||||
HTTP::Request(url, method),
|
||||
complete(false)
|
||||
{
|
||||
}
|
||||
@ -133,6 +133,13 @@ public:
|
||||
|
||||
method = line[0];
|
||||
path = line[1];
|
||||
|
||||
int queryPos = path.find('?');
|
||||
if (queryPos != string::npos) {
|
||||
parseArgs(path.substr(queryPos + 1));
|
||||
path = path.substr(0, queryPos);
|
||||
}
|
||||
|
||||
httpVersion = line[2];
|
||||
requestHeaders.clear();
|
||||
buffer.clear();
|
||||
@ -156,10 +163,28 @@ public:
|
||||
requestHeaders[key] = value;
|
||||
buffer.clear();
|
||||
} else if (state == STATE_REQUEST_BODY) {
|
||||
|
||||
cerr << "done getting requst body";
|
||||
receivedBody();
|
||||
setTerminator("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
void parseArgs(const string& argData)
|
||||
{
|
||||
string_list argv = strutils::split(argData, "&");
|
||||
for (unsigned int a=0; a<argv.size(); ++a) {
|
||||
int eqPos = argv[a].find('=');
|
||||
if (eqPos < 0) {
|
||||
cerr << "malformed HTTP argument:" << argv[a] << endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
string key = argv[a].substr(0, eqPos);
|
||||
string value = argv[a].substr(eqPos + 1);
|
||||
args[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
void receivedRequestHeaders()
|
||||
{
|
||||
state = STATE_IDLE;
|
||||
@ -172,6 +197,14 @@ public:
|
||||
d << "\r\n"; // final CRLF to terminate the headers
|
||||
d << contentStr;
|
||||
push(d.str().c_str());
|
||||
} else if (path == "/test_zero_length_content") {
|
||||
string contentStr;
|
||||
stringstream d;
|
||||
d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
|
||||
d << "Content-Length:" << contentStr.size() << "\r\n";
|
||||
d << "\r\n"; // final CRLF to terminate the headers
|
||||
d << contentStr;
|
||||
push(d.str().c_str());
|
||||
} else if (path == "/test_headers") {
|
||||
COMPARE(requestHeaders["X-Foo"], string("Bar"));
|
||||
COMPARE(requestHeaders["X-AnotherHeader"], string("A longer value"));
|
||||
@ -239,8 +272,53 @@ public:
|
||||
d << contentStr;
|
||||
push(d.str().c_str());
|
||||
closeWhenDone();
|
||||
} else if (path == "/test_args") {
|
||||
if ((args["foo"] != "abc") || (args["bar"] != "1234") || (args["username"] != "johndoe")) {
|
||||
sendErrorResponse(400, true, "bad arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
string contentStr(BODY1);
|
||||
stringstream d;
|
||||
d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
|
||||
d << "Content-Length:" << contentStr.size() << "\r\n";
|
||||
d << "\r\n"; // final CRLF to terminate the headers
|
||||
d << contentStr;
|
||||
push(d.str().c_str());
|
||||
} else if (path == "/test_post") {
|
||||
if (requestHeaders["Content-Type"] != "application/x-www-form-urlencoded") {
|
||||
cerr << "bad content type: '" << requestHeaders["Content-Type"] << "'" << endl;
|
||||
sendErrorResponse(400, true, "bad content type");
|
||||
return;
|
||||
}
|
||||
|
||||
requestContentLength = strutils::to_int(requestHeaders["Content-Length"]);
|
||||
setByteCount(requestContentLength);
|
||||
state = STATE_REQUEST_BODY;
|
||||
} else {
|
||||
sendErrorResponse(404, true, "");
|
||||
sendErrorResponse(404, false, "");
|
||||
}
|
||||
}
|
||||
|
||||
void receivedBody()
|
||||
{
|
||||
state = STATE_IDLE;
|
||||
if (method == "POST") {
|
||||
parseArgs(buffer);
|
||||
}
|
||||
|
||||
if (path == "/test_post") {
|
||||
if ((args["foo"] != "abc") || (args["bar"] != "1234") || (args["username"] != "johndoe")) {
|
||||
sendErrorResponse(400, true, "bad arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
stringstream d;
|
||||
d << "HTTP/1.1 " << 204 << " " << reasonForCode(204) << "\r\n";
|
||||
d << "\r\n"; // final CRLF to terminate the headers
|
||||
push(d.str().c_str());
|
||||
|
||||
cerr << "sent 204 response ok" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,6 +351,7 @@ public:
|
||||
{
|
||||
switch (code) {
|
||||
case 200: return "OK";
|
||||
case 204: return "no content";
|
||||
case 404: return "not found";
|
||||
default: return "unknown code";
|
||||
}
|
||||
@ -284,6 +363,8 @@ public:
|
||||
string path;
|
||||
string httpVersion;
|
||||
std::map<string, string> requestHeaders;
|
||||
std::map<string, string> args;
|
||||
int requestContentLength;
|
||||
};
|
||||
|
||||
class TestServer : public NetChannel
|
||||
@ -374,7 +455,17 @@ int main(int argc, char* argv[])
|
||||
COMPARE(tr->responseBytesReceived(), strlen(BODY1));
|
||||
COMPARE(tr->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
TestRequest* tr = new TestRequest("http://localhost:2000/test_args?foo=abc&bar=1234&username=johndoe");
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
}
|
||||
|
||||
cerr << "done args" << endl;
|
||||
|
||||
{
|
||||
TestRequest* tr = new TestRequest("http://localhost:2000/test_headers");
|
||||
HTTP::Request_ptr own(tr);
|
||||
@ -405,6 +496,7 @@ int main(int argc, char* argv[])
|
||||
COMPARE(tr->bodyData, string(body2, body2Size));
|
||||
}
|
||||
|
||||
cerr << "testing chunked" << endl;
|
||||
{
|
||||
TestRequest* tr = new TestRequest("http://localhost:2000/testchunked");
|
||||
HTTP::Request_ptr own(tr);
|
||||
@ -430,6 +522,16 @@ int main(int argc, char* argv[])
|
||||
COMPARE(tr->responseLength(), 0);
|
||||
}
|
||||
|
||||
cout << "done 404 test" << endl;
|
||||
|
||||
{
|
||||
TestRequest* tr = new TestRequest("http://localhost:2000/test_args?foo=abc&bar=1234&username=johndoe");
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
}
|
||||
|
||||
cout << "done1" << endl;
|
||||
// test HTTP/1.0
|
||||
{
|
||||
@ -536,6 +638,29 @@ int main(int argc, char* argv[])
|
||||
COMPARE(tr3->bodyData, string(BODY1));
|
||||
}
|
||||
|
||||
// POST
|
||||
{
|
||||
cout << "POST" << endl;
|
||||
TestRequest* tr = new TestRequest("http://localhost:2000/test_post?foo=abc&bar=1234&username=johndoe", "POST");
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 204);
|
||||
}
|
||||
|
||||
// test_zero_length_content
|
||||
{
|
||||
cout << "zero-length-content-response" << endl;
|
||||
TestRequest* tr = new TestRequest("http://localhost:2000/test_zero_length_content");
|
||||
HTTP::Request_ptr own(tr);
|
||||
cl.makeRequest(tr);
|
||||
waitForComplete(&cl, tr);
|
||||
COMPARE(tr->responseCode(), 200);
|
||||
COMPARE(tr->bodyData, string());
|
||||
COMPARE(tr->responseBytesReceived(), 0);
|
||||
}
|
||||
|
||||
|
||||
cout << "all tests passed ok" << endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user