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 #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