]> git.mxchange.org Git - simgear.git/blobdiff - simgear/io/HTTPRequest.cxx
Don't use object returned from vector::end()
[simgear.git] / simgear / io / HTTPRequest.cxx
index 6b2a8290a38e36c790d0cdfc64ed8e6612c1bfd2..6b06785ad00bf57d79fb12e77b971a9475362138 100644 (file)
@@ -1,9 +1,27 @@
+// Copyright (C) 2011  James Turner <zakalawe@mac.com>
+// Copyright (C) 2013  Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
+
 #include "HTTPRequest.hxx"
 
 #include <simgear/compiler.h>
 #include <simgear/debug/logstream.hxx>
 #include <simgear/misc/strutils.hxx>
 #include <simgear/props/props_io.hxx>
+#include <simgear/structure/exception.hxx>
 
 namespace simgear
 {
@@ -14,6 +32,7 @@ extern const int DEFAULT_HTTP_PORT;
 
 //------------------------------------------------------------------------------
 Request::Request(const std::string& url, const std::string method):
+  _client(0),
   _method(method),
   _url(url),
   _responseVersion(HTTP_VERSION_UNKNOWN),
@@ -21,7 +40,8 @@ Request::Request(const std::string& url, const std::string method):
   _responseLength(0),
   _receivedBodyBytes(0),
   _ready_state(UNSENT),
-  _willClose(false)
+  _willClose(false),
+  _connectionCloseHeader(false)
 {
 
 }
@@ -38,7 +58,7 @@ Request* Request::done(const Callback& cb)
   if( _ready_state == DONE )
     cb(this);
   else
-    _cb_done = cb;
+    _cb_done.push_back(cb);
 
   return this;
 }
@@ -49,7 +69,7 @@ Request* Request::fail(const Callback& cb)
   if( _ready_state == FAILED )
     cb(this);
   else
-    _cb_fail = cb;
+    _cb_fail.push_back(cb);
 
   return this;
 }
@@ -60,7 +80,7 @@ Request* Request::always(const Callback& cb)
   if( isComplete() )
     cb(this);
   else
-    _cb_always = cb;
+    _cb_always.push_back(cb);
 
   return this;
 }
@@ -115,21 +135,28 @@ void Request::responseStart(const std::string& r)
     const int maxSplit = 2; // HTTP/1.1 nnn reason-string
     string_list parts = strutils::split(r, NULL, maxSplit);
     if (parts.size() != 3) {
-        SG_LOG(SG_IO, SG_WARN, "HTTP::Request: malformed response start:" << r);
-        setFailure(400, "malformed HTTP response header");
-        return;
+        throw sg_io_exception("bad HTTP response:" + r);
     }
-    
+
     _responseVersion = decodeHTTPVersion(parts[0]);
     _responseStatus = strutils::to_int(parts[1]);
     _responseReason = parts[2];
+
+    setReadyState(STATUS_RECEIVED);
 }
 
 //------------------------------------------------------------------------------
 void Request::responseHeader(const std::string& key, const std::string& value)
 {
-  if( key == "connection" )
-    _willClose = (value.find("close") != std::string::npos);
+  if( key == "connection" ) {
+    _connectionCloseHeader = (value.find("close") != std::string::npos);
+      // track willClose seperately because other conditions (abort, for
+      // example) can also set it
+    _willClose = _connectionCloseHeader;
+  } else if (key == "content-length") {
+    int sz = strutils::to_int(value);
+    setResponseLength(sz);
+  }
 
   _responseHeaders[key] = value;
 }
@@ -191,7 +218,7 @@ std::string Request::scheme() const
     if (firstColon > 0) {
         return url().substr(0, firstColon);
     }
-    
+
     return ""; // couldn't parse scheme
 }
 
@@ -203,20 +230,20 @@ std::string Request::path() const
     if (schemeEnd < 0) {
         return ""; // couldn't parse scheme
     }
-    
+
     int hostEnd = u.find('/', schemeEnd + 3);
     if (hostEnd < 0) {
-// couldn't parse host, or URL looks like 'http://foo.com' (no trailing '/') 
-// fixup to root resource path: '/' 
-        return "/"; 
+// couldn't parse host, or URL looks like 'http://foo.com' (no trailing '/')
+// fixup to root resource path: '/'
+        return "/";
     }
-    
+
     int query = u.find('?', hostEnd + 1);
     if (query < 0) {
         // all remainder of URL is path
         return u.substr(hostEnd);
     }
-    
+
     return u.substr(hostEnd, query - hostEnd);
 }
 
@@ -228,7 +255,7 @@ std::string Request::query() const
   if (query < 0) {
     return "";  //no query string found
   }
-  
+
   return u.substr(query);   //includes question mark
 }
 
@@ -273,6 +300,16 @@ std::string Request::hostAndPort() const
   return u.substr(schemeEnd + 3, hostEnd - (schemeEnd + 3));
 }
 
+//------------------------------------------------------------------------------
+std::string Request::responseMime() const
+{
+  std::string content_type = _responseHeaders.get("content-type");
+  if( content_type.empty() )
+    return "application/octet-stream";
+
+  return content_type.substr(0, content_type.find(';'));
+}
+
 //------------------------------------------------------------------------------
 void Request::setResponseLength(unsigned int l)
 {
@@ -293,9 +330,12 @@ unsigned int Request::responseLength() const
 //------------------------------------------------------------------------------
 void Request::setFailure(int code, const std::string& reason)
 {
+    SG_LOG(SG_IO, SG_WARN, "HTTP request: set failure:" << code << " reason " << reason);
   _responseStatus = code;
   _responseReason = reason;
-  setReadyState(FAILED);
+
+  if( !isComplete() )
+    setReadyState(FAILED);
 }
 
 //------------------------------------------------------------------------------
@@ -304,45 +344,45 @@ void Request::setReadyState(ReadyState state)
   _ready_state = state;
   if( state == DONE )
   {
-    if( _cb_done )
-      _cb_done(this);
+    // Finish C++ part of request to ensure everything is finished (for example
+    // files and streams are closed) before calling any callback (possibly using
+    // such files)
     onDone();
+    onAlways();
+
+    _cb_done(this);
   }
   else if( state == FAILED )
   {
-    if( _cb_fail )
-      _cb_fail(this);
     onFail();
+    onAlways();
+
+    _cb_fail(this);
   }
   else
     return;
 
-  if( _cb_always )
-    _cb_always(this);
-  onAlways();
+  _cb_always(this);
 }
 
 //------------------------------------------------------------------------------
-void Request::abort()
+bool Request::closeAfterComplete() const
 {
-  abort("Request aborted.");
+  // for non HTTP/1.1 connections, assume server closes
+  return _willClose || (_responseVersion != HTTP_1_1);
 }
 
-//----------------------------------------------------------------------------
-void Request::abort(const std::string& reason)
-{
-  if( isComplete() )
-    return;
+//------------------------------------------------------------------------------
 
-  setFailure(-1, reason);
-  _willClose = true;
+void Request::setCloseAfterComplete()
+{
+    _willClose = true;
 }
 
 //------------------------------------------------------------------------------
-bool Request::closeAfterComplete() const
+bool Request::serverSupportsPipelining() const
 {
-  // for non HTTP/1.1 connections, assume server closes
-  return _willClose || (_responseVersion != HTTP_1_1);
+    return (_responseVersion == HTTP_1_1) && !_connectionCloseHeader;
 }
 
 //------------------------------------------------------------------------------