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