]> git.mxchange.org Git - simgear.git/blobdiff - simgear/io/test_HTTP.cxx
hla: Use HLADataElementIndices for HLAInteractionClass.
[simgear.git] / simgear / io / test_HTTP.cxx
index d0c771213902f58272fe7b04cb4d0d9c48173aa9..43d660c3a7536bebd2f39f3b53338c320976a01b 100644 (file)
@@ -22,6 +22,14 @@ using std::stringstream;
 using namespace simgear;
 
 const char* BODY1 = "The quick brown fox jumps over a lazy dog.";
+const char* BODY3 = "Cras ut neque nulla. Duis ut velit neque, sit amet "
+"pharetra risus. In est ligula, lacinia vitae congue in, sollicitudin at "
+"libero. Mauris pharetra pretium elit, nec placerat dui semper et. Maecenas "
+"magna magna, placerat sed luctus ac, commodo et ligula. Mauris at purus et "
+"nisl molestie auctor placerat at quam. Donec sapien magna, venenatis sed "
+"iaculis id, fringilla vel arcu. Duis sed neque nisi. Cras a arcu sit amet "
+"risus ultrices varius. Integer sagittis euismod dui id varius. Cras vel "
+"justo gravida metus.";
 
 const unsigned int body2Size = 8 * 1024;
 char body2[body2Size];
@@ -29,7 +37,7 @@ char body2[body2Size];
 #define COMPARE(a, b) \
     if ((a) != (b))  { \
         cerr << "failed:" << #a << " != " << #b << endl; \
-        cerr << "\tgot:" << a << endl; \
+        cerr << "\tgot:'" << a << "'" << endl; \
         exit(1); \
     }
 
@@ -46,15 +54,35 @@ 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)
     {
-        
     }
     
+    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()
     {
     }
@@ -71,6 +99,7 @@ protected:
     
     virtual void gotBodyData(const char* s, int n)
     {
+      //std::cout << "got body data:'" << string(s, n) << "'" <<std::endl;
         bodyData += string(s, n);
     }
     
@@ -87,6 +116,7 @@ public:
     {
         STATE_IDLE = 0,
         STATE_HEADERS,
+        STATE_CLOSING,
         STATE_REQUEST_BODY
     };
     
@@ -94,6 +124,7 @@ public:
     {
         state = STATE_IDLE;
         setTerminator("\r\n");
+        
     }
     
     virtual void collectIncomingData(const char* s, int n)
@@ -106,15 +137,21 @@ 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);
             }
             
             method = line[0];
             path = line[1];
+            
+            string::size_type queryPos = path.find('?');
+            if (queryPos != string::npos) {
+                parseArgs(path.substr(queryPos + 1));
+                path = path.substr(0, queryPos);
+            }
+            
             httpVersion = line[2];
-            userAgent = line[3];
             requestHeaders.clear();
             buffer.clear();
         } else if (state == STATE_HEADERS) {
@@ -125,30 +162,76 @@ public:
                 return;
             }
             
-            int colonPos = buffer.find(':');
-            if (colonPos < 0) {
+            string::size_type colonPos = buffer.find(':');
+            if (colonPos == string::npos) {
                 cerr << "malformed HTTP response header:" << buffer << endl;
                 buffer.clear();
                 return;
             }
 
             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) {
-            
+            receivedBody();
+            setTerminator("\r\n");
+        } else if (state == STATE_CLOSING) {
+          // ignore!
         }
     }  
     
+    void parseArgs(const string& argData)
+    {
+        string_list argv = strutils::split(argData, "&");
+        for (unsigned int a=0; a<argv.size(); ++a) {
+            string::size_type eqPos = argv[a].find('=');
+            if (eqPos == string::npos) {
+                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()
     {
         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 == "/testLorem") {
+            string contentStr(BODY3);
+            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_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") {
+            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;
@@ -157,7 +240,7 @@ public:
             sendBody2();
         } else if (path == "/testchunked") {
             stringstream d;
-            d << "HTTP1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
+            d << "HTTP/1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
             d << "Transfer-Encoding:chunked\r\n";
             d << "\r\n";
             d << "8\r\n"; // first chunk
@@ -172,54 +255,132 @@ public:
             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);
+            if (strutils::ends_with(path, "/B")) {
+                contentStr = BODY3;
+            }
+            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());
+            closeAfterSending();
+        } 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());
+            closeAfterSending();
+        } 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 {
-            sendErrorResponse(404);
+            sendErrorResponse(404, false, "");
+        }
+    }
+    
+    void closeAfterSending()
+    {
+      state = STATE_CLOSING;
+      closeWhenDone();
+    }
+  
+    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;
         }
     }
     
     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) 
     {
         switch (code) {
             case 200: return "OK";
+            case 204: return "no content";
             case 404: return "not found";
             default: return "unknown code";
         }
@@ -230,8 +391,9 @@ public:
     string method;
     string path;
     string httpVersion;
-    string userAgent;
     std::map<string, string> requestHeaders;
+    std::map<string, string> args;
+    int requestContentLength;
 };
 
 class TestServer : public NetChannel
@@ -254,33 +416,35 @@ public:
     {
         simgear::IPAddress addr ;
         int handle = accept ( &addr ) ;
-        cout << "did accept from " << addr.getHost() << ":" << addr.getPort() << endl;
+        //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;
         }
+        SGTimeStamp::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;
         }
+        SGTimeStamp::sleepForMSec(1);
     }
     
     cerr << "timed out waiting for failure" << endl;
@@ -313,14 +477,52 @@ 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));
         COMPARE(tr->responseBytesReceived(), strlen(BODY1));
         COMPARE(tr->bodyData, string(BODY1));
     }
+    
+    {
+      TestRequest* tr = new TestRequest("http://localhost:2000/testLorem");
+      HTTP::Request_ptr own(tr);
+      cl.makeRequest(tr);
+      
+      waitForComplete(&cl, tr);
+      COMPARE(tr->responseCode(), 200);
+      COMPARE(tr->responseReason(), string("OK"));
+      COMPARE(tr->responseLength(), strlen(BODY3));
+      COMPARE(tr->responseBytesReceived(), strlen(BODY3));
+      COMPARE(tr->bodyData, string(BODY3));
+    }
+  
+    {
+        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);
+        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);
@@ -330,18 +532,19 @@ 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));
     }
     
+    cerr << "testing chunked" << endl;
     {
         TestRequest* tr = new TestRequest("http://localhost:2000/testchunked");
         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);
@@ -355,12 +558,46 @@ 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);
     }
 
+    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
+    {
+        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
 /*
     {
@@ -377,7 +614,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));
@@ -388,12 +625,96 @@ 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
+    cout << "testing HTTP 1.1 pipelineing" << endl;
+  
+    {
+        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/testLorem");
+        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->responseLength(), strlen(BODY3));
+        COMPARE(tr2->responseBytesReceived(), strlen(BODY3));
+        COMPARE(tr2->bodyData, string(BODY3));
+      
+        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->responseLength(), strlen(BODY1));
+        COMPARE(tr->responseBytesReceived(), strlen(BODY1));
+        COMPARE(tr->bodyData, string(BODY1));
+      
+        COMPARE(tr2->responseLength(), strlen(BODY3));
+        COMPARE(tr2->responseBytesReceived(), strlen(BODY3));
+        COMPARE(tr2->bodyData, string(BODY3));
+        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;
 }