|
Packit |
a4aae4 |
// -*- mode: c++; c-basic-offset:4 -*-
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// This file is part of libdap, A C++ implementation of the OPeNDAP Data
|
|
Packit |
a4aae4 |
// Access Protocol.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Copyright (c) 2002,2003 OPeNDAP, Inc.
|
|
Packit |
a4aae4 |
// Author: James Gallagher <jgallagher@opendap.org>
|
|
Packit |
a4aae4 |
// Dan Holloway <dholloway@gso.uri.edu>
|
|
Packit |
a4aae4 |
// Reza Nekovei <reza@intcomm.net>
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// This library is free software; you can redistribute it and/or
|
|
Packit |
a4aae4 |
// modify it under the terms of the GNU Lesser General Public
|
|
Packit |
a4aae4 |
// License as published by the Free Software Foundation; either
|
|
Packit |
a4aae4 |
// version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// This library is distributed in the hope that it will be useful,
|
|
Packit |
a4aae4 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
a4aae4 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
a4aae4 |
// Lesser General Public License for more details.
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// You should have received a copy of the GNU Lesser General Public
|
|
Packit |
a4aae4 |
// License along with this library; if not, write to the Free Software
|
|
Packit |
a4aae4 |
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// (c) COPYRIGHT URI/MIT 1994-2002
|
|
Packit |
a4aae4 |
// Please read the full copyright statement in the file COPYRIGHT_URI.
|
|
Packit |
a4aae4 |
//
|
|
Packit |
a4aae4 |
// Authors:
|
|
Packit |
a4aae4 |
// jhrg,jimg James Gallagher <jgallagher@gso.uri.edu>
|
|
Packit |
a4aae4 |
// dan Dan Holloway <dholloway@gso.uri.edu>
|
|
Packit |
a4aae4 |
// reza Reza Nekovei <reza@intcomm.net>
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include "config.h"
|
|
Packit |
a4aae4 |
// #define DODS_DEBUG 1
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include <cassert>
|
|
Packit |
a4aae4 |
#include <cstring>
|
|
Packit |
a4aae4 |
#include <sstream>
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include "D4Connect.h"
|
|
Packit |
a4aae4 |
#include "HTTPConnect.h"
|
|
Packit |
a4aae4 |
#include "Response.h"
|
|
Packit |
a4aae4 |
#include "DMR.h"
|
|
Packit |
a4aae4 |
#include "D4Group.h"
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include "D4ParserSax2.h"
|
|
Packit |
a4aae4 |
#include "chunked_stream.h"
|
|
Packit |
a4aae4 |
#include "chunked_istream.h"
|
|
Packit |
a4aae4 |
#include "D4StreamUnMarshaller.h"
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include "escaping.h"
|
|
Packit |
a4aae4 |
#include "mime_util.h"
|
|
Packit |
a4aae4 |
#include "debug.h"
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
using namespace std;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
namespace libdap {
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** This private method process data from both local and remote sources. It
|
|
Packit |
a4aae4 |
exists to eliminate duplication of code. */
|
|
Packit |
a4aae4 |
void D4Connect::process_dmr(DMR &dmr, Response &rs)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
DBG(cerr << "Entering D4Connect::process_dmr" << endl);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
dmr.set_dap_version(rs.get_protocol());
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
DBG(cerr << "Entering process_data. Response.getVersion() = " << rs.get_version() << endl);
|
|
Packit |
a4aae4 |
switch (rs.get_type()) {
|
|
Packit |
a4aae4 |
case dap4_error: {
|
|
Packit |
a4aae4 |
#if 0
|
|
Packit |
a4aae4 |
Error e;
|
|
Packit |
a4aae4 |
if (!e.parse(rs.get_stream()))
|
|
Packit |
a4aae4 |
throw InternalErr(__FILE__, __LINE__, "Could not parse the Error object returned by the server!");
|
|
Packit |
a4aae4 |
throw e;
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
throw InternalErr(__FILE__, __LINE__, "DAP4 errors not processed yet: FIXME!");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
case web_error:
|
|
Packit |
a4aae4 |
// Web errors (those reported in the return document's MIME header)
|
|
Packit |
a4aae4 |
// are processed by the WWW library.
|
|
Packit |
a4aae4 |
throw InternalErr(__FILE__, __LINE__,
|
|
Packit |
a4aae4 |
"An error was reported by the remote httpd; this should have been processed by HTTPConnect..");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
case dap4_dmr: {
|
|
Packit |
a4aae4 |
// parse the DMR
|
|
Packit |
a4aae4 |
try {
|
|
Packit |
a4aae4 |
D4ParserSax2 parser;
|
|
Packit |
a4aae4 |
// When parsing a data response, we use the permissive mode of the DMR parser
|
|
Packit |
a4aae4 |
// (which allows Map elements to reference Arrays that are not in the DMR).
|
|
Packit |
a4aae4 |
// Do not use that mode when parsing the DMR response - assume the DMR is
|
|
Packit |
a4aae4 |
// valid. jhrg 4/13/16
|
|
Packit |
a4aae4 |
parser.intern(*rs.get_cpp_stream(), &dmr);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
catch (Error &e) {
|
|
Packit |
a4aae4 |
cerr << "Exception: " << e.get_error_message() << endl;
|
|
Packit |
a4aae4 |
return;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
catch (std::exception &e) {
|
|
Packit |
a4aae4 |
cerr << "Exception: " << e.what() << endl;
|
|
Packit |
a4aae4 |
return;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
catch (...) {
|
|
Packit |
a4aae4 |
cerr << "Exception: unknown error" << endl;
|
|
Packit |
a4aae4 |
return;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
return;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
default:
|
|
Packit |
a4aae4 |
throw Error("Unknown response type");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** This private method process data from both local and remote sources. It
|
|
Packit |
a4aae4 |
exists to eliminate duplication of code. */
|
|
Packit |
a4aae4 |
void D4Connect::process_data(DMR &data, Response &rs)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
DBG(cerr << "Entering D4Connect::process_data" << endl);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
assert(rs.get_cpp_stream()); // DAP4 code uses cpp streams
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
data.set_dap_version(rs.get_protocol());
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
DBG(cerr << "Entering process_data. Response.getVersion() = " << rs.get_version() << endl);
|
|
Packit |
a4aae4 |
switch (rs.get_type()) {
|
|
Packit |
a4aae4 |
case dap4_error: {
|
|
Packit |
a4aae4 |
#if 0
|
|
Packit |
a4aae4 |
Error e;
|
|
Packit |
a4aae4 |
if (!e.parse(rs.get_cpp_stream()))
|
|
Packit |
a4aae4 |
throw InternalErr(__FILE__, __LINE__, "Could not parse the Error object returned by the server!");
|
|
Packit |
a4aae4 |
throw e;
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
throw InternalErr(__FILE__, __LINE__, "DAP4 errors not processed yet: FIXME!");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
case web_error:
|
|
Packit |
a4aae4 |
// Web errors (those reported in the return document's MIME header)
|
|
Packit |
a4aae4 |
// are processed by the WWW library.
|
|
Packit |
a4aae4 |
throw InternalErr(__FILE__, __LINE__,
|
|
Packit |
a4aae4 |
"An error was reported by the remote httpd; this should have been processed by HTTPConnect..");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
case dap4_data: {
|
|
Packit |
a4aae4 |
#if BYTE_ORDER_PREFIX
|
|
Packit |
a4aae4 |
// Read the byte-order byte; used later on
|
|
Packit |
a4aae4 |
char byte_order;
|
|
Packit |
a4aae4 |
*rs.get_cpp_stream() >> byte_order;
|
|
Packit |
a4aae4 |
//if (debug) cerr << "Byte order: " << ((byte_order) ? "big endian" : "little endian") << endl;
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
// get a chunked input stream
|
|
Packit |
a4aae4 |
#if BYTE_ORDER_PREFIX
|
|
Packit |
a4aae4 |
chunked_istream cis(*rs.get_cpp_stream(), 1024, byte_order);
|
|
Packit |
a4aae4 |
#else
|
|
Packit |
a4aae4 |
chunked_istream cis(*(rs.get_cpp_stream()), CHUNK_SIZE);
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
// parse the DMR, stopping when the boundary is found.
|
|
Packit |
a4aae4 |
try {
|
|
Packit |
a4aae4 |
// force chunk read
|
|
Packit |
a4aae4 |
// get chunk size
|
|
Packit |
a4aae4 |
int chunk_size = cis.read_next_chunk();
|
|
Packit |
a4aae4 |
if (chunk_size < 0)
|
|
Packit |
a4aae4 |
throw Error("Found an unexpected end of input (EOF) while reading a DAP4 data response. (1)");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// get chunk
|
|
Packit |
a4aae4 |
char chunk[chunk_size];
|
|
Packit |
a4aae4 |
cis.read(chunk, chunk_size);
|
|
Packit |
a4aae4 |
// parse char * with given size
|
|
Packit |
a4aae4 |
D4ParserSax2 parser;
|
|
Packit |
a4aae4 |
// permissive mode allows references to Maps that are not in the response.
|
|
Packit |
a4aae4 |
// Use this mode when parsing a data response (but not the DMR). jhrg 4/13/16
|
|
Packit |
a4aae4 |
parser.set_strict(false);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// '-2' to discard the CRLF pair
|
|
Packit |
a4aae4 |
parser.intern(chunk, chunk_size - 2, &data);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
catch (Error &e) {
|
|
Packit |
a4aae4 |
cerr << "Exception: " << e.get_error_message() << endl;
|
|
Packit |
a4aae4 |
return;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
catch (std::exception &e) {
|
|
Packit |
a4aae4 |
cerr << "Exception: " << e.what() << endl;
|
|
Packit |
a4aae4 |
return;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
catch (...) {
|
|
Packit |
a4aae4 |
cerr << "Exception: unknown error" << endl;
|
|
Packit |
a4aae4 |
return;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#if BYTE_ORDER_PREFIX
|
|
Packit |
a4aae4 |
D4StreamUnMarshaller um(cis, byte_order);
|
|
Packit |
a4aae4 |
#else
|
|
Packit |
a4aae4 |
D4StreamUnMarshaller um(cis, cis.twiddle_bytes());
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
data.root()->deserialize(um, data);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
return;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
default:
|
|
Packit |
a4aae4 |
throw Error("Unknown response type");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Use when you cannot use libcurl.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@note This method tests for MIME headers with lines terminated by CRLF
|
|
Packit |
a4aae4 |
(\r\n) and Newlines (\n). In either case, the line terminators are removed
|
|
Packit |
a4aae4 |
before each header is processed.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@param rs Value/Result parameter. Dump version and type information here.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
void D4Connect::parse_mime(Response &rs)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
rs.set_version("dods/0.0"); // initial value; for backward compatibility.
|
|
Packit |
a4aae4 |
rs.set_protocol("2.0");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
istream &data_source = *rs.get_cpp_stream();
|
|
Packit |
a4aae4 |
string mime = get_next_mime_header(data_source);
|
|
Packit |
a4aae4 |
while (!mime.empty()) {
|
|
Packit |
a4aae4 |
string header, value;
|
|
Packit |
a4aae4 |
parse_mime_header(mime, header, value);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Note that this is an ordered list
|
|
Packit |
a4aae4 |
if (header == "content-description") {
|
|
Packit |
a4aae4 |
DBG(cout << header << ": " << value << endl);
|
|
Packit |
a4aae4 |
rs.set_type(get_description_type(value));
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
// Use the value of xdods-server only if no other value has been read
|
|
Packit |
a4aae4 |
else if (header == "xdods-server" && rs.get_version() == "dods/0.0") {
|
|
Packit |
a4aae4 |
DBG(cout << header << ": " << value << endl);
|
|
Packit |
a4aae4 |
rs.set_version(value);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
// This trumps 'xdods-server' and 'server'
|
|
Packit |
a4aae4 |
else if (header == "xopendap-server") {
|
|
Packit |
a4aae4 |
DBG(cout << header << ": " << value << endl);
|
|
Packit |
a4aae4 |
rs.set_version(value);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else if (header == "xdap") {
|
|
Packit |
a4aae4 |
DBG(cout << header << ": " << value << endl);
|
|
Packit |
a4aae4 |
rs.set_protocol(value);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
// Only look for 'server' if no other header supplies this info.
|
|
Packit |
a4aae4 |
else if (rs.get_version() == "dods/0.0" && header == "server") {
|
|
Packit |
a4aae4 |
DBG(cout << header << ": " << value << endl);
|
|
Packit |
a4aae4 |
rs.set_version(value);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
mime = get_next_mime_header(data_source);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// public mfuncs
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** The D4Connect constructor requires a URL or local file.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@param n The URL for the virtual connection.
|
|
Packit |
a4aae4 |
@param uname Use this username for authentication. Null by default.
|
|
Packit |
a4aae4 |
@param password Password to use for authentication. Null by default.
|
|
Packit |
a4aae4 |
@brief Create an instance of Connect. */
|
|
Packit |
a4aae4 |
D4Connect::D4Connect(const string &url, string uname, string password) :
|
|
Packit |
a4aae4 |
d_http(0), d_local(false), d_URL(""), d_UrlQueryString(""), d_server("unknown"), d_protocol("4.0")
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
string name = prune_spaces(url);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Figure out if the URL starts with 'http', if so, make sure that we
|
|
Packit |
a4aae4 |
// talk to an instance of HTTPConnect.
|
|
Packit |
a4aae4 |
if (name.find("http") == 0) {
|
|
Packit |
a4aae4 |
DBG(cerr << "Connect: The identifier is an http URL" << endl);
|
|
Packit |
a4aae4 |
d_http = new HTTPConnect(RCReader::instance());
|
|
Packit |
a4aae4 |
d_http->set_use_cpp_streams(true);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
d_URL = name;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Find and store any CE given with the URL.
|
|
Packit |
a4aae4 |
string::size_type dotpos = name.find('?');
|
|
Packit |
a4aae4 |
if (dotpos != std::string::npos) { // Found a match.
|
|
Packit |
a4aae4 |
d_URL = name.substr(0, dotpos);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
d_UrlQueryString = name.substr(dotpos + 1);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (d_UrlQueryString.find(DAP4_CE_QUERY_KEY) != std::string::npos) {
|
|
Packit |
a4aae4 |
std::stringstream msg;
|
|
Packit |
a4aae4 |
msg << endl;
|
|
Packit |
a4aae4 |
msg << "WARNING: A DAP4 constraint expression key was found in the query string!" << endl;
|
|
Packit |
a4aae4 |
msg << "The submitted dataset URL: " << name << endl;
|
|
Packit |
a4aae4 |
msg << "Contains the query string: " << d_UrlQueryString << endl;
|
|
Packit |
a4aae4 |
msg << "This will cause issues when making DAP4 requests that specify additional constraints. " << endl;
|
|
Packit |
a4aae4 |
cerr << msg.str() << endl;
|
|
Packit |
a4aae4 |
// throw Error(malformed_expr, msg.str());
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
DBG(cerr << "Connect: The identifier is a local data source." << endl);
|
|
Packit |
a4aae4 |
d_local = true; // local in this case means non-DAP
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
set_credentials(uname, password);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
D4Connect::~D4Connect()
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
if (d_http) delete d_http;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
std::string D4Connect::build_dap4_ce(const string requestSuffix, const string dap4ce)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
std::stringstream url;
|
|
Packit |
a4aae4 |
bool needsAmpersand = false;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
url << d_URL << requestSuffix << "?";
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (d_UrlQueryString.length() > 0) {
|
|
Packit |
a4aae4 |
url << d_UrlQueryString;
|
|
Packit |
a4aae4 |
needsAmpersand = true;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (dap4ce.length() > 0) {
|
|
Packit |
a4aae4 |
if (needsAmpersand) url << "&";
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
url << DAP4_CE_QUERY_KEY << "=" << id2www_ce(dap4ce);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
DBG(cerr << "D4Connect::build_dap4_ce() - Source URL: " << d_URL << endl);
|
|
Packit |
a4aae4 |
DBG(cerr << "D4Connect::build_dap4_ce() - Source URL Query String: " << d_UrlQueryString << endl);
|
|
Packit |
a4aae4 |
DBG(cerr << "D4Connect::build_dap4_ce() - dap4ce: " << dap4ce << endl);
|
|
Packit |
a4aae4 |
DBG(cerr << "D4Connect::build_dap4_ce() - request URL: " << url.str() << endl);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
return url.str();
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
void D4Connect::request_dmr(DMR &dmr, const string expr)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
string url = build_dap4_ce(".dmr", expr);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
Response *rs = 0;
|
|
Packit |
a4aae4 |
try {
|
|
Packit |
a4aae4 |
rs = d_http->fetch_url(url);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
d_server = rs->get_version();
|
|
Packit |
a4aae4 |
d_protocol = rs->get_protocol();
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
switch (rs->get_type()) {
|
|
Packit |
a4aae4 |
case unknown_type:
|
|
Packit |
a4aae4 |
DBG(cerr << "Response type unknown, assuming it's a DMR response." << endl);
|
|
Packit |
a4aae4 |
/* no break */
|
|
Packit |
a4aae4 |
case dap4_dmr: {
|
|
Packit |
a4aae4 |
D4ParserSax2 parser;
|
|
Packit |
a4aae4 |
parser.intern(*rs->get_cpp_stream(), &dmr);
|
|
Packit |
a4aae4 |
break;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
case dap4_error:
|
|
Packit |
a4aae4 |
throw InternalErr(__FILE__, __LINE__, "DAP4 errors are not processed yet.");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
case web_error:
|
|
Packit |
a4aae4 |
// We should never get here; a web error should be picked up read_url
|
|
Packit |
a4aae4 |
// (called by fetch_url) and result in a thrown Error object.
|
|
Packit |
a4aae4 |
throw InternalErr(__FILE__, __LINE__, "Web error found where it should never be.");
|
|
Packit |
a4aae4 |
break;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
default:
|
|
Packit |
a4aae4 |
throw InternalErr(__FILE__, __LINE__,
|
|
Packit |
a4aae4 |
"Response type not handled (got " + long_to_string(rs->get_type()) + ").");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
catch (...) {
|
|
Packit |
a4aae4 |
delete rs;
|
|
Packit |
a4aae4 |
throw;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
delete rs;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
void D4Connect::request_dap4_data(DMR &dmr, const string expr)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
string url = build_dap4_ce(".dap", expr);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
Response *rs = 0;
|
|
Packit |
a4aae4 |
try {
|
|
Packit |
a4aae4 |
rs = d_http->fetch_url(url);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
d_server = rs->get_version();
|
|
Packit |
a4aae4 |
d_protocol = rs->get_protocol();
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
switch (rs->get_type()) {
|
|
Packit |
a4aae4 |
case unknown_type:
|
|
Packit |
a4aae4 |
DBG(cerr << "Response type unknown, assuming it's a DAP4 Data response." << endl);
|
|
Packit |
a4aae4 |
/* no break */
|
|
Packit |
a4aae4 |
case dap4_data: {
|
|
Packit |
a4aae4 |
#if BYTE_ORDER_PREFIX
|
|
Packit |
a4aae4 |
istream &in = *rs->get_cpp_stream();
|
|
Packit |
a4aae4 |
// Read the byte-order byte; used later on
|
|
Packit |
a4aae4 |
char byte_order;
|
|
Packit |
a4aae4 |
in >> byte_order;
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// get a chunked input stream
|
|
Packit |
a4aae4 |
#if BYTE_ORDER_PREFIX
|
|
Packit |
a4aae4 |
chunked_istream cis(*(rs->get_cpp_stream()), 1024, byte_order);
|
|
Packit |
a4aae4 |
#else
|
|
Packit |
a4aae4 |
chunked_istream cis(*(rs->get_cpp_stream()), CHUNK_SIZE);
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// parse the DMR, stopping when the boundary is found.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// force chunk read
|
|
Packit |
a4aae4 |
// get chunk size
|
|
Packit |
a4aae4 |
int chunk_size = cis.read_next_chunk();
|
|
Packit |
a4aae4 |
if (chunk_size < 0)
|
|
Packit |
a4aae4 |
throw Error("Found an unexpected end of input (EOF) while reading a DAP4 data response. (2)");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// get chunk
|
|
Packit |
a4aae4 |
char chunk[chunk_size];
|
|
Packit |
a4aae4 |
cis.read(chunk, chunk_size);
|
|
Packit |
a4aae4 |
// parse char * with given size
|
|
Packit |
a4aae4 |
D4ParserSax2 parser;
|
|
Packit |
a4aae4 |
// permissive mode allows references to Maps that are not in the response.
|
|
Packit |
a4aae4 |
parser.set_strict(false);
|
|
Packit |
a4aae4 |
// '-2' to discard the CRLF pair
|
|
Packit |
a4aae4 |
parser.intern(chunk, chunk_size - 2, &dmr, false /*debug*/);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Read data and store in the DMR
|
|
Packit |
a4aae4 |
#if BYTE_ORDER_PREFIX
|
|
Packit |
a4aae4 |
D4StreamUnMarshaller um(cis, byte_order);
|
|
Packit |
a4aae4 |
#else
|
|
Packit |
a4aae4 |
D4StreamUnMarshaller um(cis, cis.twiddle_bytes());
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
dmr.root()->deserialize(um, dmr);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
break;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
case dap4_error:
|
|
Packit |
a4aae4 |
throw InternalErr(__FILE__, __LINE__, "DAP4 errors are not processed yet.");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
case web_error:
|
|
Packit |
a4aae4 |
// We should never get here; a web error should be picked up read_url
|
|
Packit |
a4aae4 |
// (called by fetch_url) and result in a thrown Error object.
|
|
Packit |
a4aae4 |
throw InternalErr(__FILE__, __LINE__, "Web error found where it should never be.");
|
|
Packit |
a4aae4 |
break;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
default:
|
|
Packit |
a4aae4 |
throw InternalErr(__FILE__, __LINE__,
|
|
Packit |
a4aae4 |
"Response type not handled (got " + long_to_string(rs->get_type()) + ").");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
catch (...) {
|
|
Packit |
a4aae4 |
delete rs;
|
|
Packit |
a4aae4 |
throw;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
delete rs;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
void D4Connect::read_dmr(DMR &dmr, Response &rs)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
parse_mime(rs);
|
|
Packit |
a4aae4 |
if (rs.get_type() == unknown_type) throw Error("Unknown response type.");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
read_dmr_no_mime(dmr, rs);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
void D4Connect::read_dmr_no_mime(DMR &dmr, Response &rs)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
// Assume callers know what they are doing
|
|
Packit |
a4aae4 |
if (rs.get_type() == unknown_type) rs.set_type(dap4_dmr);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
switch (rs.get_type()) {
|
|
Packit |
a4aae4 |
case dap4_dmr:
|
|
Packit |
a4aae4 |
process_dmr(dmr, rs);
|
|
Packit |
a4aae4 |
d_server = rs.get_version();
|
|
Packit |
a4aae4 |
d_protocol = dmr.dap_version();
|
|
Packit |
a4aae4 |
break;
|
|
Packit |
a4aae4 |
default:
|
|
Packit |
a4aae4 |
throw Error("Expected a DAP4 DMR response.");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
void D4Connect::read_data(DMR &data, Response &rs)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
parse_mime(rs);
|
|
Packit |
a4aae4 |
if (rs.get_type() == unknown_type) throw Error("Unknown response type.");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
read_data_no_mime(data, rs);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
void D4Connect::read_data_no_mime(DMR &data, Response &rs)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
// Assume callers know what they are doing
|
|
Packit |
a4aae4 |
if (rs.get_type() == unknown_type) rs.set_type(dap4_data);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
switch (rs.get_type()) {
|
|
Packit |
a4aae4 |
case dap4_data:
|
|
Packit |
a4aae4 |
process_data(data, rs);
|
|
Packit |
a4aae4 |
d_server = rs.get_version();
|
|
Packit |
a4aae4 |
d_protocol = data.dap_version();
|
|
Packit |
a4aae4 |
break;
|
|
Packit |
a4aae4 |
default:
|
|
Packit |
a4aae4 |
throw Error("Expected a DAP4 Data response.");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** @brief Set the credentials for responding to challenges while dereferencing
|
|
Packit |
a4aae4 |
URLs.
|
|
Packit |
a4aae4 |
@param u The username.
|
|
Packit |
a4aae4 |
@param p The password.
|
|
Packit |
a4aae4 |
@see extract_auth_info() */
|
|
Packit |
a4aae4 |
void D4Connect::set_credentials(string u, string p)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
if (d_http) d_http->set_credentials(u, p);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Set the \e accept deflate property.
|
|
Packit |
a4aae4 |
@param deflate True if the client can accept compressed responses, False
|
|
Packit |
a4aae4 |
otherwise. */
|
|
Packit |
a4aae4 |
void D4Connect::set_accept_deflate(bool deflate)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
if (d_http) d_http->set_accept_deflate(deflate);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Set the \e XDAP-Accept property/header. This is used to send to a server
|
|
Packit |
a4aae4 |
the (highest) DAP protocol version number that this client understands.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@param major The client dap protocol major version
|
|
Packit |
a4aae4 |
@param minor The client dap protocol minor version */
|
|
Packit |
a4aae4 |
void D4Connect::set_xdap_protocol(int major, int minor)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
if (d_http) d_http->set_xdap_protocol(major, minor);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Disable any further use of the client-side cache. In a future version
|
|
Packit |
a4aae4 |
of this software, this should be handled so that the www library is
|
|
Packit |
a4aae4 |
not initialized with the cache running by default. */
|
|
Packit |
a4aae4 |
void D4Connect::set_cache_enabled(bool cache)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
if (d_http) d_http->set_cache_enabled(cache);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
bool D4Connect::is_cache_enabled()
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
if (d_http)
|
|
Packit |
a4aae4 |
return d_http->is_cache_enabled();
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
return false;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
} // namespace libdap
|