]> git.mxchange.org Git - simgear.git/commitdiff
Merge commit 'refs/merge-requests/22' of git://gitorious.org/fg/simgear into merge...
authorJames Turner <zakalawe@mac.com>
Sun, 22 Apr 2012 19:21:45 +0000 (20:21 +0100)
committerJames Turner <zakalawe@mac.com>
Mon, 23 Apr 2012 11:21:50 +0000 (12:21 +0100)
simgear/io/HTTPClient.cxx
simgear/io/HTTPRequest.cxx
simgear/io/HTTPRequest.hxx
simgear/io/test_HTTP.cxx

index fb1b812faaeb4d91dcbb5c9a4e0b4f1cfa0c33e5..cf19589e7db29553e1da589505cc4c1cdc45e21e 100644 (file)
@@ -25,9 +25,6 @@ using std::string;
 using std::stringstream;
 using std::vector;
 
-//#include <iostream>
-//using namespace std;
-
 namespace simgear
 {
 
@@ -35,6 +32,7 @@ namespace HTTP
 {
 
 extern const int DEFAULT_HTTP_PORT = 80;
+const char* CONTENT_TYPE_URL_ENCODED = "application/x-www-form-urlencoded";
 
 class Connection : public NetChat
 {
@@ -103,15 +101,27 @@ public:
         state = STATE_SENT_REQUEST;
         bodyTransferSize = -1;
         chunkedTransfer = false;
+        noMessageBody = (r->method() == "HEAD");
         setTerminator("\r\n");
         
         stringstream headerData;
         string path = r->path();
+        string query = r->query();
+        string bodyData;
+        
         if (!client->proxyHost().empty()) {
-            path = r->url();
+            path = r->scheme() + "://" + r->host() + r->path();
         }
 
-        headerData << r->method() << " " << path << " HTTP/1.1\r\n";
+        if (r->method() == "POST") {
+            headerData << r->method() << " " << path << " HTTP/1.1\r\n";
+            bodyData = query.substr(1); // URL-encode, drop the leading '?'
+            headerData << "Content-Type:" << CONTENT_TYPE_URL_ENCODED << "\r\n";
+            headerData << "Content-Length:" << bodyData.size() << "\r\n";
+        } else {
+            headerData << r->method() << " " << path << query << " HTTP/1.1\r\n";
+        }
+        
         headerData << "Host: " << r->hostAndPort() << "\r\n";
         headerData << "User-Agent:" << client->userAgent() << "\r\n";
         if (!client->proxyAuth().empty()) {
@@ -123,9 +133,10 @@ public:
         }
 
         headerData << "\r\n"; // final CRLF to terminate the headers
-
-    // TODO - add request body support for PUT, etc operations
-
+        if (!bodyData.empty()) {
+            headerData << bodyData;
+        }
+        
         bool ok = push(headerData.str().c_str());
         if (!ok) {
             SG_LOG(SG_IO, SG_WARN, "HTTP writing to socket failed");
@@ -150,6 +161,10 @@ public:
             activeRequest->responseStart(buffer);
             state = STATE_GETTING_HEADERS;
             buffer.clear();
+            if (activeRequest->responseCode() == 204) {
+                noMessageBody = true;
+            }
+            
             break;
             
         case STATE_GETTING_HEADERS:
@@ -233,6 +248,11 @@ private:
             
             if (chunkedTransfer) {
                 state = STATE_GETTING_CHUNKED;
+            } else if (noMessageBody || (bodyTransferSize == 0)) {
+                // force the state to GETTING_BODY, to simplify logic in
+                // responseComplete and handleClose
+                state = STATE_GETTING_BODY;
+                responseComplete();
             } else {
                 setByteCount(bodyTransferSize); // may be -1, that's fine
                 state = STATE_GETTING_BODY;
@@ -327,7 +347,6 @@ private:
     {
         activeRequest->responseComplete();
         client->requestFinished(this);
-        //cout << "response complete: " << activeRequest->url() << endl;
         
         bool doClose = activeRequest->closeAfterComplete();
         activeRequest = NULL;
@@ -374,6 +393,7 @@ private:
     int bodyTransferSize;
     SGTimeStamp idleTime;
     bool chunkedTransfer;
+    bool noMessageBody;
     
     std::list<Request_ptr> queuedRequests;
 };
index 38651a3e9062090c9f22e2f90c2558e5a6099e83..84855a4c7d290c879fca3160b18e4a8744437346 100644 (file)
@@ -125,6 +125,20 @@ string Request::path() const
     return u.substr(hostEnd, query - hostEnd);
 }
 
+
+string Request::query() const
+{
+  string u(url());
+  int query = u.find('?');
+  if (query < 0) {
+    return "";  //no query string found
+  }
+  
+  return u.substr(query);   //includes question mark
+}
+
+
+
 string Request::host() const
 {
     string hp(hostAndPort());
index 0e346419d12632abd535f99782e370120d149467..8d8a227054a12ef030523d8db8b0a2cd8e11269e 100644 (file)
@@ -30,6 +30,7 @@ public:
     virtual std::string host() const;
     virtual std::string hostAndPort() const;
     virtual unsigned short port() const;
+    virtual std::string query() const;
     
     virtual string_list requestHeaders() const;
     virtual std::string header(const std::string& name) const;
index 988f8bbaa03e6392d043f83df3ac548aa379b1c4..fc2fe869bf91e7da3909a4af6fd8efbba18c43d3 100644 (file)
@@ -46,8 +46,8 @@ 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)
     {
     }
@@ -133,6 +133,13 @@ public:
             
             method = line[0];
             path = line[1];
+            
+            int queryPos = path.find('?'); 
+            if (queryPos != string::npos) {
+                parseArgs(path.substr(queryPos + 1));
+                path = path.substr(0, queryPos);
+            }
+            
             httpVersion = line[2];
             requestHeaders.clear();
             buffer.clear();
@@ -156,10 +163,28 @@ public:
             requestHeaders[key] = value;
             buffer.clear();
         } else if (state == STATE_REQUEST_BODY) {
-            
+            cerr << "done getting requst body";
+            receivedBody();
+            setTerminator("\r\n");
         }
     }  
     
+    void parseArgs(const string& argData)
+    {
+        string_list argv = strutils::split(argData, "&");
+        for (unsigned int a=0; a<argv.size(); ++a) {
+            int eqPos = argv[a].find('=');
+            if (eqPos < 0) {
+                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;
@@ -172,6 +197,14 @@ public:
             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"));
@@ -239,8 +272,53 @@ public:
             d << contentStr;
             push(d.str().c_str());
             closeWhenDone();
+        } 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, true, "");
+            sendErrorResponse(404, false, "");
+        }
+    }
+    
+    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;
         }
     }
     
@@ -273,6 +351,7 @@ public:
     {
         switch (code) {
             case 200: return "OK";
+            case 204: return "no content";
             case 404: return "not found";
             default: return "unknown code";
         }
@@ -284,6 +363,8 @@ public:
     string path;
     string httpVersion;
     std::map<string, string> requestHeaders;
+    std::map<string, string> args;
+    int requestContentLength;
 };
 
 class TestServer : public NetChannel
@@ -374,7 +455,17 @@ int main(int argc, char* argv[])
         COMPARE(tr->responseBytesReceived(), strlen(BODY1));
         COMPARE(tr->bodyData, string(BODY1));
     }
-
+    
+    {
+        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);
@@ -405,6 +496,7 @@ int main(int argc, char* argv[])
         COMPARE(tr->bodyData, string(body2, body2Size));
     }
     
+    cerr << "testing chunked" << endl;
     {
         TestRequest* tr = new TestRequest("http://localhost:2000/testchunked");
         HTTP::Request_ptr own(tr);
@@ -430,6 +522,16 @@ int main(int argc, char* argv[])
         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
     {
@@ -536,6 +638,29 @@ int main(int argc, char* argv[])
         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;
 }