libdap Updated for version 3.21.1
libdap4 is an implementation of OPeNDAP's DAP protocol.
D4Connect.cc
Go to the documentation of this file.
1// -*- mode: c++; c-basic-offset:4 -*-
2
3// This file is part of libdap, A C++ implementation of the OPeNDAP Data
4// Access Protocol.
5
6// Copyright (c) 2002,2003 OPeNDAP, Inc.
7// Author: James Gallagher <jgallagher@opendap.org>
8// Dan Holloway <dholloway@gso.uri.edu>
9// Reza Nekovei <reza@intcomm.net>
10//
11// This library is free software; you can redistribute it and/or
12// modify it under the terms of the GNU Lesser General Public
13// License as published by the Free Software Foundation; either
14// version 2.1 of the License, or (at your option) any later version.
15//
16// This library is distributed in the hope that it will be useful,
17// but WITHOUT ANY WARRANTY; without even the implied warranty of
18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19// Lesser General Public License for more details.
20//
21// You should have received a copy of the GNU Lesser General Public
22// License along with this library; if not, write to the Free Software
23// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24//
25// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
26
27// (c) COPYRIGHT URI/MIT 1994-2002
28// Please read the full copyright statement in the file COPYRIGHT_URI.
29//
30// Authors:
31// jhrg,jimg James Gallagher <jgallagher@gso.uri.edu>
32// dan Dan Holloway <dholloway@gso.uri.edu>
33// reza Reza Nekovei <reza@intcomm.net>
34
35#include "config.h"
36// #define DODS_DEBUG 1
37
38#include <cassert>
39
40#include <sstream>
41
42#include "D4Connect.h"
43#include "D4Group.h"
44#include "DMR.h"
45#include "HTTPConnect.h"
46#include "HTTPResponse.h"
47#include "RCReader.h"
48#include "Response.h"
49
50#include "D4ParserSax2.h"
52#include "chunked_istream.h"
53#include "chunked_stream.h"
54
55#include "debug.h"
56#include "escaping.h"
57#include "mime_util.h"
58
59using namespace std;
60
61namespace libdap {
62
65void D4Connect::process_dmr(DMR &dmr, Response &rs) {
66 DBG(cerr << "Entering D4Connect::process_dmr" << endl);
67
68 dmr.set_dap_version(rs.get_protocol());
69
70 DBG(cerr << "Entering process_data. Response.getVersion() = " << rs.get_version() << endl);
71 switch (rs.get_type()) {
72 case dap4_error: {
73#if 0
74 Error e;
75 if (!e.parse(rs.get_stream()))
76 throw InternalErr(__FILE__, __LINE__, "Could not parse the Error object returned by the server!");
77 throw e;
78#endif
79 throw InternalErr(__FILE__, __LINE__, "DAP4 errors not processed yet: FIXME!");
80 }
81
82 case web_error:
83 // Web errors (those reported in the return document's MIME header)
84 // are processed by the WWW library.
85 throw InternalErr(__FILE__, __LINE__,
86 "An error was reported by the remote httpd; this should have been processed by HTTPConnect.");
87
88 case dap4_dmr: {
89 // parse the DMR
90 try {
91 D4ParserSax2 parser;
92 // When parsing a data response, we use the permissive mode of the DMR parser
93 // (which allows Map elements to reference Arrays that are not in the DMR).
94 // Do not use that mode when parsing the DMR response - assume the DMR is
95 // valid. jhrg 4/13/16
96 parser.intern(*rs.get_cpp_stream(), &dmr);
97 } catch (Error &e) {
98 cerr << "Exception: " << e.get_error_message() << endl;
99 return;
100 } catch (std::exception &e) {
101 cerr << "Exception: " << e.what() << endl;
102 return;
103 } catch (...) {
104 cerr << "Exception: unknown error" << endl;
105 return;
106 }
107
108 return;
109 }
110
111 default:
112 throw Error("Unknown response type");
113 }
114}
115
118void D4Connect::process_data(DMR &data, Response &rs) {
119 DBG(cerr << "Entering D4Connect::process_data" << endl);
120
121 assert(rs.get_cpp_stream()); // DAP4 code uses cpp streams
122
123 data.set_dap_version(rs.get_protocol());
124
125 DBG(cerr << "Entering process_data. Response.getVersion() = " << rs.get_version() << endl);
126 switch (rs.get_type()) {
127 case dap4_error: {
128 throw InternalErr(__FILE__, __LINE__, "DAP4 errors not processed yet: FIXME!");
129 }
130
131 case web_error:
132 // Web errors (those reported in the return document's MIME header)
133 // are processed by the WWW library.
134 throw InternalErr(
135 __FILE__, __LINE__,
136 "An error was reported by the remote httpd; this should have been processed by HTTPConnect..");
137
138 case dap4_data: {
139 chunked_istream cis(*(rs.get_cpp_stream()), CHUNK_SIZE);
140 // parse the DMR, stopping when the boundary is found.
141 try {
142 // force chunk read
143 // get chunk size
144 int chunk_size = cis.read_next_chunk();
145 if (chunk_size < 0)
146 throw Error("Found an unexpected end of input (EOF) while reading a DAP4 data response. (1)");
147
148 // get chunk
149 char chunk[chunk_size];
150 cis.read(chunk, chunk_size);
151 // parse char * with given size
152 D4ParserSax2 parser;
153 // permissive mode allows references to Maps that are not in the response.
154 // Use this mode when parsing a data response (but not the DMR). jhrg 4/13/16
155 parser.set_strict(false);
156
157 // '-2' to discard the CRLF pair
158 parser.intern(chunk, chunk_size - 2, &data);
159 } catch (Error &e) {
160 cerr << "Exception: " << e.get_error_message() << endl;
161 return;
162 } catch (std::exception &e) {
163 cerr << "Exception: " << e.what() << endl;
164 return;
165 } catch (...) {
166 cerr << "Exception: unknown error" << endl;
167 return;
168 }
169
170 D4StreamUnMarshaller um(cis, cis.twiddle_bytes());
171 data.root()->deserialize(um, data);
172
173 return;
174 }
175
176 default:
177 throw Error("Unknown response type");
178 }
179}
180
189void D4Connect::parse_mime(Response &rs) {
190 rs.set_version("dods/0.0"); // initial value; for backward compatibility.
191 rs.set_protocol("2.0");
192
193 istream &data_source = *rs.get_cpp_stream();
194 string mime = get_next_mime_header(data_source);
195 while (!mime.empty()) {
196 string header, value;
197 parse_mime_header(mime, header, value);
198
199 // Note that this is an ordered list
200 if (header == "content-description") {
201 DBG(cout << header << ": " << value << endl);
202 rs.set_type(get_description_type(value));
203 }
204 // Use the value of xdods-server only if no other value has been read
205 else if (header == "xdods-server" && rs.get_version() == "dods/0.0") {
206 DBG(cout << header << ": " << value << endl);
207 rs.set_version(value);
208 }
209 // This trumps 'xdods-server' and 'server'
210 else if (header == "xopendap-server") {
211 DBG(cout << header << ": " << value << endl);
212 rs.set_version(value);
213 } else if (header == "xdap") {
214 DBG(cout << header << ": " << value << endl);
215 rs.set_protocol(value);
216 }
217 // Only look for 'server' if no other header supplies this info.
218 else if (rs.get_version() == "dods/0.0" && header == "server") {
219 DBG(cout << header << ": " << value << endl);
220 rs.set_version(value);
221 }
222
223 mime = get_next_mime_header(data_source);
224 }
225}
226
227// public mfuncs
228
235D4Connect::D4Connect(const string &url, string uname, string password)
236 : d_http(0), d_local(false), d_URL(""), d_UrlQueryString(""), d_server("unknown"), d_protocol("4.0") {
237 string name = prune_spaces(url);
238
239 // Figure out if the URL starts with 'http', if so, make sure that we
240 // talk to an instance of HTTPConnect.
241 if (name.find("http") == 0) {
242 DBG(cerr << "Connect: The identifier is an http URL" << endl);
243 d_http = new HTTPConnect(RCReader::instance());
244 d_http->set_use_cpp_streams(true);
245
246 d_URL = name;
247
248 // Find and store any CE given with the URL.
249 string::size_type dotpos = name.find('?');
250 if (dotpos != std::string::npos) { // Found a match.
251 d_URL = name.substr(0, dotpos);
252
253 d_UrlQueryString = name.substr(dotpos + 1);
254
255 if (d_UrlQueryString.find(DAP4_CE_QUERY_KEY) != std::string::npos) {
256 std::stringstream msg;
257 msg << endl;
258 msg << "WARNING: A DAP4 constraint expression key was found in the query string!" << endl;
259 msg << "The submitted dataset URL: " << name << endl;
260 msg << "Contains the query string: " << d_UrlQueryString << endl;
261 msg << "This will cause issues when making DAP4 requests that specify additional constraints. " << endl;
262 cerr << msg.str() << endl;
263 // throw Error(malformed_expr, msg.str());
264 }
265 }
266 } else {
267 DBG(cerr << "Connect: The identifier is a local data source." << endl);
268 d_local = true; // local in this case means non-DAP
269 }
270
271 set_credentials(uname, password);
272}
273
275 if (d_http)
276 delete d_http;
277}
278
279std::string D4Connect::build_dap4_ce(const string requestSuffix, const string dap4ce) {
280 std::stringstream url;
281 bool needsAmpersand = false;
282
283 url << d_URL << requestSuffix << "?";
284
285 if (d_UrlQueryString.length() > 0) {
286 url << d_UrlQueryString;
287 needsAmpersand = true;
288 }
289
290 if (dap4ce.length() > 0) {
291 if (needsAmpersand)
292 url << "&";
293
294 url << DAP4_CE_QUERY_KEY << "=" << id2www_ce(dap4ce);
295 }
296
297 DBG(cerr << "D4Connect::build_dap4_ce() - Source URL: " << d_URL << endl);
298 DBG(cerr << "D4Connect::build_dap4_ce() - Source URL Query String: " << d_UrlQueryString << endl);
299 DBG(cerr << "D4Connect::build_dap4_ce() - dap4ce: " << dap4ce << endl);
300 DBG(cerr << "D4Connect::build_dap4_ce() - request URL: " << url.str() << endl);
301
302 return url.str();
303}
304
305void D4Connect::request_dmr(DMR &dmr, const string expr) {
306 string url = build_dap4_ce(".dmr", expr);
307
308 Response *rs = 0;
309 try {
310 rs = d_http->fetch_url(url);
311
312 d_server = rs->get_version();
313 d_protocol = rs->get_protocol();
314
315 switch (rs->get_type()) {
316 case unknown_type:
317 DBG(cerr << "Response type unknown, assuming it's a DMR response." << endl);
318 /* no break */
319 case dap4_dmr: {
320 D4ParserSax2 parser;
321 parser.intern(*rs->get_cpp_stream(), &dmr);
322 break;
323 }
324
325 case dap4_error:
326 throw InternalErr(__FILE__, __LINE__, "DAP4 errors are not processed yet.");
327
328 case web_error:
329 // We should never get here; a web error should be picked up read_url
330 // (called by fetch_url) and result in a thrown Error object.
331 throw InternalErr(__FILE__, __LINE__, "Web error found where it should never be.");
332
333 default:
334 throw InternalErr(__FILE__, __LINE__,
335 "Response type not handled (got " + long_to_string(rs->get_type()) + ").");
336 }
337 } catch (...) {
338 delete rs;
339 throw;
340 }
341
342 delete rs;
343}
344
345void D4Connect::request_dap4_data(DMR &dmr, const string expr) {
346 string url = build_dap4_ce(".dap", expr);
347
348 Response *rs = 0;
349 try {
350 rs = d_http->fetch_url(url);
351
352 d_server = rs->get_version();
353 d_protocol = rs->get_protocol();
354
355 switch (rs->get_type()) {
356 case unknown_type:
357 DBG(cerr << "Response type unknown, assuming it's a DAP4 Data response." << endl);
358 /* no break */
359 case dap4_data: {
360 // get a chunked input stream
362
363 // parse the DMR, stopping when the boundary is found.
364
365 // force chunk read
366 // get chunk size
367 int chunk_size = cis.read_next_chunk();
368 if (chunk_size < 0)
369 throw Error("Found an unexpected end of input (EOF) while reading a DAP4 data response. (2)");
370
371 // get chunk
372 char chunk[chunk_size];
373 cis.read(chunk, chunk_size);
374 // parse char * with given size
375 D4ParserSax2 parser;
376 // permissive mode allows references to Maps that are not in the response.
377 parser.set_strict(false);
378 // '-2' to discard the CRLF pair
379 parser.intern(chunk, chunk_size - 2, &dmr, false /*debug*/);
380
381 // Read data and store in the DMR
382 D4StreamUnMarshaller um(cis, cis.twiddle_bytes());
383 dmr.root()->deserialize(um, dmr);
384
385 break;
386 }
387
388 case dap4_error:
389 throw InternalErr(__FILE__, __LINE__, "DAP4 errors are not processed yet.");
390
391 case web_error:
392 // We should never get here; a web error should be picked up read_url
393 // (called by fetch_url) and result in a thrown Error object.
394 throw InternalErr(__FILE__, __LINE__, "Web error found where it should never be.");
395
396 default:
397 throw InternalErr(__FILE__, __LINE__,
398 "Response type not handled (got " + long_to_string(rs->get_type()) + ").");
399 }
400 } catch (...) {
401 delete rs;
402 throw;
403 }
404
405 delete rs;
406}
407
409 parse_mime(rs);
410 if (rs.get_type() == unknown_type)
411 throw Error("Unknown response type.");
412
413 read_dmr_no_mime(dmr, rs);
414}
415
417 // Assume callers know what they are doing
418 if (rs.get_type() == unknown_type)
419 rs.set_type(dap4_dmr);
420
421 switch (rs.get_type()) {
422 case dap4_dmr:
423 process_dmr(dmr, rs);
424 d_server = rs.get_version();
425 d_protocol = dmr.dap_version();
426 break;
427 default:
428 throw Error("Expected a DAP4 DMR response.");
429 }
430}
431
433 parse_mime(rs);
434 if (rs.get_type() == unknown_type)
435 throw Error("Unknown response type.");
436
437 read_data_no_mime(data, rs);
438}
439
441 // Assume callers know what they are doing
442 if (rs.get_type() == unknown_type)
444
445 switch (rs.get_type()) {
446 case dap4_data:
447 process_data(data, rs);
448 d_server = rs.get_version();
449 d_protocol = data.dap_version();
450 break;
451 default:
452 throw Error("Expected a DAP4 Data response.");
453 }
454}
455
461void D4Connect::set_credentials(string u, string p) {
462 if (d_http)
463 d_http->set_credentials(u, p);
464}
465
470 if (d_http)
471 d_http->set_accept_deflate(deflate);
472}
473
479void D4Connect::set_xdap_protocol(int major, int minor) {
480 if (d_http)
481 d_http->set_xdap_protocol(major, minor);
482}
483
488 if (d_http)
489 d_http->set_cache_enabled(cache);
490}
491
493 if (d_http)
494 return d_http->is_cache_enabled();
495 else
496 return false;
497}
498
499} // namespace libdap
#define DAP4_CE_QUERY_KEY
Definition D4Connect.h:31
#define CHUNK_SIZE
virtual void read_dmr_no_mime(DMR &dmr, Response &rs)
Definition D4Connect.cc:416
virtual ~D4Connect()
Definition D4Connect.cc:274
virtual void read_data_no_mime(DMR &data, Response &rs)
Definition D4Connect.cc:440
virtual void request_dap4_data(DMR &dmr, const std::string expr="")
Definition D4Connect.cc:345
virtual void read_data(DMR &data, Response &rs)
Definition D4Connect.cc:432
void set_accept_deflate(bool deflate)
Definition D4Connect.cc:469
virtual void request_dmr(DMR &dmr, const std::string expr="")
Definition D4Connect.cc:305
virtual void read_dmr(DMR &dmr, Response &rs)
Definition D4Connect.cc:408
void set_cache_enabled(bool enabled)
Definition D4Connect.cc:487
void set_xdap_protocol(int major, int minor)
Definition D4Connect.cc:479
void set_credentials(std::string u, std::string p)
Set the credentials for responding to challenges while dereferencing URLs.
Definition D4Connect.cc:461
bool is_cache_enabled()
Definition D4Connect.cc:492
void deserialize(D4StreamUnMarshaller &um, DMR &dmr) override
Definition D4Group.cc:540
void intern(istream &f, DMR *dest_dmr, bool debug=false)
Read data from the stream made by D4StreamMarshaller.
std::string dap_version() const
Definition DMR.h:146
D4Group * root()
Definition DMR.cc:228
A class for error processing.
Definition Error.h:92
A class for software fault reporting.
Definition InternalErr.h:61
static RCReader * instance()
Definition RCReader.cc:427
virtual std::string get_protocol() const
Definition Response.h:108
virtual void set_type(ObjectType o)
Definition Response.h:118
virtual std::istream * get_cpp_stream() const
Definition Response.h:104
virtual ObjectType get_type() const
Definition Response.h:106
virtual std::string get_version() const
Definition Response.h:107
#define DBG(x)
Definition debug.h:58
void set_strict(bool s)
Set the 'strict' mode to true or false.
top level DAP object to house generic methods
Definition AISConnect.cc:30
ObjectType get_description_type(const string &value)
Definition mime_util.cc:309
string long_to_string(long val, int base)
Definition util.cc:946
void parse_mime_header(const string &header, string &name, string &value)
Definition mime_util.cc:848
string prune_spaces(const string &name)
Definition util.cc:451
string id2www_ce(string in, const string &allowable)
Definition escaping.cc:166
@ dap4_data
Definition ObjectType.h:68
@ dap4_error
Definition ObjectType.h:69
@ unknown_type
Definition ObjectType.h:58
@ web_error
Definition ObjectType.h:65
@ dap4_dmr
Definition ObjectType.h:67
string get_next_mime_header(FILE *in)
Definition mime_util.cc:777