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  _mySock->sync() ;
195 }
196 
203 int
204 PPTConnection::readBuffer( char *buffer, const unsigned int buffer_size )
205 {
206  return _mySock->receive( buffer, buffer_size ) ;
207 }
208 
209 int
210 PPTConnection::readChunkHeader( char *buffer, unsigned int buffer_size )
211 {
212  char *temp_buffer = buffer ;
213  int totalBytesRead = 0 ;
214  bool done = false ;
215  while( !done )
216  {
217  int bytesRead = readBuffer( temp_buffer, buffer_size ) ;
218  BESDEBUG( "ppt", "PPTConnection::readChunkHeader - read "
219  << bytesRead << " bytes" << endl ) ;
220  if( bytesRead < 0 )
221  {
222  return bytesRead ;
223  }
224  if( bytesRead < buffer_size )
225  {
226  buffer_size = buffer_size - bytesRead ;
227  temp_buffer = temp_buffer + bytesRead ;
228  totalBytesRead += bytesRead ;
229  }
230  else
231  {
232  totalBytesRead += bytesRead ;
233  done = true ;
234  }
235  }
236  buffer[totalBytesRead] = '\0' ;
237  return totalBytesRead ;
238 }
239 
255 bool
256 PPTConnection::receive( map<string,string> &extensions,
257  ostream *strm )
258 {
259  ostream *use_strm = _out ;
260  if( strm )
261  use_strm = strm ;
262 
263  // If the receive buffer has not yet been created, get the receive size
264  // and create the buffer.
265  BESDEBUG( "ppt", "PPTConnection::receive: buffer size = " << _inBuff_len
266  << endl ) ;
267  if( !_inBuff )
268  {
269  _inBuff_len = _mySock->getRecvBufferSize() + 1 ;
270  _inBuff = new char[_inBuff_len+1] ;
271  }
272 
273  // The first buffer will contain the length of the chunk at the beginning.
274  // read the first 8 bytes. The first 7 are the length and the next 1
275  // if x then extensions follow, if d then data follows.
276  int bytesRead = readChunkHeader( _inBuff, 8 ) ;
277  BESDEBUG( "ppt", "Reading header, read "
278  << bytesRead << " bytes" << endl ) ;
279  if( bytesRead != 8 )
280  {
281  string err = "Failed to read length and type of chunk" ;
282  throw BESInternalError( err, __FILE__, __LINE__ ) ;
283  }
284 
285  char lenbuffer[8] ;
286  lenbuffer[0] = _inBuff[0] ;
287  lenbuffer[1] = _inBuff[1] ;
288  lenbuffer[2] = _inBuff[2] ;
289  lenbuffer[3] = _inBuff[3] ;
290  lenbuffer[4] = _inBuff[4] ;
291  lenbuffer[5] = _inBuff[5] ;
292  lenbuffer[6] = _inBuff[6] ;
293  lenbuffer[7] = '\0' ;
294  istringstream lenstrm( lenbuffer ) ;
295  unsigned long inlen = 0 ;
296  lenstrm >> hex >> setw(7) >> inlen ;
297  BESDEBUG( "ppt", "Reading header, chunk length = " << inlen << endl ) ;
298  BESDEBUG( "ppt", "Reading header, chunk type = " << _inBuff[7] << endl ) ;
299 
300  if( _inBuff[7] == 'x' )
301  {
302  ostringstream xstrm ;
303  receive( xstrm, inlen ) ;
304  read_extensions( extensions, xstrm.str() ) ;
305  }
306  else if( _inBuff[7] == 'd' )
307  {
308  if( !inlen )
309  {
310  // we've received the last chunk, return true, there
311  // is nothing more to read from the socket
312  return true ;
313  }
314  receive( *use_strm, inlen ) ;
315  }
316  else
317  {
318  string err = (string)"type of data is " + _inBuff[7]
319  + ", should be x for extensions or d for data" ;
320  throw BESInternalError( err, __FILE__, __LINE__ ) ;
321  }
322 
323  return false ;
324 }
325 
335 void
336 PPTConnection::receive( ostream &strm, const unsigned int len )
337 {
338  BESDEBUG( "ppt", "PPTConnect::receive - len = " << len << endl ) ;
339  if( !_inBuff )
340  {
341  string err = "buffer has not been initialized" ;
342  throw BESInternalError( err, __FILE__, __LINE__ ) ;
343  }
344 
345  unsigned int to_read = len ;
346  if( len > _inBuff_len )
347  {
348  to_read = _inBuff_len ;
349  }
350  BESDEBUG( "ppt", "PPTConnect::receive - to_read = " << to_read << endl ) ;
351 
352  // read a buffer
353  int bytesRead = readBuffer( _inBuff, to_read ) ;
354  if( bytesRead <= 0 )
355  {
356  string err = "Failed to read data from socket" ;
357  throw BESInternalError( err, __FILE__, __LINE__ ) ;
358  }
359  BESDEBUG( "ppt", "PPTConnect::receive - bytesRead = "
360  << bytesRead << endl ) ;
361 
362  // write the buffer read to the stream
363  _inBuff[bytesRead] = '\0' ;
364  strm.write( _inBuff, bytesRead ) ;
365 
366  // if bytesRead is less than the chunk length, then we need to go get
367  // some more. It doesn't matter what _inBuff_len is, because we need
368  // len bytes to be read and we read bytesRead bytes.
369  if( bytesRead < len )
370  {
371  BESDEBUG( "ppt", "PPTConnect::receive - remaining = "
372  << (len - bytesRead) << endl ) ;
373  receive( strm, len - bytesRead ) ;
374  }
375 }
376 
387 void
388 PPTConnection::read_extensions( map<string,string> &extensions, const string &xstr )
389 {
390  // extensions are in the form var[=val]; There is always a semicolon at the end
391  // if there is no equal sign then there is no value.
392 
393  string var ;
394  string val ;
395  int index = 0 ;
396  bool done = false ;
397  while( !done )
398  {
399  string::size_type semi = xstr.find( ';', index ) ;
400  if( semi == string::npos )
401  {
402  string err = "malformed extensions "
403  + xstr.substr( index, xstr.length() - index )
404  + ", missing semicolon" ;
405  throw BESInternalError( err, __FILE__, __LINE__ ) ;
406  }
407  string::size_type eq = xstr.find( '=', index ) ;
408  if( eq == string::npos || eq > semi )
409  {
410  // there is no value for this variable
411  var = xstr.substr( index, semi-index ) ;
412  extensions[var] = "" ;
413  }
414  else if( eq == semi-1 )
415  {
416  string err = "malformed extensions "
417  + xstr.substr( index, xstr.length() - index )
418  + ", missing value after =" ;
419  throw BESInternalError( err, __FILE__, __LINE__ ) ;
420  }
421  else
422  {
423  var = xstr.substr( index, eq-index ) ;
424  val = xstr.substr( eq+1, semi-eq-1 ) ;
425  extensions[var] = val ;
426  }
427  index = semi+1 ;
428  if( index >= xstr.length() )
429  {
430  done = true ;
431  }
432  }
433 }
434 
445 int
447  const unsigned int buffer_size )
448 {
449  struct pollfd p ;
450  p.fd = getSocket()->getSocketDescriptor();
451  p.events = POLLIN ;
452  struct pollfd arr[1] ;
453  arr[0] = p ;
454 
455  // Lets loop _timeout times with a delay block on poll of 1000 milliseconds
456  // and see if there is any data.
457  for( int j = 0; j < _timeout; j++ )
458  {
459  if( poll( arr, 1, 1000 ) < 0 )
460  {
461  string error( "poll error" ) ;
462  const char* error_info = strerror( errno ) ;
463  if( error_info )
464  error += " " + (string)error_info ;
465  throw BESInternalError( error, __FILE__, __LINE__ ) ;
466  }
467  else
468  {
469  if (arr[0].revents==POLLIN)
470  {
471  return readBuffer( inBuff, buffer_size ) ;
472  }
473  else
474  {
475  cout << " " << j << flush ;
476  }
477  }
478  }
479  cout << endl ;
480  return -1 ;
481 }
482 
483 unsigned int
485 {
487 }
488 
489 unsigned int
491 {
493 }
494 
501 void
502 PPTConnection::dump( ostream &strm ) const
503 {
504  strm << BESIndent::LMarg << "PPTConnection::dump - ("
505  << (void *)this << ")" << endl ;
507  Connection::dump( strm ) ;
509 }
510 
virtual int readBufferNonBlocking(char *inBuff, const unsigned int buff_size)
read a buffer of data from the socket without blocking
exception thrown if inernal error encountered
virtual unsigned int getRecvBufferSize()=0
#define PPT_CHUNK_HEADER_SPACE
Definition: PPTConnection.h:41
virtual void dump(ostream &strm) const
dumps information about this object
Definition: Connection.cc:42
ostream * _out
Definition: Connection.h:51
virtual ~PPTConnection()
static void Indent()
Definition: BESIndent.cc:38
virtual void dump(ostream &strm) const
dumps information about this object
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()
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:70
#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
virtual void sync()
Definition: Socket.cc:124