OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
PPTConnection.cc
Go to the documentation of this file.
1 // PPTConnection.cc
2 
3 // This file is part of bes, A C++ back-end server implementation framework
4 // for the OPeNDAP Data Access Protocol.
5 
6 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research
7 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 //
23 // You can contact University Corporation for Atmospheric Research at
24 // 3080 Center Green Drive, Boulder, CO 80301
25 
26 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
27 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
28 //
29 // Authors:
30 // pwest Patrick West <pwest@ucar.edu>
31 // jgarcia Jose Garcia <jgarcia@ucar.edu>
32 
33 #include <poll.h>
34 
35 #include <cerrno>
36 #include <cstring>
37 #include <iostream>
38 #include <sstream>
39 #include <iomanip>
40 
41 using std::cout ;
42 using std::cerr ;
43 using std::endl ;
44 using std::flush ;
45 using std::ostringstream ;
46 using std::istringstream ;
47 using std::hex ;
48 using std::setw ;
49 using std::setfill ;
50 
51 #include "PPTConnection.h"
52 #include "PPTProtocol.h"
53 #include "Socket.h"
54 #include "BESDebug.h"
55 #include "BESInternalError.h"
56 
58 {
59  if( _inBuff )
60  {
61  delete [] _inBuff ;
62  _inBuff = 0 ;
63  }
64 }
65 
100 void
101 PPTConnection::send( const string &buffer,
102  map<string,string> &extensions )
103 {
104  if( !buffer.empty() )
105  {
106  sendChunk( buffer, extensions ) ;
107 
108  // send the last chunk without the extensions
109  map<string,string> no_extensions ;
110  sendChunk( "", no_extensions ) ;
111  }
112  else
113  {
114  sendChunk( "", extensions ) ;
115  }
116 }
117 
120 void
122 {
123  map<string,string> extensions ;
124  extensions["status"] = PPTProtocol::PPT_EXIT_NOW ;
125  send( "", extensions ) ;
126 }
127 
136 void
137 PPTConnection::sendChunk( const string &buffer, map<string,string> &extensions )
138 {
139  ostringstream strm ;
140  if( extensions.size() )
141  {
142  sendExtensions( extensions ) ;
143  }
144  strm << hex << setw( 7 ) << setfill( '0' ) << buffer.length() << "d" ;
145  if( !buffer.empty() )
146  {
147  strm << buffer ;
148  }
149  string toSend = strm.str() ;
150  send( toSend ) ;
151 }
152 
157 void
158 PPTConnection::sendExtensions( map<string,string> &extensions )
159 {
160  ostringstream strm ;
161  if( extensions.size() )
162  {
163  ostringstream estrm ;
164  map<string,string>::const_iterator i = extensions.begin() ;
165  map<string,string>::const_iterator ie = extensions.end() ;
166  for( ; i != ie; i++ )
167  {
168  estrm << (*i).first ;
169  string value = (*i).second ;
170  if( !value.empty() )
171  {
172  estrm << "=" << value ;
173  }
174  estrm << ";" ;
175  }
176  string xstr = estrm.str() ;
177  strm << hex << setw( 7 ) << setfill( '0' ) << xstr.length() << "x" << xstr ;
178  string toSend = strm.str() ;
179  send( toSend ) ;
180  }
181 }
182 
189 void
190 PPTConnection::send( const string &buffer )
191 {
192  BESDEBUG( "ppt", "PPTConnection::send - sending " << buffer << endl ) ;
193  _mySock->send( buffer, 0, buffer.length() ) ;
194 #if 0
195  // was calling fsync() which is not defined for sockets. There might be some
196  // 'sync' operation needed in the future, but for now this is an empty call. removed
197  // jhrg 5/5/11
198  _mySock->sync() ;
199 #endif
200 }
201 
208 int
209 PPTConnection::readBuffer( char *buffer, const unsigned int buffer_size )
210 {
211  return _mySock->receive( buffer, buffer_size ) ;
212 }
213 
214 int
215 PPTConnection::readChunkHeader( char *buffer, /*unsigned */ int buffer_size )
216 {
217  char *temp_buffer = buffer ;
218  int totalBytesRead = 0 ;
219  bool done = false ;
220  while( !done )
221  {
222  int bytesRead = readBuffer( temp_buffer, buffer_size ) ;
223  BESDEBUG( "ppt", "PPTConnection::readChunkHeader - read "
224  << bytesRead << " bytes" << endl ) ;
225  if( bytesRead < 0 )
226  {
227  return bytesRead ;
228  }
229  if( bytesRead < buffer_size )
230  {
231  buffer_size = buffer_size - bytesRead ;
232  temp_buffer = temp_buffer + bytesRead ;
233  totalBytesRead += bytesRead ;
234  }
235  else
236  {
237  totalBytesRead += bytesRead ;
238  done = true ;
239  }
240  }
241  buffer[totalBytesRead] = '\0' ;
242  return totalBytesRead ;
243 }
244 
260 bool
261 PPTConnection::receive( map<string,string> &extensions,
262  ostream *strm )
263 {
264  ostream *use_strm = _out ;
265  if( strm )
266  use_strm = strm ;
267 
268  // If the receive buffer has not yet been created, get the receive size
269  // and create the buffer.
270  BESDEBUG( "ppt", "PPTConnection::receive: buffer size = " << _inBuff_len
271  << endl ) ;
272  if( !_inBuff )
273  {
274  _inBuff_len = _mySock->getRecvBufferSize() + 1 ;
275  _inBuff = new char[_inBuff_len+1] ;
276  }
277 
278  // The first buffer will contain the length of the chunk at the beginning.
279  // read the first 8 bytes. The first 7 are the length and the next 1
280  // if x then extensions follow, if d then data follows.
281  int bytesRead = readChunkHeader( _inBuff, 8 ) ;
282  BESDEBUG( "ppt", "Reading header, read "
283  << bytesRead << " bytes" << endl ) ;
284  if( bytesRead != 8 )
285  {
286  string err = "Failed to read length and type of chunk" ;
287  throw BESInternalError( err, __FILE__, __LINE__ ) ;
288  }
289 
290  char lenbuffer[8] ;
291  lenbuffer[0] = _inBuff[0] ;
292  lenbuffer[1] = _inBuff[1] ;
293  lenbuffer[2] = _inBuff[2] ;
294  lenbuffer[3] = _inBuff[3] ;
295  lenbuffer[4] = _inBuff[4] ;
296  lenbuffer[5] = _inBuff[5] ;
297  lenbuffer[6] = _inBuff[6] ;
298  lenbuffer[7] = '\0' ;
299  istringstream lenstrm( lenbuffer ) ;
300  unsigned long inlen = 0 ;
301  lenstrm >> hex >> setw(7) >> inlen ;
302  BESDEBUG( "ppt", "Reading header, chunk length = " << inlen << endl ) ;
303  BESDEBUG( "ppt", "Reading header, chunk type = " << _inBuff[7] << endl ) ;
304 
305  if( _inBuff[7] == 'x' )
306  {
307  ostringstream xstrm ;
308  receive( xstrm, inlen ) ;
309  read_extensions( extensions, xstrm.str() ) ;
310  }
311  else if( _inBuff[7] == 'd' )
312  {
313  if( !inlen )
314  {
315  // we've received the last chunk, return true, there
316  // is nothing more to read from the socket
317  return true ;
318  }
319  receive( *use_strm, inlen ) ;
320  }
321  else
322  {
323  string err = (string)"type of data is " + _inBuff[7]
324  + ", should be x for extensions or d for data" ;
325  throw BESInternalError( err, __FILE__, __LINE__ ) ;
326  }
327 
328  return false ;
329 }
330 
340 void
341 PPTConnection::receive( ostream &strm, const /* unsigned */ int len )
342 {
343  BESDEBUG( "ppt", "PPTConnect::receive - len = " << len << endl ) ;
344  if( !_inBuff )
345  {
346  string err = "buffer has not been initialized" ;
347  throw BESInternalError( err, __FILE__, __LINE__ ) ;
348  }
349 
350  /* unsigned */ int to_read = len ;
351  if( len > _inBuff_len )
352  {
353  to_read = _inBuff_len ;
354  }
355  BESDEBUG( "ppt", "PPTConnect::receive - to_read = " << to_read << endl ) ;
356 
357  // read a buffer
358  int bytesRead = readBuffer( _inBuff, to_read ) ;
359  if( bytesRead <= 0 )
360  {
361  string err = "Failed to read data from socket" ;
362  throw BESInternalError( err, __FILE__, __LINE__ ) ;
363  }
364  BESDEBUG( "ppt", "PPTConnect::receive - bytesRead = "
365  << bytesRead << endl ) ;
366 
367  // write the buffer read to the stream
368  _inBuff[bytesRead] = '\0' ;
369  strm.write( _inBuff, bytesRead ) ;
370 
371  // if bytesRead is less than the chunk length, then we need to go get
372  // some more. It doesn't matter what _inBuff_len is, because we need
373  // len bytes to be read and we read bytesRead bytes.
374  if( bytesRead < len )
375  {
376  BESDEBUG( "ppt", "PPTConnect::receive - remaining = "
377  << (len - bytesRead) << endl ) ;
378  receive( strm, len - bytesRead ) ;
379  }
380 }
381 
392 void
393 PPTConnection::read_extensions( map<string,string> &extensions, const string &xstr )
394 {
395  // extensions are in the form var[=val]; There is always a semicolon at the end
396  // if there is no equal sign then there is no value.
397 
398  string var ;
399  string val ;
400  unsigned int index = 0 ;
401  bool done = false ;
402  while( !done )
403  {
404  string::size_type semi = xstr.find( ';', index ) ;
405  if( semi == string::npos )
406  {
407  string err = "malformed extensions "
408  + xstr.substr( index, xstr.length() - index )
409  + ", missing semicolon" ;
410  throw BESInternalError( err, __FILE__, __LINE__ ) ;
411  }
412  string::size_type eq = xstr.find( '=', index ) ;
413  if( eq == string::npos || eq > semi )
414  {
415  // there is no value for this variable
416  var = xstr.substr( index, semi-index ) ;
417  extensions[var] = "" ;
418  }
419  else if( eq == semi-1 )
420  {
421  string err = "malformed extensions "
422  + xstr.substr( index, xstr.length() - index )
423  + ", missing value after =" ;
424  throw BESInternalError( err, __FILE__, __LINE__ ) ;
425  }
426  else
427  {
428  var = xstr.substr( index, eq-index ) ;
429  val = xstr.substr( eq+1, semi-eq-1 ) ;
430  extensions[var] = val ;
431  }
432  index = semi+1 ;
433  if( index >= xstr.length() )
434  {
435  done = true ;
436  }
437  }
438 }
439 
450 int
452  const /* unsigned*/ int buffer_size )
453 {
454  struct pollfd p ;
455  p.fd = getSocket()->getSocketDescriptor();
456  p.events = POLLIN ;
457  struct pollfd arr[1] ;
458  arr[0] = p ;
459 
460  // Lets loop _timeout times with a delay block on poll of 1000 milliseconds
461  // and see if there are any data.
462  for( int j = 0; j < _timeout; j++ )
463  {
464  if( poll( arr, 1, 1000 ) < 0 )
465  {
466  string error( "poll error" ) ;
467 
468  // Allow this call to be interrupted with it being an error. jhrg 6/15/11
469  if (errno == EINTR || errno == EAGAIN)
470  continue;
471 
472  const char* error_info = strerror( errno ) ;
473  if( error_info )
474  error += " " + (string)error_info ;
475  throw BESInternalError( error, __FILE__, __LINE__ ) ;
476  }
477  else
478  {
479  if (arr[0].revents==POLLIN)
480  {
481  return readBuffer( inBuff, buffer_size ) ;
482  }
483  else
484  {
485  cout << " " << j << flush ;
486  }
487  }
488  }
489  cout << endl ;
490  return -1 ;
491 }
492 
493 unsigned int
495 {
497 }
498 
499 unsigned int
501 {
503 }
504 
511 void
512 PPTConnection::dump( ostream &strm ) const
513 {
514  strm << BESIndent::LMarg << "PPTConnection::dump - ("
515  << (void *)this << ")" << endl ;
517  Connection::dump( strm ) ;
519 }
520 
exception thrown if inernal error encountered
virtual unsigned int getRecvBufferSize()=0
#define PPT_CHUNK_HEADER_SPACE
Definition: PPTConnection.h:41
ostream * _out
Definition: Connection.h:51
virtual ~PPTConnection()
static void Indent()
Definition: BESIndent.cc:38
virtual void sendExit()
Send the exit token as an extension.
virtual void read_extensions(map< string, string > &extensions, const string &xstr)
the string passed are extensions, read them and store the name/value pairs into the passed map ...
static ostream & LMarg(ostream &strm)
Definition: BESIndent.cc:73
virtual void send(const string &str, int start, int end)
Definition: Socket.cc:93
virtual void sendExtensions(map< string, string > &extensions)
send the specified extensions
virtual unsigned int getSendChunkSize()
virtual void dump(ostream &strm) const
dumps information about this object
Definition: Connection.cc:42
virtual int readBufferNonBlocking(char *inBuff, const int buff_size)
read a buffer of data from the socket without blocking
static string PPT_EXIT_NOW
Definition: PPTProtocol.h:47
virtual int receive(char *inBuff, const int inSize)
Definition: Socket.cc:108
virtual unsigned int getRecvChunkSize()
virtual int getSocketDescriptor()
Definition: Socket.h:74
virtual void dump(ostream &strm) const
dumps information about this object
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
static void UnIndent()
Definition: BESIndent.cc:44
virtual Socket * getSocket()
Definition: Connection.h:77
virtual void send(const string &buffer)
sends the buffer to the socket
Socket * _mySock
Definition: Connection.h:50
virtual unsigned int getSendBufferSize()=0