bes  Updated for version 3.19.1
PPTConnection.cc
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 
57 PPTConnection::~PPTConnection()
58 {
59  if (_inBuff) {
60  delete[] _inBuff;
61  _inBuff = 0;
62  }
63 }
64 
99 void PPTConnection::send(const string &buffer, map<string, string> &extensions)
100 {
101  if (!buffer.empty()) {
102  sendChunk(buffer, extensions);
103 
104  // send the last chunk without the extensions
105  map<string, string> no_extensions;
106  sendChunk("", no_extensions);
107  }
108  else {
109  sendChunk("", extensions);
110  }
111 }
112 
116 {
117  map<string, string> extensions;
118  extensions["status"] = PPTProtocol::PPT_EXIT_NOW;
119  send("", extensions);
120 
121  // Need to send a zero-length chunk here
122  extensions.clear();
123  sendChunk("", extensions);
124 }
125 
134 void PPTConnection::sendChunk(const string &buffer, map<string, string> &extensions)
135 {
136  ostringstream strm;
137  if (extensions.size()) {
138  sendExtensions(extensions);
139  }
140  strm << hex << setw(7) << setfill('0') << buffer.length() << "d";
141  if (!buffer.empty()) {
142  strm << buffer;
143  }
144  string toSend = strm.str();
145  send(toSend);
146 }
147 
152 void PPTConnection::sendExtensions(map<string, string> &extensions)
153 {
154  ostringstream strm;
155  if (extensions.size()) {
156  ostringstream estrm;
157  map<string, string>::const_iterator i = extensions.begin();
158  map<string, string>::const_iterator ie = extensions.end();
159  for (; i != ie; i++) {
160  estrm << (*i).first;
161  string value = (*i).second;
162  if (!value.empty()) {
163  estrm << "=" << value;
164  }
165  estrm << ";";
166  }
167  string xstr = estrm.str();
168  strm << hex << setw(7) << setfill('0') << xstr.length() << "x" << xstr;
169  string toSend = strm.str();
170  send(toSend);
171  }
172 }
173 
180 void PPTConnection::send(const string &buffer)
181 {
182  BESDEBUG("ppt", "PPTConnection::send - sending " << buffer << endl);
183 
184  _mySock->send(buffer, 0, buffer.length());
185 
186 #if 0
187  // was calling fsync() which is not defined for sockets. There might be some
188  // 'sync' operation needed in the future, but for now this is an empty call. removed
189  // jhrg 5/5/11
190  _mySock->sync();
191 #endif
192 }
193 
200 int PPTConnection::readBuffer(char *buffer, const unsigned int buffer_size)
201 {
202  return _mySock->receive(buffer, buffer_size);
203 }
204 
205 int PPTConnection::readChunkHeader(char *buffer, /*unsigned */int buffer_size)
206 {
207  char *temp_buffer = buffer;
208  int totalBytesRead = 0;
209  bool done = false;
210  while (!done) {
211  int bytesRead = readBuffer(temp_buffer, buffer_size);
212  BESDEBUG( "ppt", "PPTConnection::readChunkHeader - read " << bytesRead << " bytes" << endl );
213  // change: this what < 0 but it can never be < 0 because Socket::receive()
214  // will throw a BESInternalError. However, 0 indicates EOF. Note that if
215  // the read(2) call in Socket::receive() returns without reading any bytes,
216  // that code will call read(2) again. jhrg 3/4/14
217  if (bytesRead == 0) {
218  return bytesRead;
219  }
220  if (bytesRead < buffer_size) {
221  buffer_size = buffer_size - bytesRead;
222  temp_buffer = temp_buffer + bytesRead;
223  totalBytesRead += bytesRead;
224  }
225  else {
226  totalBytesRead += bytesRead;
227  done = true;
228  }
229  }
230  buffer[totalBytesRead] = '\0';
231  return totalBytesRead;
232 }
233 
249 bool PPTConnection::receive(map<string, string> &extensions, ostream *strm)
250 {
251  ostream *use_strm = _out;
252  if (strm) use_strm = strm;
253 
254  // If the receive buffer has not yet been created, get the receive size
255  // and create the buffer.
256  BESDEBUG( "ppt", "PPTConnection::receive: buffer size = " << _inBuff_len << endl );
257  if (!_inBuff) {
258  _inBuff_len = _mySock->getRecvBufferSize() + 1;
259  _inBuff = new char[_inBuff_len + 1];
260  }
261 
262  // The first buffer will contain the length of the chunk at the beginning.
263  // read the first 8 bytes. The first 7 are the length and the next 1
264  // if x then extensions follow, if d then data follows.
265  int bytesRead = readChunkHeader(_inBuff, 8);
266  BESDEBUG( "ppt", "Reading header, read " << bytesRead << " bytes" << endl );
267  if (bytesRead != 8)
268  throw BESInternalError("Failed to read chunk header", __FILE__, __LINE__);
269 
270  char lenbuffer[8];
271  lenbuffer[0] = _inBuff[0];
272  lenbuffer[1] = _inBuff[1];
273  lenbuffer[2] = _inBuff[2];
274  lenbuffer[3] = _inBuff[3];
275  lenbuffer[4] = _inBuff[4];
276  lenbuffer[5] = _inBuff[5];
277  lenbuffer[6] = _inBuff[6];
278  lenbuffer[7] = '\0';
279  istringstream lenstrm(lenbuffer);
280  unsigned long inlen = 0;
281  lenstrm >> hex >> setw(7) >> inlen;
282  BESDEBUG( "ppt", "Reading header, chunk length = " << inlen << endl );
283  BESDEBUG( "ppt", "Reading header, chunk type = " << _inBuff[7] << endl );
284 
285  if (_inBuff[7] == 'x') {
286  ostringstream xstrm;
287  receive(xstrm, inlen);
288  read_extensions(extensions, xstrm.str());
289  }
290  else if (_inBuff[7] == 'd') {
291  if (!inlen) {
292  // we've received the last chunk, return true, there
293  // is nothing more to read from the socket
294  return true;
295  }
296  receive(*use_strm, inlen);
297  }
298  else {
299  string err = (string) "type of data is " + _inBuff[7] + ", should be x for extensions or d for data";
300  throw BESInternalError(err, __FILE__, __LINE__);
301  }
302 
303  return false;
304 }
305 
315 void PPTConnection::receive(ostream &strm, const /* unsigned */int len)
316 {
317  BESDEBUG( "ppt", "PPTConnect::receive - len = " << len << endl );
318  if( !_inBuff )
319  {
320  string err = "buffer has not been initialized";
321  throw BESInternalError( err, __FILE__, __LINE__ );
322  }
323 
324  /* unsigned */int to_read = len;
325  if( len > _inBuff_len )
326  {
327  to_read = _inBuff_len;
328  }
329  BESDEBUG( "ppt", "PPTConnect::receive - to_read = " << to_read << endl );
330 
331  // read a buffer
332  int bytesRead = readBuffer( _inBuff, to_read );
333  if( bytesRead <= 0 )
334  {
335  string err = "Failed to read data from socket";
336  throw BESInternalError( err, __FILE__, __LINE__ );
337  }
338  BESDEBUG( "ppt", "PPTConnect::receive - bytesRead = "
339  << bytesRead << endl );
340 
341  // write the buffer read to the stream
342  _inBuff[bytesRead] = '\0';
343  strm.write( _inBuff, bytesRead );
344 
345  // if bytesRead is less than the chunk length, then we need to go get
346  // some more. It doesn't matter what _inBuff_len is, because we need
347  // len bytes to be read and we read bytesRead bytes.
348  if( bytesRead < len )
349  {
350  BESDEBUG( "ppt", "PPTConnect::receive - remaining = "
351  << (len - bytesRead) << endl );
352  receive( strm, len - bytesRead );
353  }
354  }
355 
366 void PPTConnection::read_extensions(map<string, string> &extensions, const string &xstr)
367 {
368  // extensions are in the form var[=val]; There is always a semicolon at the end
369  // if there is no equal sign then there is no value.
370 
371  string var;
372  string val;
373  unsigned int index = 0;
374  bool done = false;
375  while (!done) {
376  string::size_type semi = xstr.find(';', index);
377  if (semi == string::npos) {
378  string err = "malformed extensions " + xstr.substr(index, xstr.length() - index) + ", missing semicolon";
379  throw BESInternalError(err, __FILE__, __LINE__);
380  }
381  string::size_type eq = xstr.find('=', index);
382  if (eq == string::npos || eq > semi) {
383  // there is no value for this variable
384  var = xstr.substr(index, semi - index);
385  extensions[var] = "";
386  }
387  else if (eq == semi - 1) {
388  string err = "malformed extensions " + xstr.substr(index, xstr.length() - index)
389  + ", missing value after =";
390  throw BESInternalError(err, __FILE__, __LINE__);
391  }
392  else {
393  var = xstr.substr(index, eq - index);
394  val = xstr.substr(eq + 1, semi - eq - 1);
395  extensions[var] = val;
396  }
397  index = semi + 1;
398  if (index >= xstr.length()) {
399  done = true;
400  }
401  }
402 }
403 
414 int PPTConnection::readBufferNonBlocking(char *inBuff, const /* unsigned*/int buffer_size)
415 {
416  struct pollfd arr[1];
417  arr[0].fd = getSocket()->getSocketDescriptor();
418  arr[0].events = POLLIN;
419  arr[0].revents = 0;
420 #if 0
421  struct pollfd p = {};
422  p.fd = getSocket()->getSocketDescriptor();
423  p.events = POLLIN;
424  struct pollfd arr[1];
425  arr[0] = p;
426 #endif
427  // Lets loop _timeout times with a delay block on poll of 1000 milliseconds
428  // and see if there are any data.
429  for (int j = 0; j < _timeout; j++) {
430  if (poll(arr, 1, 1000) < 0) {
431  // Allow this call to be interrupted without it being an error. jhrg 6/15/11
432  if (errno == EINTR || errno == EAGAIN) continue;
433 
434  throw BESInternalError(string("poll error") + " " + strerror(errno), __FILE__, __LINE__);
435  }
436  else {
437  if (arr[0].revents == POLLIN) {
438  return readBuffer(inBuff, buffer_size);
439  }
440  else {
441  cout << " " << j << flush;
442  }
443  }
444  }
445  cout << endl;
446  return -1;
447 }
448 
449 unsigned int PPTConnection::getRecvChunkSize()
450 {
451  return _mySock->getRecvBufferSize() - PPT_CHUNK_HEADER_SPACE;
452 }
453 
454 unsigned int PPTConnection::getSendChunkSize()
455 {
456  return _mySock->getSendBufferSize() - PPT_CHUNK_HEADER_SPACE;
457 }
458 
465 void PPTConnection::dump(ostream &strm) const
466 {
467  strm << BESIndent::LMarg << "PPTConnection::dump - (" << (void *) this << ")" << endl;
468  BESIndent::Indent();
469  Connection::dump(strm);
470  BESIndent::UnIndent();
471 }
472 
exception thrown if inernal error encountered
virtual void dump(ostream &strm) const
dumps information about this object
Definition: Connection.cc:41
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
virtual void sendExtensions(map< string, string > &extensions)
send the specified extensions
virtual int readBuffer(char *inBuff, const unsigned int buff_size)
read a buffer of data from the socket
virtual int readBufferNonBlocking(char *inBuff, const int buff_size)
read a buffer of data from the socket without blocking
virtual void send(const string &buffer)
sends the buffer to the socket