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     _mySock->sync() ;
00195 }
00196 
00203 int
00204 PPTConnection::readBuffer( char *buffer, unsigned int buffer_size )
00205 {
00206     return _mySock->receive( buffer, buffer_size ) ;
00207 }
00208 
00209 int
00210 PPTConnection::readChunkHeader( char *buffer, unsigned int buffer_size )
00211 {
00212     char *temp_buffer = buffer ;
00213     int totalBytesRead = 0 ;
00214     bool done = false ;
00215     while( !done )
00216     {
00217         int bytesRead = readBuffer( temp_buffer, buffer_size ) ;
00218         BESDEBUG( "ppt", "PPTConnection::readChunkHeader - read "
00219                          << bytesRead << " bytes" << endl )
00220         if( bytesRead < 0 )
00221         {
00222             return bytesRead ;
00223         }
00224         if( bytesRead < buffer_size )
00225         {
00226             buffer_size = buffer_size - bytesRead ;
00227             temp_buffer = temp_buffer + bytesRead ;
00228             totalBytesRead += bytesRead ;
00229         }
00230         else
00231         {
00232             totalBytesRead += bytesRead ;
00233             done = true ;
00234         }
00235     }
00236     buffer[totalBytesRead] = '\0' ;
00237     return totalBytesRead ;
00238 }
00239 
00255 bool
00256 PPTConnection::receive( map<string,string> &extensions,
00257                         ostream *strm )
00258 {
00259     ostream *use_strm = _out ;
00260     if( strm )
00261         use_strm = strm ;
00262 
00263     // If the receive buffer has not yet been created, get the receive size
00264     // and create the buffer.
00265     BESDEBUG( "ppt", "PPTConnection::receive: buffer size = " << _inBuff_len
00266               << endl )
00267     if( !_inBuff )
00268     {
00269         _inBuff_len = _mySock->getRecvBufferSize() + 1 ;
00270         _inBuff = new char[_inBuff_len+1] ;
00271     }
00272 
00273     // The first buffer will contain the length of the chunk at the beginning.
00274     // read the first 8 bytes. The first 7 are the length and the next 1
00275     // if x then extensions follow, if d then data follows.
00276     int bytesRead = readChunkHeader( _inBuff, 8 ) ;
00277     BESDEBUG( "ppt", "Reading header, read " << bytesRead << " bytes" << endl )
00278     if( bytesRead != 8 )
00279     {
00280         string err = "Failed to read length and type of chunk" ;
00281         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00282     }
00283 
00284     char lenbuffer[8] ;
00285     lenbuffer[0] = _inBuff[0] ;
00286     lenbuffer[1] = _inBuff[1] ;
00287     lenbuffer[2] = _inBuff[2] ;
00288     lenbuffer[3] = _inBuff[3] ;
00289     lenbuffer[4] = _inBuff[4] ;
00290     lenbuffer[5] = _inBuff[5] ;
00291     lenbuffer[6] = _inBuff[6] ;
00292     lenbuffer[7] = '\0' ;
00293     istringstream lenstrm( lenbuffer ) ;
00294     unsigned long inlen = 0 ;
00295     lenstrm >> hex >> setw(7) >> inlen ;
00296     BESDEBUG( "ppt", "Reading header, chunk length = " << inlen << endl )
00297     BESDEBUG( "ppt", "Reading header, chunk type = " << _inBuff[7] << endl )
00298 
00299     if( _inBuff[7] == 'x' )
00300     {
00301         ostringstream xstrm ;
00302         receive( xstrm, inlen ) ;
00303         read_extensions( extensions, xstrm.str() ) ;
00304     }
00305     else if( _inBuff[7] == 'd' )
00306     {
00307         if( !inlen )
00308         {
00309             // we've received the last chunk, return true, there
00310             // is nothing more to read from the socket
00311             return true ;
00312         }
00313         receive( *use_strm, inlen ) ;
00314     }
00315     else
00316     {
00317         string err = (string)"type of data is " + _inBuff[7]
00318                      + ", should be x for extensions or d for data" ;
00319         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00320     }
00321 
00322     return false ;
00323 }
00324 
00334 void
00335 PPTConnection::receive( ostream &strm, unsigned int len )
00336 {
00337     BESDEBUG( "ppt", "PPTConnect::receive - len = " << len << endl )
00338     if( !_inBuff )
00339     {
00340         string err = "buffer has not been initialized" ;
00341         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00342     }
00343 
00344     unsigned int to_read = len ;
00345     if( len > _inBuff_len )
00346     {
00347         to_read = _inBuff_len ;
00348     }
00349     BESDEBUG( "ppt", "PPTConnect::receive - to_read = " << to_read << endl )
00350 
00351     // read a buffer
00352     int bytesRead = readBuffer( _inBuff, to_read ) ;
00353     if( bytesRead <= 0 )
00354     {
00355         string err = "Failed to read data from socket" ;
00356         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00357     }
00358     BESDEBUG( "ppt", "PPTConnect::receive - bytesRead = " << bytesRead << endl )
00359 
00360     // write the buffer read to the stream
00361     _inBuff[bytesRead] = '\0' ;
00362     strm.write( _inBuff, bytesRead ) ;
00363 
00364     // if bytesRead is less than the chunk length, then we need to go get
00365     // some more. It doesn't matter what _inBuff_len is, because we need
00366     // len bytes to be read and we read bytesRead bytes.
00367     if( bytesRead < len )
00368     {
00369         BESDEBUG( "ppt", "PPTConnect::receive - remaining = "
00370                          << (len - bytesRead) << endl )
00371         receive( strm, len - bytesRead ) ;
00372     }
00373 }
00374 
00385 void
00386 PPTConnection::read_extensions( map<string,string> &extensions, const string &xstr )
00387 {
00388     // extensions are in the form var[=val]; There is always a semicolon at the end
00389     // if there is no equal sign then there is no value.
00390 
00391     string var ;
00392     string val ;
00393     int index = 0 ;
00394     bool done = false ;
00395     while( !done )
00396     {
00397         string::size_type semi = xstr.find( ';', index ) ;
00398         if( semi == string::npos )
00399         {
00400             string err = "malformed extensions "
00401                          + xstr.substr( index, xstr.length() - index )
00402                          + ", missing semicolon" ;
00403             throw BESInternalError( err, __FILE__, __LINE__ ) ;
00404         }
00405         string::size_type eq = xstr.find( '=', index ) ;
00406         if( eq == string::npos || eq > semi )
00407         {
00408             // there is no value for this variable
00409             var = xstr.substr( index, semi-index ) ;
00410             extensions[var] = "" ;
00411         }
00412         else if( eq == semi-1 )
00413         {
00414             string err = "malformed extensions "
00415                          + xstr.substr( index, xstr.length() - index )
00416                          + ", missing value after =" ;
00417             throw BESInternalError( err, __FILE__, __LINE__ ) ;
00418         }
00419         else
00420         {
00421             var = xstr.substr( index, eq-index ) ;
00422             val = xstr.substr( eq+1, semi-eq-1 ) ;
00423             extensions[var] = val ;
00424         }
00425         index = semi+1 ;
00426         if( index >= xstr.length() )
00427         {
00428             done = true ;
00429         }
00430     }
00431 }
00432 
00442 int
00443 PPTConnection::readBufferNonBlocking( char *inBuff, unsigned int buffer_size )
00444 {
00445     struct pollfd p ;
00446     p.fd = getSocket()->getSocketDescriptor();
00447     p.events = POLLIN ;
00448     struct pollfd arr[1] ;
00449     arr[0] = p ;
00450 
00451     // Lets loop _timeout times with a delay block on poll of 1000 milliseconds
00452     // and see if there is any data.
00453     for( int j = 0; j < _timeout; j++ )
00454     {
00455         if( poll( arr, 1, 1000 ) < 0 )
00456         {
00457             string error( "poll error" ) ;
00458             const char* error_info = strerror( errno ) ;
00459             if( error_info )
00460                 error += " " + (string)error_info ;
00461             throw BESInternalError( error, __FILE__, __LINE__ ) ;
00462         }
00463         else
00464         {
00465             if (arr[0].revents==POLLIN)
00466             {
00467                 return readBuffer( inBuff, buffer_size ) ;
00468             }
00469             else
00470             {
00471                 cout << " " << j << flush ;
00472             }
00473         }
00474     }
00475     cout << endl ;
00476     return -1 ;
00477 }
00478 
00479 unsigned int
00480 PPTConnection::getRecvChunkSize()
00481 {
00482     return _mySock->getRecvBufferSize() - PPT_CHUNK_HEADER_SPACE ;
00483 }
00484 
00485 unsigned int    
00486 PPTConnection::getSendChunkSize()
00487 {
00488     return _mySock->getSendBufferSize() - PPT_CHUNK_HEADER_SPACE ;
00489 }
00490 
00497 void
00498 PPTConnection::dump( ostream &strm ) const
00499 {
00500     strm << BESIndent::LMarg << "PPTConnection::dump - ("
00501                              << (void *)this << ")" << endl ;
00502     BESIndent::Indent() ;
00503     Connection::dump( strm ) ;
00504     BESIndent::UnIndent() ;
00505 }
00506 

Generated on Sat Aug 22 06:04:40 2009 for OPeNDAP Hyrax Back End Server (BES) by  doxygen 1.6.0