]> git.mxchange.org Git - simgear.git/blobdiff - simgear/io/test_HTTP.cxx
Unit test for SGBinObj, and fix a bug in large-indice handling the test revealed.
[simgear.git] / simgear / io / test_HTTP.cxx
index 8aafef2d59cd46b5d3d811d03888a553415c8f8c..048cbb41909186e66e00a5c5c1bfcf19f1e89be9 100644 (file)
@@ -12,6 +12,7 @@
 #include <simgear/io/sg_netChat.hxx>
 #include <simgear/misc/strutils.hxx>
 #include <simgear/timing/timestamp.hxx>
+#include <simgear/misc/sg_sleep.hxx>
 
 using std::cout;
 using std::cerr;
@@ -43,16 +44,38 @@ class TestRequest : public HTTP::Request
 {
 public:
     bool complete;
+    bool failed;
     string bodyData;
     
     TestRequest(const std::string& url) : 
         HTTP::Request(url),
         complete(false)
     {
-        
     }
     
+    std::map<string, string> sendHeaders;
+    std::map<string, string> headers;
 protected:
+    string_list requestHeaders() const
+    {
+        string_list r;
+        std::map<string, string>::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<string, string>::const_iterator it = sendHeaders.find(name);
+        if (it == sendHeaders.end()) {
+            return string();
+        }
+        
+        return it->second;
+    }
+    
     virtual void responseHeadersComplete()
     {
     }
@@ -62,10 +85,20 @@ protected:
         complete = true;
     }  
     
+    virtual void failure()
+    {
+        failed = true;
+    }
+    
     virtual void gotBodyData(const char* s, int n)
     {
         bodyData += string(s, n);
     }
+    
+    virtual void responseHeader(const string& header, const string& value)
+    {
+        headers[header] =  value;
+    }
 };
 
 class TestServerChannel : public NetChat
@@ -94,7 +127,7 @@ public:
         if (state == STATE_IDLE) {
             state = STATE_HEADERS;
             string_list line = strutils::split(buffer, NULL, 3);
-            if (line.size() < 4) {
+            if (line.size() < 3) {
                 cerr << "malformed request:" << buffer << endl;
                 exit(-1);
             }
@@ -102,7 +135,6 @@ public:
             method = line[0];
             path = line[1];
             httpVersion = line[2];
-            userAgent = line[3];
             requestHeaders.clear();
             buffer.clear();
         } else if (state == STATE_HEADERS) {
@@ -121,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) {
             
@@ -133,60 +164,110 @@ public:
     void receivedRequestHeaders()
     {
         state = STATE_IDLE;
+        
         if (path == "/test1") {
             string contentStr(BODY1);
             stringstream d;
-            d << "HTTP1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
+            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";
             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 == "/test2") {
             sendBody2();
+        } else if (path == "/testchunked") {
+            stringstream d;
+            d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
+            d << "Transfer-Encoding:chunked\r\n";
+            d << "\r\n";
+            d << "8\r\n"; // first chunk
+            d << "ABCDEFGH\r\n";
+            d << "6\r\n"; // second chunk
+            d << "ABCDEF\r\n";
+            d << "10\r\n"; // third chunk
+            d << "ABCDSTUVABCDSTUV\r\n";
+            d << "0\r\n"; // start of trailer
+            d << "X-Foobar: wibble\r\n"; // trailer data
+            d << "\r\n";
+            push(d.str().c_str());
         } else if (path == "http://www.google.com/test2") {
             // proxy test
-            if (requestHeaders["host"] != "www.google.com") {
-                sendErrorResponse(400);
+            if (requestHeaders["Host"] != "www.google.com") {
+                sendErrorResponse(400, true, "bad destination");
             }
             
-            if (requestHeaders["proxy-authorization"] != string()) {
-                sendErrorResponse(401); // shouldn't supply auth
+            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") {
-                sendErrorResponse(400);
+            if (requestHeaders["Host"] != "www.google.com") {
+                sendErrorResponse(400, true, "bad destination");
             }
 
-            if (requestHeaders["proxy-authorization"] != "ABCDEF") {
-                sendErrorResponse(401); // forbidden
+            if (requestHeaders["Proxy-Authorization"] != "ABCDEF") {
+                sendErrorResponse(401, false, "bad auth"); // forbidden
             }
 
             sendBody2();
+        } else if (strutils::starts_with(path, "/test_1_0")) {
+            string contentStr(BODY1);
+            stringstream d;
+            d << "HTTP/1.0 " << 200 << " " << reasonForCode(200) << "\r\n";
+            d << "\r\n"; // final CRLF to terminate the headers
+            d << contentStr;
+            push(d.str().c_str());
+            closeWhenDone();
+        } else if (path == "/test_close") {
+            string contentStr(BODY1);
+            stringstream d;
+            d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
+            d << "Connection: close\r\n";
+            d << "\r\n"; // final CRLF to terminate the headers
+            d << contentStr;
+            push(d.str().c_str());
+            closeWhenDone();
         } else {
-            sendErrorResponse(404);
+            sendErrorResponse(404, true, "");
         }
     }
     
     void sendBody2()
     {
         stringstream d;
-        d << "HTTP1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
+        d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
         d << "Content-Length:" << body2Size << "\r\n";
         d << "\r\n"; // final CRLF to terminate the headers
         push(d.str().c_str());
         bufferSend(body2, body2Size);
     }
     
-    void sendErrorResponse(int code)
+    void sendErrorResponse(int code, bool close, string content)
     {
         cerr << "sending error " << code << " for " << path << endl;
         stringstream headerData;
-        headerData << "HTTP1.1 " << code << " " << reasonForCode(code) << "\r\n";
+        headerData << "HTTP/1.1 " << code << " " << reasonForCode(code) << "\r\n";
+        headerData << "Content-Length:" << content.size() << "\r\n";
         headerData << "\r\n"; // final CRLF to terminate the headers
         push(headerData.str().c_str());
+        push(content.c_str());
+        
+        if (close) {
+            closeWhenDone();
+        }
     }
     
     string reasonForCode(int code) 
@@ -203,7 +284,6 @@ public:
     string method;
     string path;
     string httpVersion;
-    string userAgent;
     std::map<string, string> requestHeaders;
 };
 
@@ -227,25 +307,40 @@ public:
     {
         simgear::IPAddress addr ;
         int handle = accept ( &addr ) ;
-
+        //cout << "did accept from " << addr.getHost() << ":" << addr.getPort() << endl;
         TestServerChannel* chan = new TestServerChannel();
         chan->setHandle(handle);
     }
 };
 
-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(HTTP::Client* cl, TestRequest* tr)
+{
+    SGTimeStamp start(SGTimeStamp::now());
+    while (start.elapsedMSec() <  1000) {
+        cl->update();
+        if (tr->failed) {
+            return;
+        }
+        sleepForMSec(1);
+    }
+    
+    cerr << "timed out waiting for failure" << endl;
+}
+
 int main(int argc, char* argv[])
 {
     TestServer s;
@@ -273,13 +368,29 @@ 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->contentLength(), strlen(BODY1));
+        COMPARE(tr->responseLength(), strlen(BODY1));
+        COMPARE(tr->responseBytesReceived(), strlen(BODY1));
         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<body2Size; ++i) {
         body2[i] = (i << 4) | (i >> 2);
@@ -289,46 +400,143 @@ 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->contentLength(), body2Size);
+        COMPARE(tr->responseBytesReceived(), body2Size);
         COMPARE(tr->bodyData, string(body2, body2Size));
     }
     
+    {
+        TestRequest* tr = new TestRequest("http://localhost:2000/testchunked");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+
+        waitForComplete(&cl, tr);
+        COMPARE(tr->responseCode(), 200);
+        COMPARE(tr->responseReason(), string("OK"));
+        COMPARE(tr->responseBytesReceived(), 30);
+        COMPARE(tr->bodyData, "ABCDEFGHABCDEFABCDSTUVABCDSTUV");
+    // check trailers made it too
+        COMPARE(tr->headers["x-foobar"], string("wibble"));
+    }
+    
 // test 404
     {
         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->contentLength(), 0);
+        COMPARE(tr->responseLength(), 0);
     }
-    
+
+    cout << "done1" << endl;
+// test HTTP/1.0
+    {
+        TestRequest* tr = new TestRequest("http://localhost:2000/test_1_0");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+        waitForComplete(&cl, tr);
+        COMPARE(tr->responseCode(), 200);
+        COMPARE(tr->responseLength(), strlen(BODY1));
+        COMPARE(tr->bodyData, string(BODY1));
+    }
+
+    cout << "done2" << endl;
+// test HTTP/1.1 Connection::close
+    {
+        TestRequest* tr = new TestRequest("http://localhost:2000/test_close");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+        waitForComplete(&cl, tr);
+        COMPARE(tr->responseCode(), 200);
+        COMPARE(tr->responseLength(), strlen(BODY1));
+        COMPARE(tr->bodyData, string(BODY1));
+    }
+    cout << "done3" << endl;
+// test connectToHost failure
+/*
+    {
+        TestRequest* tr = new TestRequest("http://not.found/something");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+        waitForFailed(tr);
+        COMPARE(tr->responseCode(), -1);
+    }
+    */
 // test proxy
     {
-        cl.setProxy("localhost:2000");
+        cl.setProxy("localhost", 2000);
         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->contentLength(), body2Size);
+        COMPARE(tr->responseLength(), body2Size);
         COMPARE(tr->bodyData, string(body2, body2Size));
     }
     
     {
-        cl.setProxy("localhost:2000", "ABCDEF");
+        cl.setProxy("localhost", 2000, "ABCDEF");
         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->contentLength(), body2Size);
+        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;
 }