]> git.donarmstrong.com Git - bamtools.git/blobdiff - src/api/internal/io/BamHttp_p.cpp
merge with remoteio branch
[bamtools.git] / src / api / internal / io / BamHttp_p.cpp
index 04fb8e2aaaffddae40c739dbb3ee0cd556253772..e2ade70a1327f4ea90d4928ae28b6ec4e26f17a1 100644 (file)
@@ -2,7 +2,7 @@
 // BamHttp_p.cpp (c) 2011 Derek Barnett
 // Marth Lab, Department of Biology, Boston College
 // ---------------------------------------------------------------------------
-// Last modified: 7 November 2011 (DB)
+// Last modified: 10 November 2011 (DB)
 // ---------------------------------------------------------------------------
 // Provides reading/writing of BAM files on HTTP server
 // ***************************************************************************
@@ -30,13 +30,26 @@ namespace Internal {
 static const string HTTP_PORT   = "80";
 static const string HTTP_PREFIX = "http://";
 static const size_t HTTP_PREFIX_LENGTH = 7;
-static const char COLON_CHAR = ':';
-static const char SLASH_CHAR = '/';
+
+static const string DOUBLE_NEWLINE = "\n\n";
+
+static const string GET_METHOD   = "GET";
+static const string HOST_HEADER  = "Host";
+static const string RANGE_HEADER = "Range";
+static const string BYTES_PREFIX = "bytes=";
+
+static const char HOST_SEPARATOR  = '/';
+static const char PROXY_SEPARATOR = ':';
 
 // -----------------
 // utility methods
 // -----------------
 
+static inline
+bool endsWith(const string& source, const string& pattern) {
+    return ( source.find(pattern) == (source.length() - pattern.length()) );
+}
+
 static inline
 string toLower(const string& s) {
     string out;
@@ -161,6 +174,9 @@ bool BamHttp::Open(const IBamIODevice::OpenMode mode) {
 
 void BamHttp::ParseUrl(const string& url) {
 
+    // clear flag to start
+    m_isUrlParsed = false;
+
     // make sure url starts with "http://", case-insensitive
     string tempUrl(url);
     toLower(tempUrl);
@@ -169,14 +185,14 @@ void BamHttp::ParseUrl(const string& url) {
         return;
 
     // find end of host name portion (first '/' hit after the prefix)
-    const size_t firstSlashFound = tempUrl.find(SLASH_CHAR, HTTP_PREFIX_LENGTH);
+    const size_t firstSlashFound = tempUrl.find(HOST_SEPARATOR, HTTP_PREFIX_LENGTH);
     if ( firstSlashFound == string::npos ) {
         ;  // no slash found... no filename given along with host?
     }
 
     // fetch hostname (check for proxy port)
     string hostname = tempUrl.substr(HTTP_PREFIX_LENGTH, (firstSlashFound - HTTP_PREFIX_LENGTH));
-    const size_t colonFound = hostname.find(COLON_CHAR);
+    const size_t colonFound = hostname.find(PROXY_SEPARATOR);
     if ( colonFound != string::npos ) {
         ; // TODO: handle proxy port (later, just skip for now)
     } else {
@@ -209,13 +225,14 @@ int64_t BamHttp::Read(char* data, const unsigned int numBytes) {
 
         // if socket has access to entire file contents
         // i.e. we received response with full data (status code == 200)
-        if ( !m_endRangeFilePosition >= 0 ) {
+        if ( m_endRangeFilePosition < 0 ) {
 
             // try to read 'remainingBytes' from socket
             const int64_t socketBytesRead = ReadFromSocket(data+bytesReadSoFar, remainingBytes);
             if ( socketBytesRead < 0 )
                 return -1;
             bytesReadSoFar += socketBytesRead;
+            m_filePosition += socketBytesRead;
         }
 
         // socket has access to a range of data (might already be in buffer)
@@ -225,16 +242,19 @@ int64_t BamHttp::Read(char* data, const unsigned int numBytes) {
             // there is data left from last request
             if ( m_endRangeFilePosition > m_filePosition ) {
 
-                // try to read either the total 'remainingBytes' or whatever we have remaining from last request range
+                // try to read either the total 'remainingBytes' or
+                // whatever we have remaining from last request range
                 const size_t rangeRemainingBytes = m_endRangeFilePosition - m_filePosition;
                 const size_t bytesToRead = std::min(remainingBytes, rangeRemainingBytes);
                 const int64_t socketBytesRead = ReadFromSocket(data+bytesReadSoFar, bytesToRead);
                 if ( socketBytesRead < 0 )
                     return -1;
                 bytesReadSoFar += socketBytesRead;
+                m_filePosition += socketBytesRead;
             }
 
-            // otherwise, this is a 1st-time read OR we already read everything from the last GET request
+            // otherwise, this is a 1st-time read or
+            // we already read everything from the last GET request
             else {
 
                 // request for next range
@@ -256,7 +276,6 @@ int64_t BamHttp::ReadFromSocket(char* data, const unsigned int maxNumBytes) {
     const int64_t numBytesRead = m_socket->Read(data, maxNumBytes);
     if ( numBytesRead < 0 )
         return -1;
-    m_filePosition += numBytesRead;
     return numBytesRead;
 }
 
@@ -270,21 +289,16 @@ bool BamHttp::ReceiveResponse(void) {
     if ( !EnsureSocketConnection() )
         return false;
 
-    // read response header from socket
-    RaiiBuffer header(0x10000);
-    size_t l = 0;
-    while ( m_socket->Read(header.Buffer + l, 1) >= 0 ) {
-        if ( header.Buffer[l] == '\n' && l >= 3 ) {
-            if (strncmp(header.Buffer + l - 3, "\r\n\r\n", 4) == 0)
-                break;
-        }
-        ++l;
-    }
+    // fetch header, up until double new line
     string responseHeader;
-    responseHeader.resize(l+1);
-    for ( size_t i = 0; i < l; ++i )
-        responseHeader[i] = header.Buffer[i];
+    do {
+        // read line & append to full header
+        const string headerLine = m_socket->ReadLine();
+        responseHeader += headerLine;
 
+    } while ( !endsWith(responseHeader, DOUBLE_NEWLINE) );
+
+    // sanity check
     if ( responseHeader.empty() ) {
         // TODO: set error string
         Close();
@@ -328,7 +342,7 @@ bool BamHttp::ReceiveResponse(void) {
     return false;
 }
 
-bool BamHttp::Seek(const int64_t& position) {
+bool BamHttp::Seek(const int64_t& position, const int origin) {
 
     // if HTTP device not in a valid state
     if ( !IsOpen() ) {
@@ -338,8 +352,16 @@ bool BamHttp::Seek(const int64_t& position) {
 
     // discard socket's buffer contents, update positions, & return success
     m_socket->ClearBuffer();
-    m_filePosition = position;
-    m_endRangeFilePosition = -1;
+
+    if ( origin == SEEK_CUR )
+        m_filePosition += position;
+    else if ( origin == SEEK_SET )
+        m_filePosition = position;
+    else {
+        // TODO: set error string
+        return false;
+    }
+    m_endRangeFilePosition = m_filePosition;
     return true;
 }
 
@@ -351,17 +373,17 @@ bool BamHttp::SendRequest(const size_t numBytes) {
 
     // create range string
     m_endRangeFilePosition = m_filePosition + numBytes;
-    stringstream range("bytes=");
-    range << m_filePosition << "-" << m_endRangeFilePosition;
+    stringstream range("");
+    range << BYTES_PREFIX << m_filePosition << '-' << m_endRangeFilePosition;
 
     // make sure we're connected
     if ( !EnsureSocketConnection() )
         return false;
 
     // create request
-    m_request = new HttpRequestHeader("GET", m_filename);
-    m_request->SetField("Host",  m_hostname);
-    m_request->SetField("Range", range.str());
+    m_request = new HttpRequestHeader(GET_METHOD, m_filename);
+    m_request->SetField(HOST_HEADER,  m_hostname);
+    m_request->SetField(RANGE_HEADER, range.str());
 
     // write request to socket
     const string requestHeader = m_request->ToString();
@@ -377,12 +399,13 @@ int64_t BamHttp::Write(const char* data, const unsigned int numBytes) {
     (void)data;
     (void)numBytes;
     BT_ASSERT_X(false, "BamHttp::Write : write-mode not supported on this device");
-    return 0;
+    SetErrorString("BamHttp::Write", "write-mode not supported on this device");
+    return -1;
 }
 
 int64_t BamHttp::WriteToSocket(const char* data, const unsigned int numBytes) {
-    if ( !EnsureSocketConnection() )
-        return false;
+    if ( !m_socket->IsConnected() )
+        return -1;
     m_socket->ClearBuffer();
     return m_socket->Write(data, numBytes);
 }