From 69ef461e6debc526ce66493af2c6da9119a6f8c2 Mon Sep 17 00:00:00 2001 From: James Turner Date: Sun, 22 Apr 2012 20:21:45 +0100 Subject: [PATCH] Merge commit 'refs/merge-requests/22' of git://gitorious.org/fg/simgear into merge-requests/22 --- simgear/io/HTTPClient.cxx | 38 ++++++++--- simgear/io/HTTPRequest.cxx | 14 ++++ simgear/io/HTTPRequest.hxx | 1 + simgear/io/test_HTTP.cxx | 135 +++++++++++++++++++++++++++++++++++-- 4 files changed, 174 insertions(+), 14 deletions(-) diff --git a/simgear/io/HTTPClient.cxx b/simgear/io/HTTPClient.cxx index fb1b812f..cf19589e 100644 --- a/simgear/io/HTTPClient.cxx +++ b/simgear/io/HTTPClient.cxx @@ -25,9 +25,6 @@ using std::string; using std::stringstream; using std::vector; -//#include -//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 queuedRequests; }; diff --git a/simgear/io/HTTPRequest.cxx b/simgear/io/HTTPRequest.cxx index 38651a3e..84855a4c 100644 --- a/simgear/io/HTTPRequest.cxx +++ b/simgear/io/HTTPRequest.cxx @@ -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()); diff --git a/simgear/io/HTTPRequest.hxx b/simgear/io/HTTPRequest.hxx index 0e346419..8d8a2270 100644 --- a/simgear/io/HTTPRequest.hxx +++ b/simgear/io/HTTPRequest.hxx @@ -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; diff --git a/simgear/io/test_HTTP.cxx b/simgear/io/test_HTTP.cxx index 988f8bba..fc2fe869 100644 --- a/simgear/io/test_HTTP.cxx +++ b/simgear/io/test_HTTP.cxx @@ -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 requestHeaders; + std::map 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; } -- 2.39.5