]> git.donarmstrong.com Git - bamtools.git/blobdiff - src/api/internal/io/TcpSocket_p.cpp
Stablized HTTP access on all platforms. (issue #54, issue #11)
[bamtools.git] / src / api / internal / io / TcpSocket_p.cpp
index 4ff53a8a1ae9c55bdcaff89aa362530bb9cb94c2..d3909326dbd238823cb4bcaa9bf699cbbbc104c2 100644 (file)
@@ -2,7 +2,7 @@
 // TcpSocket_p.cpp (c) 2011 Derek Barnett
 // Marth Lab, Department of Biology, Boston College
 // ---------------------------------------------------------------------------
-// Last modified: 10 November 2011 (DB)
+// Last modified: 5 January 2012 (DB)
 // ---------------------------------------------------------------------------
 // Provides basic TCP I/O interface
 // ***************************************************************************
@@ -14,6 +14,7 @@ using namespace BamTools;
 using namespace BamTools::Internal;
 
 #include <algorithm>
+#include <climits>
 #include <sstream>
 #include <vector>
 using namespace std;
@@ -26,7 +27,7 @@ namespace BamTools {
 namespace Internal {
 
 // constants
-static const size_t DEFAULT_BUFFER_SIZE = 0x4000;
+static const size_t DEFAULT_BUFFER_SIZE = 0x10000;
 
 } // namespace Internal
 } // namespace BamTools
@@ -42,7 +43,7 @@ TcpSocket::TcpSocket(void)
     , m_engine(0)
     , m_cachedSocketDescriptor(-1)
     , m_readBuffer(DEFAULT_BUFFER_SIZE)
-    , m_error(TcpSocket::UnknownSocketError)
+    , m_error(TcpSocket::NoError)
     , m_state(TcpSocket::UnconnectedState)
 { }
 
@@ -69,16 +70,16 @@ bool TcpSocket::ConnectImpl(const HostInfo& hostInfo,
 {
     // skip if we're already connected
     if ( m_state == TcpSocket::ConnectedState ) {
-        m_error = TcpSocket::SocketResourceError; 
+        m_error = TcpSocket::SocketResourceError;
         m_errorString = "socket already connected";
         return false;
     }
 
     // reset socket state
     m_hostName   = hostInfo.HostName();
-    m_mode       = mode;    
+    m_mode       = mode;
     m_state      = TcpSocket::UnconnectedState;
-    m_error      = TcpSocket::UnknownSocketError;
+    m_error      = TcpSocket::NoError;
 //    m_localPort  = 0;
     m_remotePort = 0;
 //    m_localAddress.Clear();
@@ -154,7 +155,7 @@ bool TcpSocket::ConnectToHost(const string& hostName,
     // if host name was IP address ("x.x.x.x" or IPv6 format)
     // otherwise host name was 'plain-text' ("www.foo.bar")
     // we need to look up IP address(es)
-    if ( hostAddress.HasIPAddress() ) 
+    if ( hostAddress.HasIPAddress() )
         info.SetAddresses( vector<HostAddress>(1, hostAddress) );
     else
         info = HostInfo::Lookup(hostName, port);
@@ -243,82 +244,78 @@ int64_t TcpSocket::Read(char* data, const unsigned int numBytes) {
     }
 
     // fetch data from socket, return 0 for success, -1 for failure
-    // since this should be called in a loop, we'll pull the actual bytes on next iteration
-    return ( ReadFromSocket() ? 0 : -1 );
+    // since this should be called in a loop,
+    // we'll pull the actual bytes from the buffer on next iteration
+    const int64_t socketBytesRead = ReadFromSocket();
+    if ( socketBytesRead < 0 ) {
+        // TODO: set error string/state ?
+        return -1;
+    }
+
+    // we should have data now in buffer, try to fetch requested amount
+    // if nothing in buffer, we will return 0 bytes read (signals EOF reached)
+    const size_t numBytesRead = m_readBuffer.Read(data, numBytes);
+    return static_cast<int64_t>(numBytesRead);
 }
 
-bool TcpSocket::ReadFromSocket(void) {
+int64_t TcpSocket::ReadFromSocket(void) {
 
     // check for any socket engine errors
     if ( !m_engine->IsValid() ) {
         m_errorString = "TcpSocket::ReadFromSocket - socket disconnected";
         ResetSocketEngine();
-        return false;
+        return -1;
     }
 
     // wait for ready read
     bool timedOut;
-    bool isReadyRead = m_engine->WaitForRead(5000, &timedOut);
+    const bool isReadyRead = m_engine->WaitForRead(5000, &timedOut);
 
     // if not ready
     if ( !isReadyRead ) {
 
         // if we simply timed out
         if ( timedOut ) {
+            // TODO: get add'l error info from engine ?
             m_errorString = "TcpSocket::ReadFromSocket - timed out waiting for ready read";
-            // get error from engine ?
-            return false;
         }
 
-        // otherwise, there was an error
+        // otherwise, there was some other error
         else {
+            // TODO: get add'l error info from engine ?
             m_errorString = "TcpSocket::ReadFromSocket - encountered error while waiting for ready read";
-            // get error from engine ?
-            return false;
         }
-    }
 
-    // #########################################################################
-    // clean this up - smells funky, but it's a key step so it has to be right
-    // #########################################################################
+        // return failure
+        return -1;
+    }
 
     // get number of bytes available from socket
-    // (if 0, still try to read some data so we don't trigger any OS event behavior
-    //  that respond to repeated access to a remote closed socket)
-    int64_t bytesToRead = m_engine->NumBytesAvailable();
+    const int64_t bytesToRead = m_engine->NumBytesAvailable();
     if ( bytesToRead < 0 ) {
+        // TODO: get add'l error info from engine ?
         m_errorString = "TcpSocket::ReadFromSocket - encountered error while determining numBytesAvailable";
-        // get error from engine ?
-        return false;
+        return -1;
     }
-    else if ( bytesToRead == 0 )
-        bytesToRead = 4096;
 
     // make space in buffer & read from socket
     char* buffer = m_readBuffer.Reserve(bytesToRead);
-    int64_t numBytesRead = m_engine->Read(buffer, bytesToRead);
-
-    // if error while reading
+    const int64_t numBytesRead = m_engine->Read(buffer, bytesToRead);
     if ( numBytesRead == -1 ) {
+        // TODO: get add'l error info from engine ?
         m_errorString = "TcpSocket::ReadFromSocket - encountered error while reading bytes";
-        // get error from engine ?
-        return false;
     }
 
-    // handle special case (no data, but not error)
-    if ( numBytesRead == -2 ) 
-        m_readBuffer.Chop(bytesToRead);
-
-    // return success
-    return true;
+    // return number of bytes actually read
+    return numBytesRead;
 }
 
 string TcpSocket::ReadLine(int64_t max) {
 
     // prep result byte buffer
     ByteArray result;
-
-    size_t bufferMax = ((max > static_cast<int64_t>(string::npos)) ? string::npos : static_cast<size_t>(max));
+    size_t bufferMax = ((max > static_cast<int64_t>(UINT_MAX))
+                        ? UINT_MAX : static_cast<size_t>(max));
     result.Resize(bufferMax);
 
     // read data
@@ -326,13 +323,13 @@ string TcpSocket::ReadLine(int64_t max) {
     if ( result.Size() == 0 ) {
 
         if ( bufferMax == 0 )
-            bufferMax = string::npos;
+            bufferMax = UINT_MAX;
 
         result.Resize(1);
 
         int64_t readResult;
         do {
-            result.Resize( static_cast<size_t>(std::min(bufferMax, result.Size() + DEFAULT_BUFFER_SIZE)) );
+            result.Resize( static_cast<size_t>(min(bufferMax, result.Size() + DEFAULT_BUFFER_SIZE)) );
             readResult = ReadLine(result.Data()+readBytes, result.Size()-readBytes);
             if ( readResult > 0 || readBytes == 0 )
                 readBytes += readResult;
@@ -352,13 +349,13 @@ string TcpSocket::ReadLine(int64_t max) {
 }
 
 int64_t TcpSocket::ReadLine(char* dest, size_t max) {
-    
+
     // wait for buffer to contain line contents
     if ( !WaitForReadLine() ) {
         m_errorString = "TcpSocket::ReadLine - error waiting for read line";
         return -1;
     }
-    
+
     // leave room for null term
     if ( max < 2 )
         return -1;
@@ -398,11 +395,11 @@ bool TcpSocket::WaitForReadLine(void) {
 
     // wait until we can read a line (will return immediately if already capable)
     while ( !CanReadLine() ) {
-        if ( !ReadFromSocket() ) 
+        if ( !ReadFromSocket() )
             return false;
     }
 
-    // if we get here, success  
+    // if we get here, success
     return true;
 }
 
@@ -411,22 +408,23 @@ int64_t TcpSocket::Write(const char* data, const unsigned int numBytes) {
     // single-shot attempt at write (not buffered, just try to shove the data through socket)
     // this method purely exists to send 'small' HTTP requests/FTP commands from client to server
 
-    int64_t bytesWritten(0);
-
     // wait for our socket to be write-able
     bool timedOut;
-    bool isReadyWrite = m_engine->WaitForWrite(3000, &timedOut);
+    const bool isReadyWrite = m_engine->WaitForWrite(3000, &timedOut);
+
+    // if ready, return number of bytes written
     if ( isReadyWrite )
-        bytesWritten = m_engine->Write(data, numBytes);
+        return m_engine->Write(data, numBytes);
+
+    // otherwise, socket not ready for writing
+    // set error string depending on reason & return failure
+    if ( !timedOut ) {
+        // TODO: get add'l error info from engine ??
+        m_errorString = "TcpSocket::Write - timed out waiting for ready-write";
+    }
     else {
-        // timeout is OK (with current setup), we'll just return 0 & try again
-        // but we need to report if engine encountered some other error
-        if ( !timedOut ) {
-            // TODO: set error string
-            bytesWritten = -1;
-        }
+        // TODO: get add'l error info from engine ??
+        m_errorString = "TcpSocket::Write - error encountered while waiting for ready-write";
     }
-
-    // return actual number of bytes written to socket
-    return bytesWritten;
+    return -1;
 }