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, const 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 "
00278                      << bytesRead << " bytes" << endl ) ;
00279     if( bytesRead != 8 )
00280     {
00281         string err = "Failed to read length and type of chunk" ;
00282         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00283     }
00284 
00285     char lenbuffer[8] ;
00286     lenbuffer[0] = _inBuff[0] ;
00287     lenbuffer[1] = _inBuff[1] ;
00288     lenbuffer[2] = _inBuff[2] ;
00289     lenbuffer[3] = _inBuff[3] ;
00290     lenbuffer[4] = _inBuff[4] ;
00291     lenbuffer[5] = _inBuff[5] ;
00292     lenbuffer[6] = _inBuff[6] ;
00293     lenbuffer[7] = '\0' ;
00294     istringstream lenstrm( lenbuffer ) ;
00295     unsigned long inlen = 0 ;
00296     lenstrm >> hex >> setw(7) >> inlen ;
00297     BESDEBUG( "ppt", "Reading header, chunk length = " << inlen << endl ) ;
00298     BESDEBUG( "ppt", "Reading header, chunk type = " << _inBuff[7] << endl ) ;
00299 
00300     if( _inBuff[7] == 'x' )
00301     {
00302         ostringstream xstrm ;
00303         receive( xstrm, inlen ) ;
00304         read_extensions( extensions, xstrm.str() ) ;
00305     }
00306     else if( _inBuff[7] == 'd' )
00307     {
00308         if( !inlen )
00309         {
00310             // we've received the last chunk, return true, there
00311             // is nothing more to read from the socket
00312             return true ;
00313         }
00314         receive( *use_strm, inlen ) ;
00315     }
00316     else
00317     {
00318         string err = (string)"type of data is " + _inBuff[7]
00319                      + ", should be x for extensions or d for data" ;
00320         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00321     }
00322 
00323     return false ;
00324 }
00325 
00335 void
00336 PPTConnection::receive( ostream &strm, const unsigned int len )
00337 {
00338     BESDEBUG( "ppt", "PPTConnect::receive - len = " << len << endl ) ;
00339     if( !_inBuff )
00340     {
00341         string err = "buffer has not been initialized" ;
00342         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00343     }
00344 
00345     unsigned int to_read = len ;
00346     if( len > _inBuff_len )
00347     {
00348         to_read = _inBuff_len ;
00349     }
00350     BESDEBUG( "ppt", "PPTConnect::receive - to_read = " << to_read << endl ) ;
00351 
00352     // read a buffer
00353     int bytesRead = readBuffer( _inBuff, to_read ) ;
00354     if( bytesRead <= 0 )
00355     {
00356         string err = "Failed to read data from socket" ;
00357         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00358     }
00359     BESDEBUG( "ppt", "PPTConnect::receive - bytesRead = "
00360                      << bytesRead << endl ) ;
00361 
00362     // write the buffer read to the stream
00363     _inBuff[bytesRead] = '\0' ;
00364     strm.write( _inBuff, bytesRead ) ;
00365 
00366     // if bytesRead is less than the chunk length, then we need to go get
00367     // some more. It doesn't matter what _inBuff_len is, because we need
00368     // len bytes to be read and we read bytesRead bytes.
00369     if( bytesRead < len )
00370     {
00371         BESDEBUG( "ppt", "PPTConnect::receive - remaining = "
00372                          << (len - bytesRead) << endl ) ;
00373         receive( strm, len - bytesRead ) ;
00374     }
00375 }
00376 
00387 void
00388 PPTConnection::read_extensions( map<string,string> &extensions, const string &xstr )
00389 {
00390     // extensions are in the form var[=val]; There is always a semicolon at the end
00391     // if there is no equal sign then there is no value.
00392 
00393     string var ;
00394     string val ;
00395     int index = 0 ;
00396     bool done = false ;
00397     while( !done )
00398     {
00399         string::size_type semi = xstr.find( ';', index ) ;
00400         if( semi == string::npos )
00401         {
00402             string err = "malformed extensions "
00403                          + xstr.substr( index, xstr.length() - index )
00404                          + ", missing semicolon" ;
00405             throw BESInternalError( err, __FILE__, __LINE__ ) ;
00406         }
00407         string::size_type eq = xstr.find( '=', index ) ;
00408         if( eq == string::npos || eq > semi )
00409         {
00410             // there is no value for this variable
00411             var = xstr.substr( index, semi-index ) ;
00412             extensions[var] = "" ;
00413         }
00414         else if( eq == semi-1 )
00415         {
00416             string err = "malformed extensions "
00417                          + xstr.substr( index, xstr.length() - index )
00418                          + ", missing value after =" ;
00419             throw BESInternalError( err, __FILE__, __LINE__ ) ;
00420         }
00421         else
00422         {
00423             var = xstr.substr( index, eq-index ) ;
00424             val = xstr.substr( eq+1, semi-eq-1 ) ;
00425             extensions[var] = val ;
00426         }
00427         index = semi+1 ;
00428         if( index >= xstr.length() )
00429         {
00430             done = true ;
00431         }
00432     }
00433 }
00434 
00445 int
00446 PPTConnection::readBufferNonBlocking( char *inBuff,
00447                                       const unsigned int buffer_size )
00448 {
00449     struct pollfd p ;
00450     p.fd = getSocket()->getSocketDescriptor();
00451     p.events = POLLIN ;
00452     struct pollfd arr[1] ;
00453     arr[0] = p ;
00454 
00455     // Lets loop _timeout times with a delay block on poll of 1000 milliseconds
00456     // and see if there is any data.
00457     for( int j = 0; j < _timeout; j++ )
00458     {
00459         if( poll( arr, 1, 1000 ) < 0 )
00460         {
00461             string error( "poll error" ) ;
00462             const char* error_info = strerror( errno ) ;
00463             if( error_info )
00464                 error += " " + (string)error_info ;
00465             throw BESInternalError( error, __FILE__, __LINE__ ) ;
00466         }
00467         else
00468         {
00469             if (arr[0].revents==POLLIN)
00470             {
00471                 return readBuffer( inBuff, buffer_size ) ;
00472             }
00473             else
00474             {
00475                 cout << " " << j << flush ;
00476             }
00477         }
00478     }
00479     cout << endl ;
00480     return -1 ;
00481 }
00482 
00483 unsigned int
00484 PPTConnection::getRecvChunkSize()
00485 {
00486     return _mySock->getRecvBufferSize() - PPT_CHUNK_HEADER_SPACE ;
00487 }
00488 
00489 unsigned int    
00490 PPTConnection::getSendChunkSize()
00491 {
00492     return _mySock->getSendBufferSize() - PPT_CHUNK_HEADER_SPACE ;
00493 }
00494 
00501 void
00502 PPTConnection::dump( ostream &strm ) const
00503 {
00504     strm << BESIndent::LMarg << "PPTConnection::dump - ("
00505                              << (void *)this << ")" << endl ;
00506     BESIndent::Indent() ;
00507     Connection::dump( strm ) ;
00508     BESIndent::UnIndent() ;
00509 }
00510 

Generated on Thu Sep 16 15:20:31 2010 for OPeNDAP Hyrax Back End Server (BES) by  doxygen 1.4.7