From: James Turner Date: Mon, 8 Aug 2011 17:09:30 +0000 (+0100) Subject: Tweaks to queuing of HTTP/1.0 connections, and tests for sequential connections in... X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=902948e3c637969b4ad29c9b6dd756c7ede28ec4;p=simgear.git Tweaks to queuing of HTTP/1.0 connections, and tests for sequential connections in both 1.0 and 1.1 modes. --- diff --git a/simgear/io/HTTPClient.cxx b/simgear/io/HTTPClient.cxx index ddcae484..96ef25bd 100644 --- a/simgear/io/HTTPClient.cxx +++ b/simgear/io/HTTPClient.cxx @@ -25,6 +25,9 @@ using std::string; using std::stringstream; using std::vector; +//#include +//using namespace std; + namespace simgear { @@ -41,7 +44,7 @@ public: state(STATE_CLOSED), port(DEFAULT_HTTP_PORT) { - setTerminator("\r\n"); + } void setServer(const string& h, short p) @@ -65,15 +68,16 @@ public: virtual void handleClose() { + NetChat::handleClose(); + if ((state == STATE_GETTING_BODY) && activeRequest) { // force state here, so responseComplete can avoid closing the // socket again state = STATE_CLOSED; responseComplete(); + } else { + state = STATE_CLOSED; } - - state = STATE_CLOSED; - NetChat::handleClose(); } void queueRequest(const Request_ptr& r) @@ -94,11 +98,12 @@ public: state = STATE_IDLE; } - + activeRequest = r; state = STATE_IDLE; bodyTransferSize = -1; chunkedTransfer = false; + setTerminator("\r\n"); stringstream headerData; string path = r->path(); @@ -188,6 +193,19 @@ public: { return (state == STATE_SOCKET_ERROR); } + + bool shouldStartNext() const + { + return !activeRequest && !queuedRequests.empty() && + ((state == STATE_CLOSED) || (state == STATE_IDLE)); + } + + void startNext() + { + Request_ptr next = queuedRequests.front(); + queuedRequests.pop_front(); + startRequest(next); + } private: bool connectToHost() { @@ -308,6 +326,7 @@ private: { activeRequest->responseComplete(); client->requestFinished(this); + //cout << "response complete: " << activeRequest->url() << endl; bool doClose = activeRequest->closeAfterComplete(); activeRequest = NULL; @@ -322,10 +341,12 @@ private: setTerminator("\r\n"); - if (!queuedRequests.empty()) { - Request_ptr next = queuedRequests.front(); - queuedRequests.pop_front(); - startRequest(next); + // if we have more requests, and we're idle, can start the next + // request immediately. Note we cannot do this if we're in STATE_CLOSED, + // since NetChannel::close cleans up state after calling handleClose; + // instead we pick up waiting requests in update() + if (!queuedRequests.empty() && (state == STATE_IDLE)) { + startNext(); } else { idleTime.stamp(); } @@ -374,6 +395,10 @@ void Client::update() delete del->second; _connections.erase(del); } else { + if (it->second->shouldStartNext()) { + it->second->startNext(); + } + ++it; } } // of connecion iteration diff --git a/simgear/io/test_HTTP.cxx b/simgear/io/test_HTTP.cxx index d452ec1e..048cbb41 100644 --- a/simgear/io/test_HTTP.cxx +++ b/simgear/io/test_HTTP.cxx @@ -12,6 +12,7 @@ #include #include #include +#include using std::cout; using std::cerr; @@ -50,11 +51,31 @@ public: HTTP::Request(url), complete(false) { - } + std::map sendHeaders; std::map headers; protected: + string_list requestHeaders() const + { + string_list r; + std::map::const_iterator it; + for (it = sendHeaders.begin(); it != sendHeaders.end(); ++it) { + r.push_back(it->first); + } + return r; + } + + string header(const string& name) const + { + std::map::const_iterator it = sendHeaders.find(name); + if (it == sendHeaders.end()) { + return string(); + } + + return it->second; + } + virtual void responseHeadersComplete() { } @@ -132,9 +153,8 @@ public: } string key = strutils::simplify(buffer.substr(0, colonPos)); - string lkey = boost::to_lower_copy(key); string value = strutils::strip(buffer.substr(colonPos + 1)); - requestHeaders[lkey] = value; + requestHeaders[key] = value; buffer.clear(); } else if (state == STATE_REQUEST_BODY) { @@ -144,7 +164,19 @@ public: void receivedRequestHeaders() { state = STATE_IDLE; + if (path == "/test1") { + 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_headers") { + COMPARE(requestHeaders["X-Foo"], string("Bar")); + COMPARE(requestHeaders["X-AnotherHeader"], string("A longer value")); + string contentStr(BODY1); stringstream d; d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n"; @@ -171,27 +203,27 @@ public: push(d.str().c_str()); } else if (path == "http://www.google.com/test2") { // proxy test - if (requestHeaders["host"] != "www.google.com") { + if (requestHeaders["Host"] != "www.google.com") { sendErrorResponse(400, true, "bad destination"); } - if (requestHeaders["proxy-authorization"] != string()) { + if (requestHeaders["Proxy-Authorization"] != string()) { sendErrorResponse(401, false, "bad auth"); // shouldn't supply auth } sendBody2(); } else if (path == "http://www.google.com/test3") { // proxy test - if (requestHeaders["host"] != "www.google.com") { + if (requestHeaders["Host"] != "www.google.com") { sendErrorResponse(400, true, "bad destination"); } - if (requestHeaders["proxy-authorization"] != "ABCDEF") { + if (requestHeaders["Proxy-Authorization"] != "ABCDEF") { sendErrorResponse(401, false, "bad auth"); // forbidden } sendBody2(); - } else if (path == "/test_1_0") { + } else if (strutils::starts_with(path, "/test_1_0")) { string contentStr(BODY1); stringstream d; d << "HTTP/1.0 " << 200 << " " << reasonForCode(200) << "\r\n"; @@ -281,27 +313,29 @@ public: } }; -void waitForComplete(TestRequest* tr) +void waitForComplete(HTTP::Client* cl, TestRequest* tr) { SGTimeStamp start(SGTimeStamp::now()); while (start.elapsedMSec() < 1000) { - NetChannel::poll(10); + cl->update(); if (tr->complete) { return; } + sleepForMSec(1); } cerr << "timed out" << endl; } -void waitForFailed(TestRequest* tr) +void waitForFailed(HTTP::Client* cl, TestRequest* tr) { SGTimeStamp start(SGTimeStamp::now()); while (start.elapsedMSec() < 1000) { - NetChannel::poll(10); + cl->update(); if (tr->failed) { return; } + sleepForMSec(1); } cerr << "timed out waiting for failure" << endl; @@ -334,7 +368,7 @@ int main(int argc, char* argv[]) HTTP::Request_ptr own(tr); cl.makeRequest(tr); - waitForComplete(tr); + waitForComplete(&cl, tr); COMPARE(tr->responseCode(), 200); COMPARE(tr->responseReason(), string("OK")); COMPARE(tr->responseLength(), strlen(BODY1)); @@ -342,6 +376,21 @@ int main(int argc, char* argv[]) COMPARE(tr->bodyData, string(BODY1)); } + { + TestRequest* tr = new TestRequest("http://localhost:2000/test_headers"); + HTTP::Request_ptr own(tr); + tr->sendHeaders["X-Foo"] = "Bar"; + tr->sendHeaders["X-AnotherHeader"] = "A longer value"; + cl.makeRequest(tr); + + waitForComplete(&cl, tr); + COMPARE(tr->responseCode(), 200); + COMPARE(tr->responseReason(), string("OK")); + COMPARE(tr->responseLength(), strlen(BODY1)); + COMPARE(tr->responseBytesReceived(), strlen(BODY1)); + COMPARE(tr->bodyData, string(BODY1)); + } + // larger get request for (unsigned int i=0; i> 2); @@ -351,7 +400,7 @@ int main(int argc, char* argv[]) TestRequest* tr = new TestRequest("http://localhost:2000/test2"); HTTP::Request_ptr own(tr); cl.makeRequest(tr); - waitForComplete(tr); + waitForComplete(&cl, tr); COMPARE(tr->responseCode(), 200); COMPARE(tr->responseBytesReceived(), body2Size); COMPARE(tr->bodyData, string(body2, body2Size)); @@ -362,7 +411,7 @@ int main(int argc, char* argv[]) HTTP::Request_ptr own(tr); cl.makeRequest(tr); - waitForComplete(tr); + waitForComplete(&cl, tr); COMPARE(tr->responseCode(), 200); COMPARE(tr->responseReason(), string("OK")); COMPARE(tr->responseBytesReceived(), 30); @@ -376,7 +425,7 @@ int main(int argc, char* argv[]) TestRequest* tr = new TestRequest("http://localhost:2000/not-found"); HTTP::Request_ptr own(tr); cl.makeRequest(tr); - waitForComplete(tr); + waitForComplete(&cl, tr); COMPARE(tr->responseCode(), 404); COMPARE(tr->responseReason(), string("not found")); COMPARE(tr->responseLength(), 0); @@ -388,7 +437,7 @@ int main(int argc, char* argv[]) TestRequest* tr = new TestRequest("http://localhost:2000/test_1_0"); HTTP::Request_ptr own(tr); cl.makeRequest(tr); - waitForComplete(tr); + waitForComplete(&cl, tr); COMPARE(tr->responseCode(), 200); COMPARE(tr->responseLength(), strlen(BODY1)); COMPARE(tr->bodyData, string(BODY1)); @@ -400,7 +449,7 @@ int main(int argc, char* argv[]) TestRequest* tr = new TestRequest("http://localhost:2000/test_close"); HTTP::Request_ptr own(tr); cl.makeRequest(tr); - waitForComplete(tr); + waitForComplete(&cl, tr); COMPARE(tr->responseCode(), 200); COMPARE(tr->responseLength(), strlen(BODY1)); COMPARE(tr->bodyData, string(BODY1)); @@ -422,7 +471,7 @@ int main(int argc, char* argv[]) TestRequest* tr = new TestRequest("http://www.google.com/test2"); HTTP::Request_ptr own(tr); cl.makeRequest(tr); - waitForComplete(tr); + waitForComplete(&cl, tr); COMPARE(tr->responseCode(), 200); COMPARE(tr->responseLength(), body2Size); COMPARE(tr->bodyData, string(body2, body2Size)); @@ -433,12 +482,61 @@ int main(int argc, char* argv[]) TestRequest* tr = new TestRequest("http://www.google.com/test3"); HTTP::Request_ptr own(tr); cl.makeRequest(tr); - waitForComplete(tr); + waitForComplete(&cl, tr); COMPARE(tr->responseCode(), 200); COMPARE(tr->responseBytesReceived(), body2Size); COMPARE(tr->bodyData, string(body2, body2Size)); } +// pipelining + { + cl.setProxy("", 80); + TestRequest* tr = new TestRequest("http://localhost:2000/test1"); + HTTP::Request_ptr own(tr); + cl.makeRequest(tr); + + + TestRequest* tr2 = new TestRequest("http://localhost:2000/test1"); + HTTP::Request_ptr own2(tr2); + cl.makeRequest(tr2); + + TestRequest* tr3 = new TestRequest("http://localhost:2000/test1"); + HTTP::Request_ptr own3(tr3); + cl.makeRequest(tr3); + + waitForComplete(&cl, tr3); + VERIFY(tr->complete); + VERIFY(tr2->complete); + COMPARE(tr->bodyData, string(BODY1)); + COMPARE(tr2->bodyData, string(BODY1)); + COMPARE(tr3->bodyData, string(BODY1)); + } + +// multiple requests with an HTTP 1.0 server + { + cout << "http 1.0 multiple requests" << endl; + + cl.setProxy("", 80); + TestRequest* tr = new TestRequest("http://localhost:2000/test_1_0/A"); + HTTP::Request_ptr own(tr); + cl.makeRequest(tr); + + TestRequest* tr2 = new TestRequest("http://localhost:2000/test_1_0/B"); + HTTP::Request_ptr own2(tr2); + cl.makeRequest(tr2); + + TestRequest* tr3 = new TestRequest("http://localhost:2000/test_1_0/C"); + HTTP::Request_ptr own3(tr3); + cl.makeRequest(tr3); + + waitForComplete(&cl, tr3); + VERIFY(tr->complete); + VERIFY(tr2->complete); + COMPARE(tr->bodyData, string(BODY1)); + COMPARE(tr2->bodyData, string(BODY1)); + COMPARE(tr3->bodyData, string(BODY1)); + } + cout << "all tests passed ok" << endl; return EXIT_SUCCESS; }