OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
PPTConnection.cc
Go to the documentation of this file.
00001 // PPTConnection.cc
00002 
00003 // This file is part of bes, A C++ back-end server implementation framework
00004 // for the OPeNDAP Data Access Protocol.
00005 
00006 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research
00007 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
00008 //
00009 // This library is free software; you can redistribute it and/or
00010 // modify it under the terms of the GNU Lesser General Public
00011 // License as published by the Free Software Foundation; either
00012 // version 2.1 of the License, or (at your option) any later version.
00013 // 
00014 // This library is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 // Lesser General Public License for more details.
00018 // 
00019 // You should have received a copy of the GNU Lesser General Public
00020 // License along with this library; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022 //
00023 // You can contact University Corporation for Atmospheric Research at
00024 // 3080 Center Green Drive, Boulder, CO 80301
00025  
00026 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
00027 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
00028 //
00029 // Authors:
00030 //      pwest       Patrick West <pwest@ucar.edu>
00031 //      jgarcia     Jose Garcia <jgarcia@ucar.edu>
00032 
00033 #include <poll.h>
00034 
00035 #include <cerrno>
00036 #include <cstring>
00037 #include <iostream>
00038 #include <sstream>
00039 #include <iomanip>
00040 
00041 using std::cout ;
00042 using std::cerr ;
00043 using std::endl ;
00044 using std::flush ;
00045 using std::ostringstream ;
00046 using std::istringstream ;
00047 using std::hex ;
00048 using std::setw ;
00049 using std::setfill ;
00050 
00051 #include "PPTConnection.h"
00052 #include "PPTProtocol.h"
00053 #include "Socket.h"
00054 #include "BESDebug.h"
00055 #include "BESInternalError.h"
00056 
00057 PPTConnection::~PPTConnection()
00058 {
00059     if( _inBuff )
00060     {
00061         delete [] _inBuff ;
00062         _inBuff = 0 ;
00063     }
00064 }
00065 
00100 void
00101 PPTConnection::send( const string &buffer,
00102                      map<string,string> &extensions )
00103 {
00104     if( !buffer.empty() )
00105     {
00106         sendChunk( buffer, extensions ) ;
00107 
00108         // send the last chunk without the extensions
00109         map<string,string> no_extensions ;
00110         sendChunk( "", no_extensions ) ;
00111     }
00112     else
00113     {
00114         sendChunk( "", extensions ) ;
00115     }
00116 }
00117 
00120 void
00121 PPTConnection::sendExit()
00122 {
00123     map<string,string> extensions ;
00124     extensions["status"] = PPTProtocol::PPT_EXIT_NOW ;
00125     send( "", extensions ) ;
00126 }
00127 
00136 void
00137 PPTConnection::sendChunk( const string &buffer, map<string,string> &extensions )
00138 {
00139     ostringstream strm ;
00140     if( extensions.size() )
00141     {
00142         sendExtensions( extensions ) ;
00143     }
00144     strm << hex << setw( 7 ) << setfill( '0' ) << buffer.length() << "d" ;
00145     if( !buffer.empty() )
00146     {
00147         strm << buffer ;
00148     }
00149     string toSend = strm.str() ;
00150     send( toSend ) ;
00151 }
00152 
00157 void
00158 PPTConnection::sendExtensions( map<string,string> &extensions )
00159 {
00160     ostringstream strm ;
00161     if( extensions.size() )
00162     {
00163         ostringstream estrm ;
00164         map<string,string>::const_iterator i = extensions.begin() ;
00165         map<string,string>::const_iterator ie = extensions.end() ;
00166         for( ; i != ie; i++ )
00167         {
00168             estrm << (*i).first ;
00169             string value = (*i).second ;
00170             if( !value.empty() )
00171             {
00172                 estrm << "=" << value ;
00173             }
00174             estrm << ";" ;
00175         }
00176         string xstr = estrm.str() ;
00177         strm << hex << setw( 7 ) << setfill( '0' ) << xstr.length() << "x" << xstr ;
00178         string toSend = strm.str() ;
00179         send( toSend ) ;
00180     }
00181 }
00182 
00189 void
00190 PPTConnection::send( const string &buffer )
00191 {
00192     BESDEBUG( "ppt", "PPTConnection::send - sending " << buffer << endl ) ;
00193     _mySock->send( buffer, 0, buffer.length() ) ;
00194 #if 0
00195     // was calling fsync() which is not defined for sockets. There might be some
00196     // 'sync' operation needed in the future, but for now this is an empty call. removed
00197     // jhrg 5/5/11
00198     _mySock->sync() ;
00199 #endif
00200 }
00201 
00208 int
00209 PPTConnection::readBuffer( char *buffer, const unsigned int buffer_size )
00210 {
00211     return _mySock->receive( buffer, buffer_size ) ;
00212 }
00213 
00214 int
00215 PPTConnection::readChunkHeader( char *buffer, /*unsigned */ int buffer_size )
00216 {
00217     char *temp_buffer = buffer ;
00218     int totalBytesRead = 0 ;
00219     bool done = false ;
00220     while( !done )
00221     {
00222         int bytesRead = readBuffer( temp_buffer, buffer_size ) ;
00223         BESDEBUG( "ppt", "PPTConnection::readChunkHeader - read "
00224                          << bytesRead << " bytes" << endl ) ;
00225         if( bytesRead < 0 )
00226         {
00227             return bytesRead ;
00228         }
00229         if( bytesRead < buffer_size )
00230         {
00231             buffer_size = buffer_size - bytesRead ;
00232             temp_buffer = temp_buffer + bytesRead ;
00233             totalBytesRead += bytesRead ;
00234         }
00235         else
00236         {
00237             totalBytesRead += bytesRead ;
00238             done = true ;
00239         }
00240     }
00241     buffer[totalBytesRead] = '\0' ;
00242     return totalBytesRead ;
00243 }
00244 
00260 bool
00261 PPTConnection::receive( map<string,string> &extensions,
00262                         ostream *strm )
00263 {
00264     ostream *use_strm = _out ;
00265     if( strm )
00266         use_strm = strm ;
00267 
00268     // If the receive buffer has not yet been created, get the receive size
00269     // and create the buffer.
00270     BESDEBUG( "ppt", "PPTConnection::receive: buffer size = " << _inBuff_len
00271                      << endl ) ;
00272     if( !_inBuff )
00273     {
00274         _inBuff_len = _mySock->getRecvBufferSize() + 1 ;
00275         _inBuff = new char[_inBuff_len+1] ;
00276     }
00277 
00278     // The first buffer will contain the length of the chunk at the beginning.
00279     // read the first 8 bytes. The first 7 are the length and the next 1
00280     // if x then extensions follow, if d then data follows.
00281     int bytesRead = readChunkHeader( _inBuff, 8 ) ;
00282     BESDEBUG( "ppt", "Reading header, read "
00283                      << bytesRead << " bytes" << endl ) ;
00284     if( bytesRead != 8 )
00285     {
00286         string err = "Failed to read length and type of chunk" ;
00287         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00288     }
00289 
00290     char lenbuffer[8] ;
00291     lenbuffer[0] = _inBuff[0] ;
00292     lenbuffer[1] = _inBuff[1] ;
00293     lenbuffer[2] = _inBuff[2] ;
00294     lenbuffer[3] = _inBuff[3] ;
00295     lenbuffer[4] = _inBuff[4] ;
00296     lenbuffer[5] = _inBuff[5] ;
00297     lenbuffer[6] = _inBuff[6] ;
00298     lenbuffer[7] = '\0' ;
00299     istringstream lenstrm( lenbuffer ) ;
00300     unsigned long inlen = 0 ;
00301     lenstrm >> hex >> setw(7) >> inlen ;
00302     BESDEBUG( "ppt", "Reading header, chunk length = " << inlen << endl ) ;
00303     BESDEBUG( "ppt", "Reading header, chunk type = " << _inBuff[7] << endl ) ;
00304 
00305     if( _inBuff[7] == 'x' )
00306     {
00307         ostringstream xstrm ;
00308         receive( xstrm, inlen ) ;
00309         read_extensions( extensions, xstrm.str() ) ;
00310     }
00311     else if( _inBuff[7] == 'd' )
00312     {
00313         if( !inlen )
00314         {
00315             // we've received the last chunk, return true, there
00316             // is nothing more to read from the socket
00317             return true ;
00318         }
00319         receive( *use_strm, inlen ) ;
00320     }
00321     else
00322     {
00323         string err = (string)"type of data is " + _inBuff[7]
00324                      + ", should be x for extensions or d for data" ;
00325         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00326     }
00327 
00328     return false ;
00329 }
00330 
00340 void
00341 PPTConnection::receive( ostream &strm, const /* unsigned */ int len )
00342 {
00343     BESDEBUG( "ppt", "PPTConnect::receive - len = " << len << endl ) ;
00344     if( !_inBuff )
00345     {
00346         string err = "buffer has not been initialized" ;
00347         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00348     }
00349 
00350     /* unsigned */ int to_read = len ;
00351     if( len > _inBuff_len )
00352     {
00353         to_read = _inBuff_len ;
00354     }
00355     BESDEBUG( "ppt", "PPTConnect::receive - to_read = " << to_read << endl ) ;
00356 
00357     // read a buffer
00358     int bytesRead = readBuffer( _inBuff, to_read ) ;
00359     if( bytesRead <= 0 )
00360     {
00361         string err = "Failed to read data from socket" ;
00362         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00363     }
00364     BESDEBUG( "ppt", "PPTConnect::receive - bytesRead = "
00365                      << bytesRead << endl ) ;
00366 
00367     // write the buffer read to the stream
00368     _inBuff[bytesRead] = '\0' ;
00369     strm.write( _inBuff, bytesRead ) ;
00370 
00371     // if bytesRead is less than the chunk length, then we need to go get
00372     // some more. It doesn't matter what _inBuff_len is, because we need
00373     // len bytes to be read and we read bytesRead bytes.
00374     if( bytesRead < len )
00375     {
00376         BESDEBUG( "ppt", "PPTConnect::receive - remaining = "
00377                          << (len - bytesRead) << endl ) ;
00378         receive( strm, len - bytesRead ) ;
00379     }
00380 }
00381 
00392 void
00393 PPTConnection::read_extensions( map<string,string> &extensions, const string &xstr )
00394 {
00395     // extensions are in the form var[=val]; There is always a semicolon at the end
00396     // if there is no equal sign then there is no value.
00397 
00398     string var ;
00399     string val ;
00400     unsigned int index = 0 ;
00401     bool done = false ;
00402     while( !done )
00403     {
00404         string::size_type semi = xstr.find( ';', index ) ;
00405         if( semi == string::npos )
00406         {
00407             string err = "malformed extensions "
00408                          + xstr.substr( index, xstr.length() - index )
00409                          + ", missing semicolon" ;
00410             throw BESInternalError( err, __FILE__, __LINE__ ) ;
00411         }
00412         string::size_type eq = xstr.find( '=', index ) ;
00413         if( eq == string::npos || eq > semi )
00414         {
00415             // there is no value for this variable
00416             var = xstr.substr( index, semi-index ) ;
00417             extensions[var] = "" ;
00418         }
00419         else if( eq == semi-1 )
00420         {
00421             string err = "malformed extensions "
00422                          + xstr.substr( index, xstr.length() - index )
00423                          + ", missing value after =" ;
00424             throw BESInternalError( err, __FILE__, __LINE__ ) ;
00425         }
00426         else
00427         {
00428             var = xstr.substr( index, eq-index ) ;
00429             val = xstr.substr( eq+1, semi-eq-1 ) ;
00430             extensions[var] = val ;
00431         }
00432         index = semi+1 ;
00433         if( index >= xstr.length() )
00434         {
00435             done = true ;
00436         }
00437     }
00438 }
00439 
00450 int
00451 PPTConnection::readBufferNonBlocking( char *inBuff,
00452                                       const /* unsigned*/ int buffer_size )
00453 {
00454     struct pollfd p ;
00455     p.fd = getSocket()->getSocketDescriptor();
00456     p.events = POLLIN ;
00457     struct pollfd arr[1] ;
00458     arr[0] = p ;
00459 
00460     // Lets loop _timeout times with a delay block on poll of 1000 milliseconds
00461     // and see if there is any data.
00462     for( int j = 0; j < _timeout; j++ )
00463     {
00464         if( poll( arr, 1, 1000 ) < 0 )
00465         {
00466             string error( "poll error" ) ;
00467             const char* error_info = strerror( errno ) ;
00468             if( error_info )
00469                 error += " " + (string)error_info ;
00470             throw BESInternalError( error, __FILE__, __LINE__ ) ;
00471         }
00472         else
00473         {
00474             if (arr[0].revents==POLLIN)
00475             {
00476                 return readBuffer( inBuff, buffer_size ) ;
00477             }
00478             else
00479             {
00480                 cout << " " << j << flush ;
00481             }
00482         }
00483     }
00484     cout << endl ;
00485     return -1 ;
00486 }
00487 
00488 unsigned int
00489 PPTConnection::getRecvChunkSize()
00490 {
00491     return _mySock->getRecvBufferSize() - PPT_CHUNK_HEADER_SPACE ;
00492 }
00493 
00494 unsigned int    
00495 PPTConnection::getSendChunkSize()
00496 {
00497     return _mySock->getSendBufferSize() - PPT_CHUNK_HEADER_SPACE ;
00498 }
00499 
00506 void
00507 PPTConnection::dump( ostream &strm ) const
00508 {
00509     strm << BESIndent::LMarg << "PPTConnection::dump - ("
00510                              << (void *)this << ")" << endl ;
00511     BESIndent::Indent() ;
00512     Connection::dump( strm ) ;
00513     BESIndent::UnIndent() ;
00514 }
00515