]> git.mxchange.org Git - simgear.git/commitdiff
Tweaks to HTTP code, in preparation for using it for metar - especially, test code...
authorJames Turner <zakalawe@mac.com>
Sat, 30 Jul 2011 09:43:49 +0000 (10:43 +0100)
committerJames Turner <zakalawe@mac.com>
Sat, 30 Jul 2011 09:43:49 +0000 (10:43 +0100)
simgear/io/HTTPClient.cxx
simgear/io/HTTPClient.hxx
simgear/io/HTTPRequest.cxx
simgear/io/HTTPRequest.hxx
simgear/io/test_HTTP.cxx

index 4c06fba344018003b48db234851bb34d555fe9c0..8c023ab3b7a805f2fbdb5da474a355c0df2ea7f1 100644 (file)
@@ -11,6 +11,7 @@
 #include <simgear/misc/strutils.hxx>
 #include <simgear/compiler.h>
 #include <simgear/debug/logstream.hxx>
+#include <simgear/timing/timestamp.hxx>
 
 #if defined( HAVE_VERSION_H ) && HAVE_VERSION_H
 #include "version.h"
@@ -30,6 +31,7 @@ namespace simgear
 namespace HTTP
 {
 
+extern const int DEFAULT_HTTP_PORT = 80;
 
 class Connection : public NetChat
 {
@@ -41,18 +43,10 @@ public:
         setTerminator("\r\n");
     }
     
-    void connectToHost(const string& host)
+    void connectToHost(const string& host, short port)
     {
         open();
-        
-        int colonPos = host.find(':');
-        if (colonPos > 0) {
-            string h = host.substr(0, colonPos);
-            int port = strutils::to_int(host.substr(colonPos + 1));
-            connect(h.c_str(), port);
-        } else {
-            connect(host.c_str(), 80 /* default port */);
-        }
+        connect(host.c_str(), port);
     }
     
     void queueRequest(const Request_ptr& r)
@@ -73,13 +67,11 @@ public:
         stringstream headerData;
         string path = r->path();
         if (!client->proxyHost().empty()) {
-            path = "http://" + r->host() + path;
+            path = "http://" + r->hostAndPort() + path;
         }
 
-        int requestTime = 0;
         headerData << r->method() << " " << path << " HTTP/1.1 " << client->userAgent() << "\r\n";
-        headerData << "Host: " << r->host() << "\r\n";
-        headerData << "X-Time: " << requestTime << "\r\n";
+        headerData << "Host: " << r->hostAndPort() << "\r\n";
 
         if (!client->proxyAuth().empty()) {
             headerData << "Proxy-Authorization: " << client->proxyAuth() << "\r\n";
@@ -128,12 +120,22 @@ public:
                 Request_ptr next = queuedRequests.front();
                 queuedRequests.pop_front();
                 startRequest(next);
+            } else {
+                idleTime.stamp();
             }
             
             break;
         }
     }
     
+    bool hasIdleTimeout() const
+    {
+        if (state != STATE_IDLE) {
+            return false;
+        }
+        
+        return idleTime.elapsedMSec() > 1000 * 10; // ten seconds
+    }
 private:
     void processHeader()
     {
@@ -193,6 +195,7 @@ private:
     ConnectionState state;
     std::string buffer;
     int bodyTransferSize;
+    SGTimeStamp idleTime;
     
     std::list<Request_ptr> queuedRequests;
 };
@@ -202,16 +205,31 @@ Client::Client()
     setUserAgent("SimGear-" SG_STRINGIZE(SIMGEAR_VERSION));
 }
 
+void Client::update()
+{
+    ConnectionDict::iterator it = _connections.begin();
+    for (; it != _connections.end(); ) {
+        if (it->second->hasIdleTimeout()) {
+        // connection has been idle for a while, clean it up
+            ConnectionDict::iterator del = it++;
+            delete del->second;
+            _connections.erase(del);
+        } else {
+            ++it;
+        }
+    } // of connecion iteration
+}
+
 void Client::makeRequest(const Request_ptr& r)
 {
-    string host = r->host();
+    string host = r->hostAndPort();
     if (!_proxy.empty()) {
         host = _proxy;
     }
     
     if (_connections.find(host) == _connections.end()) {
         Connection* con = new Connection(this);
-        con->connectToHost(host);
+        con->connectToHost(r->host(), r->port());
         _connections[host] = con;
     }
     
index 0e04cdc9347b5d4b148c45ec8a059982d08341cd..7e7a69b70515c20d9eba9ae0a6bb0cf7c319bf91 100644 (file)
@@ -18,6 +18,8 @@ class Client
 public:
     Client();
     
+    void update();
+    
     void makeRequest(const Request_ptr& r);
     
     void setUserAgent(const std::string& ua);
@@ -41,7 +43,8 @@ private:
     std::string _proxyAuth;
     
 // connections by host
-    std::map<std::string, Connection*> _connections;
+    typedef std::map<std::string, Connection*> ConnectionDict;
+    ConnectionDict _connections;
 };
 
 } // of namespace HTTP
index e38f7aae101383456204ff6fe497a56f575c27d5..50d059e6e2ffcfbebc55a932342a204107f1dad6 100644 (file)
@@ -19,6 +19,8 @@ namespace simgear
 namespace HTTP
 {
 
+extern const int DEFAULT_HTTP_PORT;
+
 Request::Request(const string& url, const string method) :
     _method(method),
     _url(url)
@@ -31,6 +33,11 @@ Request::~Request()
     
 }
 
+void Request::setUrl(const string& url)
+{
+    _url = url;
+}
+
 string_list Request::requestHeaders() const
 {
     string_list r;
@@ -103,6 +110,28 @@ string Request::path() const
 }
 
 string Request::host() const
+{
+    string hp(hostAndPort());
+    int colonPos = hp.find(':');
+    if (colonPos >= 0) {
+        return hp.substr(0, colonPos); // trim off the colon and port
+    } else {
+        return hp; // no port specifier
+    }
+}
+
+unsigned short Request::port() const
+{
+    string hp(hostAndPort());
+    int colonPos = hp.find(':');
+    if (colonPos >= 0) {
+        return (unsigned short) strutils::to_int(hp.substr(colonPos + 1));
+    } else {
+        return DEFAULT_HTTP_PORT;
+    }
+}
+
+string Request::hostAndPort() const
 {
     string u(url());
     int schemeEnd = u.find("://");
@@ -118,14 +147,14 @@ string Request::host() const
     return u.substr(schemeEnd + 3, hostEnd - (schemeEnd + 3));
 }
 
-int Request::contentLength() const
+unsigned int Request::contentLength() const
 {
     HeaderDict::const_iterator it = _responseHeaders.find("content-length");
     if (it == _responseHeaders.end()) {
         return 0;
     }
     
-    return strutils::to_int(it->second);
+    return (unsigned int) strutils::to_int(it->second);
 }
 
 } // of namespace HTTP
index 4afb1c75d8b02b856120c84e16026652576b210e..7cf498cd58e9d501c571c77e5809281f5ca070ea 100644 (file)
@@ -18,6 +18,8 @@ class Request : public SGReferenced
 public:
     virtual ~Request();
     
+    virtual void setUrl(const std::string& url);
+    
     virtual std::string method() const
         { return _method; }
     virtual std::string url() const
@@ -25,7 +27,9 @@ public:
     
     virtual std::string scheme() const;
     virtual std::string path() const;
-    virtual std::string host() const; // host, including port
+    virtual std::string host() const;
+    virtual std::string hostAndPort() const;
+    virtual unsigned short port() const;
     
     virtual string_list requestHeaders() const;
     virtual std::string header(const std::string& name) const;
@@ -36,7 +40,7 @@ public:
     virtual std::string resposeReason() const
         { return _responseReason; }
         
-    virtual int contentLength() const;
+    virtual unsigned int contentLength() const;
 protected:
     friend class Connection;
     
index 3d536498f7de9e7cb62ab84338fcc9a5c6361223..1dce8d29e355f59451811a31e9c7ec5fcd7f7e8b 100644 (file)
@@ -23,7 +23,7 @@ using namespace simgear;
 
 const char* BODY1 = "The quick brown fox jumps over a lazy dog.";
 
-const int body2Size = 8 * 1024;
+const unsigned int body2Size = 8 * 1024;
 char body2[body2Size];
 
 #define COMPARE(a, b) \
@@ -142,18 +142,44 @@ public:
             d << contentStr;
             push(d.str().c_str());
         } else if (path == "/test2") {
-            stringstream d;
-            d << "HTTP1.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);
-            cout << "sent body2" << endl;
+            sendBody2();
+        } else if (path == "http://www.google.com/test2") {
+            // proxy test
+            if (requestHeaders["host"] != "www.google.com") {
+                sendErrorResponse(400);
+            }
+            
+            if (requestHeaders["proxy-authorization"] != string()) {
+                sendErrorResponse(401); // 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["proxy-authorization"] != "ABCDEF") {
+                sendErrorResponse(401); // forbidden
+            }
+
+            sendBody2();
         } else {
             sendErrorResponse(404);
         }
     }
     
+    void sendBody2()
+    {
+        stringstream d;
+        d << "HTTP1.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)
     {
         cerr << "sending error " << code << " for " << path << endl;
@@ -227,11 +253,20 @@ int main(int argc, char* argv[])
     HTTP::Client cl;
 
 // test URL parsing
-    TestRequest* tr1 = new TestRequest("http://localhost:2000/test1?foo=bar");
+    TestRequest* tr1 = new TestRequest("http://localhost.woo.zar:2000/test1?foo=bar");
     COMPARE(tr1->scheme(), "http");
-    COMPARE(tr1->host(), "localhost:2000");
+    COMPARE(tr1->hostAndPort(), "localhost.woo.zar:2000");
+    COMPARE(tr1->host(), "localhost.woo.zar");
+    COMPARE(tr1->port(), 2000);
     COMPARE(tr1->path(), "/test1");
     
+    TestRequest* tr2 = new TestRequest("http://192.168.1.1/test1/dir/thing/file.png");
+    COMPARE(tr2->scheme(), "http");
+    COMPARE(tr2->hostAndPort(), "192.168.1.1");
+    COMPARE(tr2->host(), "192.168.1.1");
+    COMPARE(tr2->port(), 80);
+    COMPARE(tr2->path(), "/test1/dir/thing/file.png");
+    
 // basic get request
     {
         TestRequest* tr = new TestRequest("http://localhost:2000/test1");
@@ -245,7 +280,7 @@ int main(int argc, char* argv[])
     }
 
 // larger get request
-    for (int i=0; i<body2Size; ++i) {
+    for (unsigned int i=0; i<body2Size; ++i) {
         body2[i] = (i << 4) | (i >> 2);
     }
     
@@ -269,6 +304,29 @@ int main(int argc, char* argv[])
         COMPARE(tr->contentLength(), 0);
     }
     
+// test proxy
+    {
+        cl.setProxy("localhost:2000");
+        TestRequest* tr = new TestRequest("http://www.google.com/test2");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+        waitForComplete(tr);
+        COMPARE(tr->responseCode(), 200);
+        COMPARE(tr->contentLength(), body2Size);
+        COMPARE(tr->bodyData, string(body2, body2Size));
+    }
+    
+    {
+        cl.setProxy("localhost:2000", "ABCDEF");
+        TestRequest* tr = new TestRequest("http://www.google.com/test3");
+        HTTP::Request_ptr own(tr);
+        cl.makeRequest(tr);
+        waitForComplete(tr);
+        COMPARE(tr->responseCode(), 200);
+        COMPARE(tr->contentLength(), body2Size);
+        COMPARE(tr->bodyData, string(body2, body2Size));
+    }
+    
     cout << "all tests passed ok" << endl;
     return EXIT_SUCCESS;
 }