bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
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 "BESLog.h"
48#include "BESDebug.h"
49#include "BESInternalError.h"
50
51using std::cout;
52using std::cerr;
53using std::endl;
54using std::flush;
55using std::ostringstream;
56using std::istringstream;
57using std::hex;
58using std::setw;
59using std::setfill;
60using std::map;
61using std::ostream;
62using std::string;
63
64
65#define MODULE "ppt"
66#define prolog string("PPTConnection::").append(__func__).append("() - ")
67
68PPTConnection::~PPTConnection()
69{
70 if (_inBuff) {
71 delete[] _inBuff;
72 }
73}
74
109void 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
144void 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.size() << "d";
151 if (!buffer.empty()) {
152 strm << buffer;
153 }
154 string toSend = strm.str();
155 send(toSend);
156}
157
162void 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.size() << "x" << xstr;
179 string toSend = strm.str();
180 send(toSend);
181 }
182}
183
190void PPTConnection::send(const string &buffer)
191{
192 BESDEBUG(MODULE, prolog << "Sending " << buffer << endl);
193 _mySock->send(buffer, 0, buffer.size());
194}
195
202int PPTConnection::readBuffer(char *buffer, const unsigned int buffer_size)
203{
204 return _mySock->receive(buffer, buffer_size);
205}
206
207int PPTConnection::readChunkHeader(char *buffer, /*unsigned */int buffer_size)
208{
209 char *temp_buffer = buffer;
210 int totalBytesRead = 0;
211 bool done = false;
212 while (!done) {
213 int bytesRead = readBuffer(temp_buffer, buffer_size);
214 BESDEBUG( MODULE, prolog << "Read " << bytesRead << " bytes" << endl );
215 // change: this what < 0 but it can never be < 0 because Socket::receive()
216 // will throw a BESInternalError. However, 0 indicates EOF. Note that if
217 // the read(2) call in Socket::receive() returns without reading any bytes,
218 // that code will call read(2) again. jhrg 3/4/14
219 if (bytesRead == 0) {
220 return bytesRead;
221 }
222 if (bytesRead < buffer_size) {
223 buffer_size = buffer_size - bytesRead;
224 temp_buffer = temp_buffer + bytesRead;
225 totalBytesRead += bytesRead;
226 }
227 else {
228 totalBytesRead += bytesRead;
229 done = true;
230 }
231 }
232 buffer[totalBytesRead] = '\0';
233 return totalBytesRead;
234}
235
251bool PPTConnection::receive(map<string, string> &extensions, ostream *strm)
252{
253 ostream *use_strm = _out;
254 if (strm) use_strm = strm;
255
256 // If the receive buffer has not yet been created, get the receive size
257 // and create the buffer.
258 BESDEBUG( MODULE, prolog << "buffer size = " << _inBuff_len << endl );
259 if (!_inBuff) {
260 _inBuff_len = _mySock->getRecvBufferSize() + 1;
261 _inBuff = new char[_inBuff_len + 1];
262 }
263
264 // The first buffer will contain the length of the chunk at the beginning.
265 // read the first 8 bytes. The first 7 are the length and the next 1
266 // if x then extensions follow, if d then data follows.
267 int bytesRead = readChunkHeader(_inBuff, 8);
268 BESDEBUG( MODULE, prolog << "Reading header, read " << bytesRead << " bytes" << endl );
269 if (bytesRead == 0) {
270 INFO_LOG("PPTConnection::receive: read EOF from the OLFS, beslistener exiting.\n");
271 // Since this is the exit condition, set extensions["status"] so that it's value is PPT_EXIT_NOW.
272 // See BESServerHandler::execute(Connection *connection) and note that Connection::exit()
273 // returns a string value (!). It does not do what unix exit() does! This is how the server
274 // knows to exit. jhrg 5/30/24
275 // Note that when this child listener exits, it will use exit code '6' and that will be picked up
276 // by the master listener. That will show up in the bes.log. jhrg 5/30/24
277 extensions["status"] = PPT_EXIT_NOW;
278 return true;
279 }
280 else if (bytesRead != 8) {
281 throw BESInternalError(prolog + "Failed to read chunk header", __FILE__, __LINE__);
282 }
283
284 char lenbuffer[8];
285 lenbuffer[0] = _inBuff[0];
286 lenbuffer[1] = _inBuff[1];
287 lenbuffer[2] = _inBuff[2];
288 lenbuffer[3] = _inBuff[3];
289 lenbuffer[4] = _inBuff[4];
290 lenbuffer[5] = _inBuff[5];
291 lenbuffer[6] = _inBuff[6];
292 lenbuffer[7] = '\0';
293 istringstream lenstrm(lenbuffer);
294 unsigned long inlen = 0;
295 lenstrm >> hex >> setw(7) >> inlen;
296 BESDEBUG( MODULE, prolog << "Reading header, chunk length = " << inlen << endl );
297 BESDEBUG( MODULE, prolog << "Reading header, chunk type = " << _inBuff[7] << endl );
298
299 if (_inBuff[7] == 'x') {
300 ostringstream xstrm;
301 receive(xstrm, inlen);
302 read_extensions(extensions, xstrm.str());
303 }
304 else if (_inBuff[7] == 'd') {
305 if (!inlen) {
306 // we've received the last chunk, return true, there
307 // is nothing more to read from the socket
308 return true;
309 }
310 receive(*use_strm, inlen);
311 }
312 else {
313 string err = (string) "type of data is " + _inBuff[7] + ", should be x for extensions or d for data";
314 throw BESInternalError(err, __FILE__, __LINE__);
315 }
316
317 return false;
318}
319
329void PPTConnection::receive(ostream &strm, const /* unsigned */int len)
330{
331 BESDEBUG( MODULE, prolog << "len = " << len << endl );
332 if( !_inBuff )
333 {
334 string err = "buffer has not been initialized";
335 throw BESInternalError( err, __FILE__, __LINE__ );
336 }
337
338 /* unsigned */int to_read = len;
339 if( len > _inBuff_len )
340 {
341 to_read = _inBuff_len;
342 }
343 BESDEBUG( MODULE, prolog << "to_read = " << to_read << endl );
344
345 // read a buffer
346 int bytesRead = readBuffer( _inBuff, to_read );
347 if( bytesRead <= 0 )
348 {
349 string err = "Failed to read data from socket";
350 throw BESInternalError( err, __FILE__, __LINE__ );
351 }
352 BESDEBUG( MODULE, prolog << "bytesRead = " << bytesRead << endl );
353
354 // write the buffer read to the stream
355 _inBuff[bytesRead] = '\0';
356 strm.write( _inBuff, bytesRead );
357
358 // if bytesRead is less than the chunk length, then we need to go get
359 // some more. It doesn't matter what _inBuff_len is, because we need
360 // len bytes to be read and we read bytesRead bytes.
361 if( bytesRead < len )
362 {
363 BESDEBUG( MODULE, prolog << "remaining = " << (len - bytesRead) << endl );
364 receive( strm, len - bytesRead );
365 }
366 }
367
378void PPTConnection::read_extensions(map<string, string> &extensions, const string &xstr)
379{
380 // extensions are in the form var[=val]; There is always a semicolon at the end
381 // if there is no equal sign then there is no value.
382
383 string var;
384 string val;
385 unsigned int index = 0;
386 bool done = false;
387 while (!done) {
388 string::size_type semi = xstr.find(';', index);
389 if (semi == string::npos) {
390 string err = "malformed extensions " + xstr.substr(index, xstr.size() - index) + ", missing semicolon";
391 throw BESInternalError(err, __FILE__, __LINE__);
392 }
393 string::size_type eq = xstr.find('=', index);
394 if (eq == string::npos || eq > semi) {
395 // there is no value for this variable
396 var = xstr.substr(index, semi - index);
397 extensions[var] = "";
398 }
399 else if (eq == semi - 1) {
400 string err = "malformed extensions " + xstr.substr(index, xstr.size() - index)
401 + ", missing value after =";
402 throw BESInternalError(err, __FILE__, __LINE__);
403 }
404 else {
405 var = xstr.substr(index, eq - index);
406 val = xstr.substr(eq + 1, semi - eq - 1);
407 extensions[var] = val;
408 }
409 index = semi + 1;
410 if (index >= xstr.size()) {
411 done = true;
412 }
413 }
414}
415
426int PPTConnection::readBufferNonBlocking(char *inBuff, const /* unsigned*/int buffer_size)
427{
428 struct pollfd arr[1];
429 arr[0].fd = getSocket()->getSocketDescriptor();
430 arr[0].events = POLLIN;
431 arr[0].revents = 0;
432
433 // Let's loop _timeout times with a delay block on poll of 1000 milliseconds
434 // and see if there are any data.
435 for (int j = 0; j < _timeout; j++) {
436 if (poll(arr, 1, 1000) < 0) {
437 // Allow this call to be interrupted without it being an error. jhrg 6/15/11
438 if (errno == EINTR || errno == EAGAIN) continue;
439
440 throw BESInternalError(string("poll error") + " " + strerror(errno), __FILE__, __LINE__);
441 }
442 else {
443 if (arr[0].revents == POLLIN) {
444 return readBuffer(inBuff, buffer_size);
445 }
446 else {
447 cout << " " << j << flush;
448 }
449 }
450 }
451 cout << endl;
452 return -1;
453}
454
455unsigned int PPTConnection::getRecvChunkSize()
456{
457 return _mySock->getRecvBufferSize() - PPT_CHUNK_HEADER_SPACE;
458}
459
460unsigned int PPTConnection::getSendChunkSize()
461{
462 return _mySock->getSendBufferSize() - PPT_CHUNK_HEADER_SPACE;
463}
464
471void PPTConnection::dump(ostream &strm) const
472{
473 strm << BESIndent::LMarg << "PPTConnection::dump - (" << (void *) this << ")" << endl;
474 BESIndent::Indent();
475 Connection::dump(strm);
476 BESIndent::UnIndent();
477}
478
exception thrown if internal error encountered
void dump(std::ostream &strm) const override
dumps information about this object
Definition Connection.cc:44
void send(const std::string &buffer) override
sends the buffer to the socket
virtual int readBuffer(char *inBuff, const unsigned int buff_size)
read a buffer of data from the socket
void sendExtensions(std::map< std::string, std::string > &extensions) override
send the specified extensions
void sendExit() override
Send the exit token as an extension.
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
void dump(std::ostream &strm) const override
dumps information about this object
STL iterator class.