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,2005 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::endl ;
00043 using std::flush ;
00044 using std::ostringstream ;
00045 using std::istringstream ;
00046 using std::hex ;
00047 using std::setw ;
00048 using std::setfill ;
00049 
00050 #include "PPTConnection.h"
00051 #include "PPTProtocol.h"
00052 #include "Socket.h"
00053 #include "BESInternalError.h"
00054 
00089 void
00090 PPTConnection::send( const string &buffer,
00091                      map<string,string> &extensions )
00092 {
00093     if( !buffer.empty() )
00094     {
00095         sendChunk( buffer, extensions ) ;
00096 
00097         // send the last chunk without the extensions
00098         map<string,string> no_extensions ;
00099         sendChunk( "", no_extensions ) ;
00100     }
00101     else
00102     {
00103         sendChunk( "", extensions ) ;
00104     }
00105 }
00106 
00109 void
00110 PPTConnection::sendExit()
00111 {
00112     map<string,string> extensions ;
00113     extensions["status"] = PPTProtocol::PPT_EXIT_NOW ;
00114     send( "", extensions ) ;
00115 }
00116 
00125 void
00126 PPTConnection::sendChunk( const string &buffer, map<string,string> &extensions )
00127 {
00128     ostringstream strm ;
00129     if( extensions.size() )
00130     {
00131         sendExtensions( extensions ) ;
00132     }
00133     strm << hex << setw( 4 ) << setfill( '0' ) << buffer.length() << "d" ;
00134     if( !buffer.empty() )
00135     {
00136         strm << buffer ;
00137     }
00138     string toSend = strm.str() ;
00139     send( toSend ) ;
00140 }
00141 
00146 void
00147 PPTConnection::sendExtensions( map<string,string> &extensions )
00148 {
00149     ostringstream strm ;
00150     if( extensions.size() )
00151     {
00152         ostringstream estrm ;
00153         map<string,string>::const_iterator i = extensions.begin() ;
00154         map<string,string>::const_iterator ie = extensions.end() ;
00155         for( ; i != ie; i++ )
00156         {
00157             estrm << (*i).first ;
00158             string value = (*i).second ;
00159             if( !value.empty() )
00160             {
00161                 estrm << "=" << value ;
00162             }
00163             estrm << ";" ;
00164         }
00165         string xstr = estrm.str() ;
00166         strm << hex << setw( 4 ) << setfill( '0' ) << xstr.length() << "x" << xstr ;
00167         string toSend = strm.str() ;
00168         send( toSend ) ;
00169     }
00170 }
00171 
00178 void
00179 PPTConnection::send( const string &buffer )
00180 {
00181     _mySock->send( buffer, 0, buffer.length() ) ;
00182     _mySock->sync() ;
00183 }
00184     
00185 /* OLD RECEIVE
00186 bool
00187 PPTConnection::receive( ostream *strm )
00188 {
00189     bool isDone = false ;
00190     ostream *use_strm = _out ;
00191     if( strm )
00192         use_strm = strm ;
00193 
00194     int termlen = PPTProtocol::PPT_COMPLETE_DATA_TRANSMITION.length() ;
00195     int exitlen = PPTProtocol::PPT_EXIT_NOW.length() ;
00196     int start = termlen ;
00197     bool done = false ;
00198     char *inBuff = new char[PPT_PROTOCOL_BUFFER_SIZE+termlen+1] ;
00199     while( done == false )
00200     {
00201         int bytesRead = readBuffer( inBuff+termlen ) ;
00202         if( bytesRead != 0 )
00203         {
00204             if( !strncmp( inBuff+start, PPTProtocol::PPT_EXIT_NOW.c_str(), exitlen ) )
00205             {
00206                 done = true ;
00207                 isDone = true ;
00208             }
00209             else
00210             {
00211                 int charsInBuff = bytesRead + ( termlen - start ) ;
00212                 int writeBytes = charsInBuff - termlen ;
00213                 if( charsInBuff >= termlen )
00214                 {
00215                     if( !strncmp( inBuff+bytesRead, PPTProtocol::PPT_COMPLETE_DATA_TRANSMITION.c_str(), termlen ) )
00216                     {
00217                         done = true ;
00218                     }
00219 
00220                     for( int j = 0; j < writeBytes; j++ )
00221                     {
00222                         if( use_strm )
00223                         {
00224                             (*use_strm) << inBuff[start+j] ;
00225                         }
00226                     }
00227 
00228                     if( !done )
00229                     {
00230                         memcpy( inBuff, inBuff + bytesRead, termlen ) ;
00231                         start = 0 ;
00232                     }
00233                 }
00234                 else
00235                 {
00236                     int newstart = termlen - charsInBuff ;
00237                     memcpy( inBuff + newstart, inBuff + start, charsInBuff ) ;
00238                     start = newstart ;
00239                 }
00240             }
00241         }
00242         else
00243         {
00244             done = true ;
00245         }
00246     }
00247     delete [] inBuff ;
00248     return isDone ;
00249 }
00250 */
00251 
00252 /* WORKING TOKEN RECEIVE
00253 bool
00254 PPTConnection::receive( ostream *strm )
00255 {
00256     bool isDone = false ;
00257     ostream *use_strm = _out ;
00258     if( strm )
00259         use_strm = strm ;
00260 
00261     int bytesRead, markBufBytes, i ;
00262 
00263     int termlen = PPTProtocol::PPT_COMPLETE_DATA_TRANSMITION.length() ;
00264     int exitlen = PPTProtocol::PPT_EXIT_NOW.length() ;
00265 
00266     PPTMarkFinder mf( (unsigned char *)PPTProtocol::PPT_COMPLETE_DATA_TRANSMITION.c_str(),
00267                       termlen ) ;
00268     unsigned char markBuffer[termlen] ;
00269     markBufBytes = 0 ; // zero byte count in the mark buffer
00270 
00271     char *inBuff = new char[PPT_PROTOCOL_BUFFER_SIZE+1] ;
00272     bool done = false;
00273     while( !done )
00274     {
00275         bytesRead = readBuffer( inBuff ) ;
00276         if( bytesRead != 0 )
00277         {
00278             // did we find an exit string?
00279             if( !strncmp( inBuff, PPTProtocol::PPT_EXIT_NOW.c_str(), exitlen ) )
00280             {
00281                 done = true ;
00282                 isDone = true ;
00283             }
00284             else
00285             {
00286                 // look at what we got to find the exit or the term string
00287                 for( i = 0; i < bytesRead && !done; i++ )
00288                 {
00289                     // check for the mark. If we've found the entire marker
00290                     // then markCheck returns true. If we haven't yet found
00291                     // the entire mark but have found part of it then
00292                     // markCheck will return false and the markIndex will be
00293                     // greater than 0. If the check failed because the
00294                     // character checked is not part of the marker then the
00295                     // markIndex is set to 0 and false is returned.
00296                     done = mf.markCheck( inBuff[i] ) ;
00297                     if( !done )
00298                     {
00299                         // didn't find the mark yet, check if we've found
00300                         // part of it
00301                         if( mf.getMarkIndex() > 0 )
00302                         {
00303                             // we found part of it, so cache what we found
00304                             // in markBuffer
00305                             markBuffer[markBufBytes++] = inBuff[i] ;
00306                         }
00307                         else
00308                         {
00309                             // we are here because inBuff[i] does not match
00310                             // the current position in the marker.
00311 
00312                             // if we found part of the mark (there's some
00313                             // characters in markBuffer meaning markBufBytes
00314                             // is greater than 0) then we need to send the
00315                             // first character in the markBuffer and begiin
00316                             // checking the rest of the characters in the
00317                             // markBuffer against the markFinder. inBuff[i]
00318                             // could still be part of the marker, so don't
00319                             // send it to the stream just yet. The case here
00320                             // is that the inBuff contains PPPT instead of
00321                             // PPT as the beginning of the marker. PP is in
00322                             // the markBuffer and we've just checked the
00323                             // third 'P', which returns false and markIndex
00324                             // is set to 0. So, send the first 'P' in
00325                             // markBuffer, shift the remaining characters
00326                             // and check them against the markFinder.
00327                             if( markBufBytes > 0 )
00328                             {
00329                                 // let's put inBuf[i] in the markBuffer so
00330                                 // that we don't have to worry about it
00331                                 // anymore
00332                                 markBuffer[markBufBytes++] = inBuff[i] ;
00333 
00334                                 bool isdone = false ;
00335                                 while( !isdone )
00336                                 {
00337                                     // always throw the first character iin
00338                                     // markBuffer to the stream
00339                                     (*use_strm) << markBuffer[0] ;
00340 
00341                                     // shift the rest of the characters in
00342                                     // markBuffer to the left one
00343                                     for( int j = 1; j < markBufBytes; j++ )
00344                                     {
00345                                         markBuffer[j-1] = markBuffer[j] ;
00346                                     }
00347 
00348                                     // we've sent the first character to the
00349                                     // stream, so reduce the number in
00350                                     // markBuffer
00351                                     markBufBytes-- ;
00352 
00353                                     // start checking the rest of the
00354                                     // characters in markBuffer
00355                                     bool partof = true ;
00356                                     for( int j = 0; j < markBufBytes && partof; j++ )
00357                                     {
00358                                         // don't need to look at the result
00359                                         // of markCheck because we already
00360                                         // know that we have not
00361                                         // found the complete marker, we're
00362                                         // just dealing with potentially
00363                                         // part of the marker
00364                                         mf.markCheck( markBuffer[j] ) ;
00365                                         if( mf.getMarkIndex() == 0 )
00366                                         {
00367                                             // if the markIndex is 0 then
00368                                             // the character we just checked
00369                                             // is not part of the marker, so
00370                                             // repeat this process starting
00371                                             // at for( !isdone )
00372                                             partof = false ;
00373                                         }
00374                                     }
00375 
00376                                     if( partof == true )
00377                                     {
00378                                         // if we've made it this far then
00379                                         // what's in the markBuffer is part
00380                                         // of the marker, so move on to the
00381                                         // next character in inBuff
00382                                         isdone = true ;
00383                                     }
00384                                 }
00385                             }
00386                             else
00387                             {
00388                                 // There's nothing in the markBuffer so just
00389                                 // send inBuff on to the stream
00390                                 (*use_strm) << inBuff[i] ;
00391                             }
00392                         }
00393                     }
00394                 }
00395             }
00396         }
00397         else
00398         {
00399             done = true;
00400         }
00401     }
00402     delete [] inBuff ;
00403     return isDone ;
00404 }
00405 */
00406 
00413 int
00414 PPTConnection::readBuffer( char *buffer, unsigned int buffer_size )
00415 {
00416     return _mySock->receive( buffer, buffer_size ) ;
00417 }
00418 
00434 bool
00435 PPTConnection::receive( map<string,string> &extensions,
00436                         ostream *strm )
00437 {
00438     ostream *use_strm = _out ;
00439     if( strm )
00440         use_strm = strm ;
00441 
00442     // The first buffer will contain the length of the chunk at the beginning.
00443     if( !_inBuff )
00444         _inBuff = new char[PPT_PROTOCOL_BUFFER_SIZE+1] ;
00445 
00446     // read the first 5 bytes. The first 4 are the length and the next 1
00447     // if x then extensions follow, if d then data follows.
00448     int bytesRead = readBuffer( _inBuff, 5 ) ;
00449     if( bytesRead != 5 )
00450     {
00451         string err = "Failed to read length and type of chunk" ;
00452         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00453     }
00454 
00455     char lenbuffer[5] ;
00456     lenbuffer[0] = _inBuff[0] ;
00457     lenbuffer[1] = _inBuff[1] ;
00458     lenbuffer[2] = _inBuff[2] ;
00459     lenbuffer[3] = _inBuff[3] ;
00460     lenbuffer[4] = '\0' ;
00461     istringstream lenstrm( lenbuffer ) ;
00462     unsigned short inlen = 0 ;
00463     lenstrm >> hex >> setw(4) >> inlen ;
00464 
00465     if( _inBuff[4] == 'x' )
00466     {
00467         ostringstream xstrm ;
00468         receive( xstrm, inlen ) ;
00469         read_extensions( extensions, xstrm.str() ) ;
00470     }
00471     else if( _inBuff[4] == 'd' )
00472     {
00473         if( !inlen )
00474         {
00475             // we've received the last chunk, return true, there
00476             // is nothing more to read from the socket
00477             return true ;
00478         }
00479         receive( *use_strm, inlen ) ;
00480     }
00481     else
00482     {
00483         string err = (string)"type of data is " + _inBuff[4]
00484                      + ", should be x for extensions or d for data" ;
00485         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00486     }
00487 
00488     return false ;
00489 }
00490 
00500 void
00501 PPTConnection::receive( ostream &strm, unsigned short len )
00502 {
00503     if( !_inBuff )
00504     {
00505         string err = "buffer has not been initialized" ;
00506         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00507     }
00508     // I added this test because in PPTConnection::receive( map<string,string>,
00509         // ostream ) this method is called with 'len' passed a value that's read from
00510         // the input stream. That value could be manipulated to cause a bufer
00511         // overflow. Note that _inBuff is PPT_PROTOCOL_BUFFER_SIZE + 1 so reading
00512         // that many bytes leaves room for the null byte. jhrg 3/3/08
00513     if( len > PPT_PROTOCOL_BUFFER_SIZE )
00514     {
00515         string err = "buffer is not large enough" ;
00516         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00517     }
00518     int bytesRead = readBuffer( _inBuff, len ) ;
00519     if( bytesRead <= 0 )
00520     {
00521         string err = "Failed to read data from socket" ;
00522         throw BESInternalError( err, __FILE__, __LINE__ ) ;
00523     }
00524     _inBuff[bytesRead] = '\0' ;
00525     strm.write( _inBuff, bytesRead ) ;
00526     if( bytesRead < len )
00527     {
00528         receive( strm, len - bytesRead ) ;
00529     }
00530 }
00531 
00542 void
00543 PPTConnection::read_extensions( map<string,string> &extensions, const string &xstr )
00544 {
00545     // extensions are in the form var[=val]; There is always a semicolon at the end
00546     // if there is no equal sign then there is no value.
00547 
00548     string var ;
00549     string val ;
00550     int index = 0 ;
00551     bool done = false ;
00552     while( !done )
00553     {
00554         string::size_type semi = xstr.find( ';', index ) ;
00555         if( semi == string::npos )
00556         {
00557             string err = "malformed extensions "
00558                          + xstr.substr( index, xstr.length() - index )
00559                          + ", missing semicolon" ;
00560             throw BESInternalError( err, __FILE__, __LINE__ ) ;
00561         }
00562         string::size_type eq = xstr.find( '=', index ) ;
00563         if( eq == string::npos || eq > semi )
00564         {
00565             // there is no value for this variable
00566             var = xstr.substr( index, semi-index ) ;
00567             extensions[var] = "" ;
00568         }
00569         else if( eq == semi-1 )
00570         {
00571             string err = "malformed extensions "
00572                          + xstr.substr( index, xstr.length() - index )
00573                          + ", missing value after =" ;
00574             throw BESInternalError( err, __FILE__, __LINE__ ) ;
00575         }
00576         else
00577         {
00578             var = xstr.substr( index, eq-index ) ;
00579             val = xstr.substr( eq+1, semi-eq-1 ) ;
00580             extensions[var] = val ;
00581         }
00582         index = semi+1 ;
00583         if( index >= xstr.length() )
00584         {
00585             done = true ;
00586         }
00587     }
00588 }
00589 
00599 int
00600 PPTConnection::readBufferNonBlocking( char *inBuff )
00601 {
00602     struct pollfd p ;
00603     p.fd = getSocket()->getSocketDescriptor();
00604     p.events = POLLIN ;
00605     struct pollfd arr[1] ;
00606     arr[0] = p ;
00607 
00608     // Lets loop _timeout times with a delay block on poll of 1000 milliseconds
00609     // and see if there is any data.
00610     for( int j = 0; j < _timeout; j++ )
00611     {
00612         if( poll( arr, 1, 1000 ) < 0 )
00613         {
00614             string error( "poll error" ) ;
00615             const char* error_info = strerror( errno ) ;
00616             if( error_info )
00617                 error += " " + (string)error_info ;
00618             throw BESInternalError( error, __FILE__, __LINE__ ) ;
00619         }
00620         else
00621         {
00622             if (arr[0].revents==POLLIN)
00623             {
00624                 return readBuffer( inBuff, PPT_PROTOCOL_BUFFER_SIZE ) ;
00625             }
00626             else
00627             {
00628                 cout << " " << j << flush ;
00629             }
00630         }
00631     }
00632     cout << endl ;
00633     return -1 ;
00634 }
00635 
00642 void
00643 PPTConnection::dump( ostream &strm ) const
00644 {
00645     strm << BESIndent::LMarg << "PPTConnection::dump - ("
00646                              << (void *)this << ")" << endl ;
00647     BESIndent::Indent() ;
00648     Connection::dump( strm ) ;
00649     BESIndent::UnIndent() ;
00650 }
00651 

Generated on Tue Mar 4 23:13:37 2008 for OPeNDAP Back End Server (BES) by  doxygen 1.5.1