bes  Updated for version 3.20.10
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 "config.h"
34 
35 #include <poll.h>
36 
37 #include <cerrno>
38 #include <cstring>
39 #include <iostream>
40 #include <sstream>
41 #include <iomanip>
42 #include <map>
43 
44 #include "PPTConnection.h"
45 #include "PPTProtocolNames.h"
46 #include "Socket.h"
47 #include "BESDebug.h"
48 #include "BESInternalError.h"
49 
50 using std::cout;
51 using std::cerr;
52 using std::endl;
53 using std::flush;
54 using std::ostringstream;
55 using std::istringstream;
56 using std::hex;
57 using std::setw;
58 using std::setfill;
59 using std::map;
60 using std::ostream;
61 using std::string;
62 
63 
64 #define MODULE "ppt"
65 #define prolog string("PPTConnection::").append(__func__).append("() - ")
66 
67 PPTConnection::~PPTConnection()
68 {
69  if (_inBuff) {
70  delete[] _inBuff;
71  _inBuff = 0;
72  }
73 }
74 
109 void PPTConnection::send(const string &buffer, map<string, string> &extensions)
110 {
111  if (!buffer.empty()) {
112  sendChunk(buffer, extensions);
113 
114  // send the last chunk without the extensions
115  map<string, string> no_extensions;
116  sendChunk("", no_extensions);
117  }
118  else {
119  sendChunk("", extensions);
120  }
121 }
122 
126 {
127  map<string, string> extensions;
128  extensions["status"] = PPT_EXIT_NOW;
129  send("", extensions);
130 
131  // Need to send a zero-length chunk here
132  extensions.clear();
133  sendChunk("", extensions);
134 }
135 
144 void PPTConnection::sendChunk(const string &buffer, map<string, string> &extensions)
145 {
146  ostringstream strm;
147  if (extensions.size()) {
148  sendExtensions(extensions);
149  }
150  strm << hex << setw(7) << setfill('0') << buffer.length() << "d";
151  if (!buffer.empty()) {
152  strm << buffer;
153  }
154  string toSend = strm.str();
155  send(toSend);
156 }
157 
162 void PPTConnection::sendExtensions(map<string, string> &extensions)
163 {
164  ostringstream strm;
165  if (extensions.size()) {
166  ostringstream estrm;
167  map<string, string>::const_iterator i = extensions.begin();
168  map<string, string>::const_iterator ie = extensions.end();
169  for (; i != ie; i++) {
170  estrm << (*i).first;
171  string value = (*i).second;
172  if (!value.empty()) {
173  estrm << "=" << value;
174  }
175  estrm << ";";
176  }
177  string xstr = estrm.str();
178  strm << hex << setw(7) << setfill('0') << xstr.length() << "x" << xstr;
179  string toSend = strm.str();
180  send(toSend);
181  }
182 }
183 
190 void PPTConnection::send(const string &buffer)
191 {
192  BESDEBUG(MODULE, prolog << "Sending " << buffer << endl);
193  _mySock->send(buffer, 0, buffer.length());
194 
195 #if 0
196  // was calling fsync() which is not defined for sockets. There might be some
197  // 'sync' operation needed in the future, but for now this is an empty call. removed
198  // jhrg 5/5/11
199  _mySock->sync();
200 #endif
201 }
202 
209 int PPTConnection::readBuffer(char *buffer, const unsigned int buffer_size)
210 {
211  return _mySock->receive(buffer, buffer_size);
212 }
213 
214 int PPTConnection::readChunkHeader(char *buffer, /*unsigned */int buffer_size)
215 {
216  char *temp_buffer = buffer;
217  int totalBytesRead = 0;
218  bool done = false;
219  while (!done) {
220  int bytesRead = readBuffer(temp_buffer, buffer_size);
221  BESDEBUG( MODULE, prolog << "Read " << bytesRead << " bytes" << endl );
222  // change: this what < 0 but it can never be < 0 because Socket::receive()
223  // will throw a BESInternalError. However, 0 indicates EOF. Note that if
224  // the read(2) call in Socket::receive() returns without reading any bytes,
225  // that code will call read(2) again. jhrg 3/4/14
226  if (bytesRead == 0) {
227  return bytesRead;
228  }
229  if (bytesRead < buffer_size) {
230  buffer_size = buffer_size - bytesRead;
231  temp_buffer = temp_buffer + bytesRead;
232  totalBytesRead += bytesRead;
233  }
234  else {
235  totalBytesRead += bytesRead;
236  done = true;
237  }
238  }
239  buffer[totalBytesRead] = '\0';
240  return totalBytesRead;
241 }
242 
258 bool PPTConnection::receive(map<string, string> &extensions, ostream *strm)
259 {
260  ostream *use_strm = _out;
261  if (strm) 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( MODULE, prolog << "buffer size = " << _inBuff_len << endl );
266  if (!_inBuff) {
267  _inBuff_len = _mySock->getRecvBufferSize() + 1;
268  _inBuff = new char[_inBuff_len + 1];
269  }
270 
271  // The first buffer will contain the length of the chunk at the beginning.
272  // read the first 8 bytes. The first 7 are the length and the next 1
273  // if x then extensions follow, if d then data follows.
274  int bytesRead = readChunkHeader(_inBuff, 8);
275  BESDEBUG( MODULE, prolog << "Reading header, read " << bytesRead << " bytes" << endl );
276  if (bytesRead != 8)
277  throw BESInternalError(prolog + "Failed to read chunk header", __FILE__, __LINE__);
278 
279  char lenbuffer[8];
280  lenbuffer[0] = _inBuff[0];
281  lenbuffer[1] = _inBuff[1];
282  lenbuffer[2] = _inBuff[2];
283  lenbuffer[3] = _inBuff[3];
284  lenbuffer[4] = _inBuff[4];
285  lenbuffer[5] = _inBuff[5];
286  lenbuffer[6] = _inBuff[6];
287  lenbuffer[7] = '\0';
288  istringstream lenstrm(lenbuffer);
289  unsigned long inlen = 0;
290  lenstrm >> hex >> setw(7) >> inlen;
291  BESDEBUG( MODULE, prolog << "Reading header, chunk length = " << inlen << endl );
292  BESDEBUG( MODULE, prolog << "Reading header, chunk type = " << _inBuff[7] << endl );
293 
294  if (_inBuff[7] == 'x') {
295  ostringstream xstrm;
296  receive(xstrm, inlen);
297  read_extensions(extensions, xstrm.str());
298  }
299  else if (_inBuff[7] == 'd') {
300  if (!inlen) {
301  // we've received the last chunk, return true, there
302  // is nothing more to read from the socket
303  return true;
304  }
305  receive(*use_strm, inlen);
306  }
307  else {
308  string err = (string) "type of data is " + _inBuff[7] + ", should be x for extensions or d for data";
309  throw BESInternalError(err, __FILE__, __LINE__);
310  }
311 
312  return false;
313 }
314 
324 void PPTConnection::receive(ostream &strm, const /* unsigned */int len)
325 {
326  BESDEBUG( MODULE, prolog << "len = " << len << endl );
327  if( !_inBuff )
328  {
329  string err = "buffer has not been initialized";
330  throw BESInternalError( err, __FILE__, __LINE__ );
331  }
332 
333  /* unsigned */int to_read = len;
334  if( len > _inBuff_len )
335  {
336  to_read = _inBuff_len;
337  }
338  BESDEBUG( MODULE, prolog << "to_read = " << to_read << endl );
339 
340  // read a buffer
341  int bytesRead = readBuffer( _inBuff, to_read );
342  if( bytesRead <= 0 )
343  {
344  string err = "Failed to read data from socket";
345  throw BESInternalError( err, __FILE__, __LINE__ );
346  }
347  BESDEBUG( MODULE, prolog << "bytesRead = " << bytesRead << endl );
348 
349  // write the buffer read to the stream
350  _inBuff[bytesRead] = '\0';
351  strm.write( _inBuff, bytesRead );
352 
353  // if bytesRead is less than the chunk length, then we need to go get
354  // some more. It doesn't matter what _inBuff_len is, because we need
355  // len bytes to be read and we read bytesRead bytes.
356  if( bytesRead < len )
357  {
358  BESDEBUG( MODULE, prolog << "remaining = " << (len - bytesRead) << endl );
359  receive( strm, len - bytesRead );
360  }
361  }
362 
373 void PPTConnection::read_extensions(map<string, string> &extensions, const string &xstr)
374 {
375  // extensions are in the form var[=val]; There is always a semicolon at the end
376  // if there is no equal sign then there is no value.
377 
378  string var;
379  string val;
380  unsigned int index = 0;
381  bool done = false;
382  while (!done) {
383  string::size_type semi = xstr.find(';', index);
384  if (semi == string::npos) {
385  string err = "malformed extensions " + xstr.substr(index, xstr.length() - index) + ", missing semicolon";
386  throw BESInternalError(err, __FILE__, __LINE__);
387  }
388  string::size_type eq = xstr.find('=', index);
389  if (eq == string::npos || eq > semi) {
390  // there is no value for this variable
391  var = xstr.substr(index, semi - index);
392  extensions[var] = "";
393  }
394  else if (eq == semi - 1) {
395  string err = "malformed extensions " + xstr.substr(index, xstr.length() - index)
396  + ", missing value after =";
397  throw BESInternalError(err, __FILE__, __LINE__);
398  }
399  else {
400  var = xstr.substr(index, eq - index);
401  val = xstr.substr(eq + 1, semi - eq - 1);
402  extensions[var] = val;
403  }
404  index = semi + 1;
405  if (index >= xstr.length()) {
406  done = true;
407  }
408  }
409 }
410 
421 int PPTConnection::readBufferNonBlocking(char *inBuff, const /* unsigned*/int buffer_size)
422 {
423  struct pollfd arr[1];
424  arr[0].fd = getSocket()->getSocketDescriptor();
425  arr[0].events = POLLIN;
426  arr[0].revents = 0;
427 #if 0
428  struct pollfd p = {};
429  p.fd = getSocket()->getSocketDescriptor();
430  p.events = POLLIN;
431  struct pollfd arr[1];
432  arr[0] = p;
433 #endif
434  // Lets loop _timeout times with a delay block on poll of 1000 milliseconds
435  // and see if there are any data.
436  for (int j = 0; j < _timeout; j++) {
437  if (poll(arr, 1, 1000) < 0) {
438  // Allow this call to be interrupted without it being an error. jhrg 6/15/11
439  if (errno == EINTR || errno == EAGAIN) continue;
440 
441  throw BESInternalError(string("poll error") + " " + strerror(errno), __FILE__, __LINE__);
442  }
443  else {
444  if (arr[0].revents == POLLIN) {
445  return readBuffer(inBuff, buffer_size);
446  }
447  else {
448  cout << " " << j << flush;
449  }
450  }
451  }
452  cout << endl;
453  return -1;
454 }
455 
456 unsigned int PPTConnection::getRecvChunkSize()
457 {
458  return _mySock->getRecvBufferSize() - PPT_CHUNK_HEADER_SPACE;
459 }
460 
461 unsigned int PPTConnection::getSendChunkSize()
462 {
463  return _mySock->getSendBufferSize() - PPT_CHUNK_HEADER_SPACE;
464 }
465 
472 void PPTConnection::dump(ostream &strm) const
473 {
474  strm << BESIndent::LMarg << "PPTConnection::dump - (" << (void *) this << ")" << endl;
475  BESIndent::Indent();
476  Connection::dump(strm);
477  BESIndent::UnIndent();
478 }
479 
exception thrown if internal error encountered
virtual void dump(std::ostream &strm) const
dumps information about this object
Definition: Connection.cc:44
virtual void sendExit()
Send the exit token as an extension.
virtual int readBuffer(char *inBuff, const unsigned int buff_size)
read a buffer of data from the socket
virtual void sendExtensions(std::map< std::string, std::string > &extensions)
send the specified extensions
virtual void send(const std::string &buffer)
sends the buffer to the socket
virtual void read_extensions(std::map< std::string, std::string > &extensions, const std::string &xstr)
the string passed are extensions, read them and store the name/value pairs into the passed map
virtual int readBufferNonBlocking(char *inBuff, const int buff_size)
read a buffer of data from the socket without blocking
virtual void dump(std::ostream &strm) const
dumps information about this object