OPeNDAP Hyrax Back End Server (BES)
Updated for version 3.8.3
|
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