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