From 8a4871db8376c60645e71e379f4c96223d530359 Mon Sep 17 00:00:00 2001 From: James Turner Date: Wed, 13 Feb 2019 12:34:17 +0000 Subject: [PATCH] Improve HTTP redirect handling, and add test. Ensure we get the final status code for the request after redirecting. --- simgear/io/HTTPClient.cxx | 14 ++++++++++++++ simgear/io/HTTPRequest.cxx | 10 ++++++++++ simgear/io/HTTPRequest.hxx | 2 +- simgear/io/test_HTTP.cxx | 36 +++++++++++++++++++++++++++++++++++- simgear/io/test_HTTP.hxx | 6 ++---- 5 files changed, 62 insertions(+), 6 deletions(-) diff --git a/simgear/io/HTTPClient.cxx b/simgear/io/HTTPClient.cxx index fe6fa737..fd8ec7e5 100644 --- a/simgear/io/HTTPClient.cxx +++ b/simgear/io/HTTPClient.cxx @@ -472,12 +472,26 @@ size_t Client::requestReadCallback(char *ptr, size_t size, size_t nmemb, void *u return actualBytes; } +bool isRedirectStatus(int code) +{ + return ((code >= 300) && (code < 400)); +} + size_t Client::requestHeaderCallback(char *rawBuffer, size_t size, size_t nitems, void *userdata) { size_t byteSize = size * nitems; Request* req = static_cast(userdata); std::string h = strutils::simplify(std::string(rawBuffer, byteSize)); + if (req->readyState() >= HTTP::Request::HEADERS_RECEIVED) { + // this can happen with chunked transfers (secondary chunks) + // or redirects + if (isRedirectStatus(req->responseCode())) { + req->responseStart(h); + return byteSize; + } + } + if (req->readyState() == HTTP::Request::OPENED) { req->responseStart(h); return byteSize; diff --git a/simgear/io/HTTPRequest.cxx b/simgear/io/HTTPRequest.cxx index 294d2f0c..7266d780 100644 --- a/simgear/io/HTTPRequest.cxx +++ b/simgear/io/HTTPRequest.cxx @@ -328,6 +328,16 @@ unsigned int Request::responseLength() const return _responseLength; } +//------------------------------------------------------------------------------ +void Request::setSuccess(int code) +{ + _responseStatus = code; + _responseReason.clear(); + if( !isComplete() ) { + setReadyState(DONE); + } +} + //------------------------------------------------------------------------------ void Request::setFailure(int code, const std::string& reason) { diff --git a/simgear/io/HTTPRequest.hxx b/simgear/io/HTTPRequest.hxx index 0def0888..9ba8db3e 100644 --- a/simgear/io/HTTPRequest.hxx +++ b/simgear/io/HTTPRequest.hxx @@ -224,7 +224,7 @@ protected: virtual void onAlways(); void setFailure(int code, const std::string& reason); - + void setSuccess(int code); private: friend class Client; friend class Connection; diff --git a/simgear/io/test_HTTP.cxx b/simgear/io/test_HTTP.cxx index 3bf9947d..ccef3f9e 100644 --- a/simgear/io/test_HTTP.cxx +++ b/simgear/io/test_HTTP.cxx @@ -273,7 +273,23 @@ public: d << "\r\n"; // final CRLF to terminate the headers d << contentStr; push(d.str().c_str()); - + } else if (path == "/test_redirect") { + string contentStr("See Here"); + stringstream d; + d << "HTTP/1.1 " << 302 << " " << "Found" << "\r\n"; + d << "Location:" << " http://localhost:2000/was_redirected" << "\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 == "/was_redirected") { + 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 { TestServerChannel::processRequestHeaders(); } @@ -773,6 +789,24 @@ cout << "testing proxy close" << endl; SG_CHECK_EQUAL(tr2->bodyData, string(BODY1)); SG_CHECK_EQUAL(tr2->responseBytesReceived(), strlen(BODY1)); } + + { + cout << "redirect test" << endl; + // redirect test + testServer.disconnectAll(); + cl.clearAllConnections(); + + TestRequest* tr = new TestRequest("http://localhost:2000/test_redirect"); + HTTP::Request_ptr own(tr); + cl.makeRequest(tr); + + waitForComplete(&cl, tr); + SG_CHECK_EQUAL(tr->responseCode(), 200); + SG_CHECK_EQUAL(tr->responseReason(), string("OK")); + SG_CHECK_EQUAL(tr->responseLength(), strlen(BODY1)); + SG_CHECK_EQUAL(tr->responseBytesReceived(), strlen(BODY1)); + SG_CHECK_EQUAL(tr->bodyData, string(BODY1)); + } cout << "all tests passed ok" << endl; return EXIT_SUCCESS; diff --git a/simgear/io/test_HTTP.hxx b/simgear/io/test_HTTP.hxx index 3cb925c0..f8dde98c 100644 --- a/simgear/io/test_HTTP.hxx +++ b/simgear/io/test_HTTP.hxx @@ -30,7 +30,6 @@ public: virtual ~TestServerChannel() { - std::cerr << "dtor test server channel" << std::endl; } virtual void collectIncomingData(const char* s, int n) @@ -139,8 +138,8 @@ public: void sendErrorResponse(int code, bool close, std::string content) { - std::cerr << "sending error " << code << " for " << path << std::endl; - std::cerr << "\tcontent:" << content << std::endl; + // std::cerr << "sending error " << code << " for " << path << std::endl; + // std::cerr << "\tcontent:" << content << std::endl; std::stringstream headerData; headerData << "HTTP/1.1 " << code << " " << reasonForCode(code) << "\r\n"; @@ -168,7 +167,6 @@ public: virtual void handleClose (void) { - std::cerr << "channel close" << std::endl; NetBufferChannel::handleClose(); }