|
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) 2011 OPeNDAP, Inc.
|
|
Packit |
a4aae4 |
// Author: James Gallagher <jgallagher@opendap.org>
|
|
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 |
#include "config.h"
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include <signal.h>
|
|
Packit |
a4aae4 |
#include <unistd.h>
|
|
Packit |
a4aae4 |
#include <sys/stat.h>
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#ifdef HAVE_UUID_UUID_H
|
|
Packit |
a4aae4 |
#include <uuid/uuid.h> // used to build CID header value for data ddx
|
|
Packit |
a4aae4 |
#elif defined(HAVE_UUID_H)
|
|
Packit |
a4aae4 |
#include <uuid.h>
|
|
Packit |
a4aae4 |
#else
|
|
Packit |
a4aae4 |
#error "Could not find UUID library header"
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#ifndef WIN32
|
|
Packit |
a4aae4 |
#include <sys/wait.h>
|
|
Packit |
a4aae4 |
#else
|
|
Packit |
a4aae4 |
#include <io.h>
|
|
Packit |
a4aae4 |
#include <fcntl.h>
|
|
Packit |
a4aae4 |
#include <process.h>
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include <iostream>
|
|
Packit |
a4aae4 |
#include <string>
|
|
Packit |
a4aae4 |
#include <sstream>
|
|
Packit |
a4aae4 |
#include <fstream>
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include <cstring>
|
|
Packit |
a4aae4 |
#include <ctime>
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
//#define DODS_DEBUG
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include "DAS.h"
|
|
Packit |
a4aae4 |
#include "DDS.h"
|
|
Packit |
a4aae4 |
#include "ConstraintEvaluator.h"
|
|
Packit |
a4aae4 |
#include "DDXParserSAX2.h"
|
|
Packit |
a4aae4 |
#include "Ancillary.h"
|
|
Packit |
a4aae4 |
#include "ResponseBuilder.h"
|
|
Packit |
a4aae4 |
#include "XDRStreamMarshaller.h"
|
|
Packit |
a4aae4 |
#include "XDRFileUnMarshaller.h"
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
//#include "DAPCache3.h"
|
|
Packit |
a4aae4 |
//#include "ResponseCache.h"
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#include "debug.h"
|
|
Packit |
a4aae4 |
#include "mime_util.h" // for last_modified_time() and rfc_822_date()
|
|
Packit |
a4aae4 |
#include "escaping.h"
|
|
Packit |
a4aae4 |
#include "util.h"
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#ifndef WIN32
|
|
Packit |
a4aae4 |
#include "SignalHandler.h"
|
|
Packit |
a4aae4 |
#include "EventHandler.h"
|
|
Packit |
a4aae4 |
#include "AlarmHandler.h"
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#define CRLF "\r\n" // Change here, expr-test.cc
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
using namespace std;
|
|
Packit |
a4aae4 |
using namespace libdap;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Called when initializing a ResponseBuilder that's not going to be passed
|
|
Packit |
a4aae4 |
command line arguments. */
|
|
Packit |
a4aae4 |
void ResponseBuilder::initialize()
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
// Set default values. Don't use the C++ constructor initialization so
|
|
Packit |
a4aae4 |
// that a subclass can have more control over this process.
|
|
Packit |
a4aae4 |
d_dataset = "";
|
|
Packit |
a4aae4 |
d_dap2ce = "";
|
|
Packit |
a4aae4 |
d_dap2_btp_func_ce = "";
|
|
Packit |
a4aae4 |
d_timeout = 0;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
d_default_protocol = DAP_PROTOCOL_VERSION;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
ResponseBuilder::~ResponseBuilder()
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
// If an alarm was registered, delete it. The register code in SignalHandler
|
|
Packit |
a4aae4 |
// always deletes the old alarm handler object, so only the one returned by
|
|
Packit |
a4aae4 |
// remove_handler needs to be deleted at this point.
|
|
Packit |
a4aae4 |
delete dynamic_cast<AlarmHandler*>(SignalHandler::instance()->remove_handler(SIGALRM));
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Return the entire constraint expression in a string. This
|
|
Packit |
a4aae4 |
includes both the projection and selection clauses, but not the
|
|
Packit |
a4aae4 |
question mark.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@brief Get the constraint expression.
|
|
Packit |
a4aae4 |
@return A string object that contains the constraint expression. */
|
|
Packit |
a4aae4 |
string ResponseBuilder::get_ce() const
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
return d_dap2ce;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Set the constraint expression. This will filter the CE text removing
|
|
Packit |
a4aae4 |
* any 'WWW' escape characters except space. Spaces are left in the CE
|
|
Packit |
a4aae4 |
* because the CE parser uses whitespace to delimit tokens while some
|
|
Packit |
a4aae4 |
* datasets have identifiers that contain spaces. It's possible to use
|
|
Packit |
a4aae4 |
* double quotes around identifiers too, but most client software doesn't
|
|
Packit |
a4aae4 |
* know about that.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* @@brief Set the CE
|
|
Packit |
a4aae4 |
* @param _ce The constraint expression
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
void ResponseBuilder::set_ce(string _ce)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
d_dap2ce = www2id(_ce, "%", "%20");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** The ``dataset name'' is the filename or other string that the
|
|
Packit |
a4aae4 |
filter program will use to access the data. In some cases this
|
|
Packit |
a4aae4 |
will indicate a disk file containing the data. In others, it
|
|
Packit |
a4aae4 |
may represent a database query or some other exotic data
|
|
Packit |
a4aae4 |
access method.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@brief Get the dataset name.
|
|
Packit |
a4aae4 |
@return A string object that contains the name of the dataset. */
|
|
Packit |
a4aae4 |
string ResponseBuilder::get_dataset_name() const
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
return d_dataset;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Set the dataset name, which is a string used to access the dataset
|
|
Packit |
a4aae4 |
* on the machine running the server. That is, this is typically a pathname
|
|
Packit |
a4aae4 |
* to a data file, although it doesn't have to be. This is not
|
|
Packit |
a4aae4 |
* echoed in error messages (because that would reveal server
|
|
Packit |
a4aae4 |
* storage patterns that data providers might want to hide). All WWW-style
|
|
Packit |
a4aae4 |
* escapes are replaced except for spaces.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* @brief Set the dataset pathname.
|
|
Packit |
a4aae4 |
* @param ds The pathname (or equivalent) to the dataset.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
void ResponseBuilder::set_dataset_name(const string ds)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
d_dataset = www2id(ds, "%", "%20");
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
#if 0
|
|
Packit |
a4aae4 |
/** Set the server's timeout value. A value of zero (the default) means no
|
|
Packit |
a4aae4 |
timeout.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@see To establish a timeout, call establish_timeout(ostream &)
|
|
Packit |
a4aae4 |
@param t Server timeout in seconds. Default is zero (no timeout). */
|
|
Packit |
a4aae4 |
void ResponseBuilder::set_timeout(int t)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
d_timeout = t;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Get the server's timeout value. */
|
|
Packit |
a4aae4 |
int ResponseBuilder::get_timeout() const
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
return d_timeout;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Use values of this instance to establish a timeout alarm for the server.
|
|
Packit |
a4aae4 |
If the timeout value is zero, do nothing.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
void ResponseBuilder::establish_timeout(ostream &stream) const
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
#ifndef WIN32
|
|
Packit |
a4aae4 |
if (d_timeout > 0) {
|
|
Packit |
a4aae4 |
SignalHandler *sh = SignalHandler::instance();
|
|
Packit |
a4aae4 |
EventHandler *old_eh = sh->register_handler(SIGALRM, new AlarmHandler(stream));
|
|
Packit |
a4aae4 |
delete old_eh;
|
|
Packit |
a4aae4 |
alarm(d_timeout);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/**
|
|
Packit |
a4aae4 |
* Split the CE so that the server functions that compute new values are
|
|
Packit |
a4aae4 |
* separated into their own string and can be evaluated separately from
|
|
Packit |
a4aae4 |
* the rest of the CE (which can contain simple and slicing projection
|
|
Packit |
a4aae4 |
* as well as other types of function calls).
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
void
|
|
Packit |
a4aae4 |
ResponseBuilder::split_ce(ConstraintEvaluator &eval, const string &expr)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
string ce;
|
|
Packit |
a4aae4 |
if (!expr.empty())
|
|
Packit |
a4aae4 |
ce = expr;
|
|
Packit |
a4aae4 |
else
|
|
Packit |
a4aae4 |
ce = d_dap2ce;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
string btp_function_ce = "";
|
|
Packit |
a4aae4 |
string::size_type pos = 0;
|
|
Packit |
a4aae4 |
DBG(cerr << "ce: " << ce << endl);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
string::size_type first_paren = ce.find("(", pos);
|
|
Packit |
a4aae4 |
string::size_type closing_paren = ce.find(")", pos);
|
|
Packit |
a4aae4 |
while (first_paren != string::npos && closing_paren != string::npos) {
|
|
Packit |
a4aae4 |
// Maybe a BTP function; get the name of the potential function
|
|
Packit |
a4aae4 |
string name = ce.substr(pos, first_paren-pos);
|
|
Packit |
a4aae4 |
DBG(cerr << "name: " << name << endl);
|
|
Packit |
a4aae4 |
// is this a BTP function
|
|
Packit |
a4aae4 |
btp_func f;
|
|
Packit |
a4aae4 |
if (eval.find_function(name, &f)) {
|
|
Packit |
a4aae4 |
// Found a BTP function
|
|
Packit |
a4aae4 |
if (!btp_function_ce.empty())
|
|
Packit |
a4aae4 |
btp_function_ce += ",";
|
|
Packit |
a4aae4 |
btp_function_ce += ce.substr(pos, closing_paren+1-pos);
|
|
Packit |
a4aae4 |
ce.erase(pos, closing_paren+1-pos);
|
|
Packit |
a4aae4 |
if (ce[pos] == ',')
|
|
Packit |
a4aae4 |
ce.erase(pos, 1);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
pos = closing_paren + 1;
|
|
Packit |
a4aae4 |
// exception?
|
|
Packit |
a4aae4 |
if (pos < ce.length() && ce.at(pos) == ',')
|
|
Packit |
a4aae4 |
++pos;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
first_paren = ce.find("(", pos);
|
|
Packit |
a4aae4 |
closing_paren = ce.find(")", pos);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
DBG(cerr << "Modified constraint: " << ce << endl);
|
|
Packit |
a4aae4 |
DBG(cerr << "BTP Function part: " << btp_function_ce << endl);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
d_dap2ce = ce;
|
|
Packit |
a4aae4 |
d_dap2_btp_func_ce = btp_function_ce;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
#if 0
|
|
Packit |
a4aae4 |
/** This function formats and prints an ASCII representation of a
|
|
Packit |
a4aae4 |
DAS on stdout. This has the effect of sending the DAS object
|
|
Packit |
a4aae4 |
back to the client program.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@note This is the DAP2 attribute response.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@brief Send a DAS.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@param out The output stream to which the DAS is to be sent.
|
|
Packit |
a4aae4 |
@param das The DAS object to be sent.
|
|
Packit |
a4aae4 |
@param with_mime_headers If true (the default) send MIME headers.
|
|
Packit |
a4aae4 |
@return void
|
|
Packit |
a4aae4 |
@see DAS
|
|
Packit |
a4aae4 |
@deprecated */
|
|
Packit |
a4aae4 |
void ResponseBuilder::send_das(ostream &out, DAS &das, bool with_mime_headers) const
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
if (with_mime_headers)
|
|
Packit |
a4aae4 |
set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset), "2.0");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
das.print(out);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
out << flush;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Send the DAP2 DAS response to the given stream. This version of
|
|
Packit |
a4aae4 |
* send_das() uses the DDS object, assuming that it contains attribute
|
|
Packit |
a4aae4 |
* information. If there is a constraint expression associated with this
|
|
Packit |
a4aae4 |
* instance of ResponseBuilder, then it will be applied. This means
|
|
Packit |
a4aae4 |
* that CEs that contain server functions will populate the response cache
|
|
Packit |
a4aae4 |
* even if the server's initial request is for a DAS. This is different
|
|
Packit |
a4aae4 |
* from the older behavior of libdap where CEs were never evaluated for
|
|
Packit |
a4aae4 |
* the DAS response. This does not actually change the resulting DAS,
|
|
Packit |
a4aae4 |
* just the behavior 'under the covers'.
|
|
Packit |
a4aae4 |
*
|
|
Packit |
a4aae4 |
* @param out Send the response to this ostream
|
|
Packit |
a4aae4 |
* @param dds Use this DDS object
|
|
Packit |
a4aae4 |
* @param eval A Constraint Evaluator to use for any CE bound to this
|
|
Packit |
a4aae4 |
* ResponseBuilder instance
|
|
Packit |
a4aae4 |
* @param constrained Should the result be constrained
|
|
Packit |
a4aae4 |
* @param with_mime_headers Should MIME headers be sent to out?
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
void ResponseBuilder::send_das(ostream &out, DDS &dds, ConstraintEvaluator &eval, bool constrained, bool with_mime_headers)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
// Set up the alarm.
|
|
Packit |
a4aae4 |
establish_timeout(out);
|
|
Packit |
a4aae4 |
dds.set_timeout(d_timeout);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (!constrained) {
|
|
Packit |
a4aae4 |
if (with_mime_headers)
|
|
Packit |
a4aae4 |
set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset), "2.0");
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
dds.print_das(out);
|
|
Packit |
a4aae4 |
out << flush;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
return;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
split_ce(eval);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// If there are functions, parse them and eval.
|
|
Packit |
a4aae4 |
// Use that DDS and parse the non-function ce
|
|
Packit |
a4aae4 |
// Serialize using the second ce and the second dds
|
|
Packit |
a4aae4 |
if (!d_btp_func_ce.empty()) {
|
|
Packit |
a4aae4 |
DDS *fdds = 0;
|
|
Packit |
a4aae4 |
string cache_token = "";
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (responseCache()) {
|
|
Packit |
a4aae4 |
DBG(cerr << "Using the cache for the server function CE" << endl);
|
|
Packit |
a4aae4 |
fdds = responseCache()->read_cached_dataset(dds, d_btp_func_ce, this, &eval, cache_token);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
DBG(cerr << "Cache not found; (re)calculating" << endl);
|
|
Packit |
a4aae4 |
eval.parse_constraint(d_btp_func_ce, dds);
|
|
Packit |
a4aae4 |
fdds = eval.eval_function_clauses(dds);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (with_mime_headers)
|
|
Packit |
a4aae4 |
set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
fdds->print_das(out);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (responseCache())
|
|
Packit |
a4aae4 |
responseCache()->unlock_and_close(cache_token);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
delete fdds;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
DBG(cerr << "Simple constraint" << endl);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
eval.parse_constraint(d_dap2ce, dds); // Throws Error if the ce doesn't parse.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (with_mime_headers)
|
|
Packit |
a4aae4 |
set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
dds.print_das(out);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
out << flush;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** This function formats and prints an ASCII representation of a
|
|
Packit |
a4aae4 |
DDS on stdout. Either an entire DDS or a constrained DDS may be sent.
|
|
Packit |
a4aae4 |
This function looks in the local cache and uses a DDS object there
|
|
Packit |
a4aae4 |
if it's valid. Otherwise, if the request CE contains server functions
|
|
Packit |
a4aae4 |
that build data for the response, the resulting DDS will be cached.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@brief Transmit a DDS.
|
|
Packit |
a4aae4 |
@param out The output stream to which the DAS is to be sent.
|
|
Packit |
a4aae4 |
@param dds The DDS to send back to a client.
|
|
Packit |
a4aae4 |
@param eval A reference to the ConstraintEvaluator to use.
|
|
Packit |
a4aae4 |
@param constrained If this argument is true, evaluate the
|
|
Packit |
a4aae4 |
current constraint expression and send the `constrained DDS'
|
|
Packit |
a4aae4 |
back to the client.
|
|
Packit |
a4aae4 |
@param constrained If true, apply the constraint bound to this instance
|
|
Packit |
a4aae4 |
of ResponseBuilder
|
|
Packit |
a4aae4 |
@param with_mime_headers If true (default) send MIME headers.
|
|
Packit |
a4aae4 |
@return void
|
|
Packit |
a4aae4 |
@see DDS */
|
|
Packit |
a4aae4 |
void ResponseBuilder::send_dds(ostream &out, DDS &dds, ConstraintEvaluator &eval, bool constrained,
|
|
Packit |
a4aae4 |
bool with_mime_headers)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
if (!constrained) {
|
|
Packit |
a4aae4 |
if (with_mime_headers)
|
|
Packit |
a4aae4 |
set_mime_text(out, dods_dds, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
dds.print(out);
|
|
Packit |
a4aae4 |
out << flush;
|
|
Packit |
a4aae4 |
return;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Set up the alarm.
|
|
Packit |
a4aae4 |
establish_timeout(out);
|
|
Packit |
a4aae4 |
dds.set_timeout(d_timeout);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Split constraint into two halves
|
|
Packit |
a4aae4 |
split_ce(eval);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// If there are functions, parse them and eval.
|
|
Packit |
a4aae4 |
// Use that DDS and parse the non-function ce
|
|
Packit |
a4aae4 |
// Serialize using the second ce and the second dds
|
|
Packit |
a4aae4 |
if (!d_btp_func_ce.empty()) {
|
|
Packit |
a4aae4 |
string cache_token = "";
|
|
Packit |
a4aae4 |
DDS *fdds = 0;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (responseCache()) {
|
|
Packit |
a4aae4 |
DBG(cerr << "Using the cache for the server function CE" << endl);
|
|
Packit |
a4aae4 |
fdds = responseCache()->read_cached_dataset(dds, d_btp_func_ce, this, &eval, cache_token);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
DBG(cerr << "Cache not found; (re)calculating" << endl);
|
|
Packit |
a4aae4 |
eval.parse_constraint(d_btp_func_ce, dds);
|
|
Packit |
a4aae4 |
fdds = eval.eval_function_clauses(dds);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Server functions might mark variables to use their read()
|
|
Packit |
a4aae4 |
// methods. Clear that so the CE in d_dap2ce will control what is
|
|
Packit |
a4aae4 |
// sent. If that is empty (there was only a function call) all
|
|
Packit |
a4aae4 |
// of the variables in the intermediate DDS (i.e., the function
|
|
Packit |
a4aae4 |
// result) will be sent.
|
|
Packit |
a4aae4 |
fdds->mark_all(false);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
eval.parse_constraint(d_dap2ce, *fdds);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (with_mime_headers)
|
|
Packit |
a4aae4 |
set_mime_text(out, dods_dds, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
fdds->print_constrained(out);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (responseCache())
|
|
Packit |
a4aae4 |
responseCache()->unlock_and_close(cache_token);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
delete fdds;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
DBG(cerr << "Simple constraint" << endl);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
eval.parse_constraint(d_dap2ce, dds); // Throws Error if the ce doesn't parse.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (with_mime_headers)
|
|
Packit |
a4aae4 |
set_mime_text(out, dods_dds, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
dds.print_constrained(out);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
out << flush;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
#endif
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/**
|
|
Packit |
a4aae4 |
* Build/return the BLOB part of the DAP2 data response.
|
|
Packit |
a4aae4 |
*/
|
|
Packit |
a4aae4 |
void ResponseBuilder::dataset_constraint(ostream &out, DDS & dds, ConstraintEvaluator & eval, bool ce_eval)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
DBG(cerr << "Inside dataset_constraint" << endl);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
dds.print_constrained(out);
|
|
Packit |
a4aae4 |
out << "Data:\n";
|
|
Packit |
a4aae4 |
out << flush;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
XDRStreamMarshaller m(out);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
try {
|
|
Packit |
a4aae4 |
// Send all variables in the current projection (send_p())
|
|
Packit |
a4aae4 |
for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++)
|
|
Packit |
a4aae4 |
if ((*i)->send_p()) {
|
|
Packit |
a4aae4 |
(*i)->serialize(eval, dds, m, ce_eval);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
catch (Error & e) {
|
|
Packit |
a4aae4 |
throw;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
/** Send the data in the DDS object back to the client program. The data is
|
|
Packit |
a4aae4 |
encoded using a Marshaller, and enclosed in a MIME document which is all sent
|
|
Packit |
a4aae4 |
to \c data_stream.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@note This is the DAP2 data response.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
@brief Transmit data.
|
|
Packit |
a4aae4 |
@param dds A DDS object containing the data to be sent.
|
|
Packit |
a4aae4 |
@param eval A reference to the ConstraintEvaluator to use.
|
|
Packit |
a4aae4 |
@param data_stream Write the response to this stream.
|
|
Packit |
a4aae4 |
@param anc_location A directory to search for ancillary files (in
|
|
Packit |
a4aae4 |
addition to the CWD). This is used in a call to
|
|
Packit |
a4aae4 |
get_data_last_modified_time().
|
|
Packit |
a4aae4 |
@param with_mime_headers If true, include the MIME headers in the response.
|
|
Packit |
a4aae4 |
Defaults to true.
|
|
Packit |
a4aae4 |
@return void */
|
|
Packit |
a4aae4 |
void ResponseBuilder::send_data(ostream &data_stream, DDS &dds, ConstraintEvaluator &eval, bool with_mime_headers)
|
|
Packit |
a4aae4 |
{
|
|
Packit |
a4aae4 |
// Split constraint into two halves
|
|
Packit |
a4aae4 |
split_ce(eval);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// If there are functions, parse them and eval.
|
|
Packit |
a4aae4 |
// Use that DDS and parse the non-function ce
|
|
Packit |
a4aae4 |
// Serialize using the second ce and the second dds
|
|
Packit |
a4aae4 |
if (!d_dap2_btp_func_ce.empty()) {
|
|
Packit |
a4aae4 |
DBG(cerr << "Found function(s) in CE: " << d_btp_func_ce << endl);
|
|
Packit |
a4aae4 |
string cache_token = "";
|
|
Packit |
a4aae4 |
DDS *fdds = 0;
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// The BES code caches the function result
|
|
Packit |
a4aae4 |
eval.parse_constraint(d_dap2_btp_func_ce, dds);
|
|
Packit |
a4aae4 |
fdds = eval.eval_function_clauses(dds);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
DBG(fdds->print_constrained(cerr));
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
// Server functions might mark variables to use their read()
|
|
Packit |
a4aae4 |
// methods. Clear that so the CE in d_dap2ce will control what is
|
|
Packit |
a4aae4 |
// sent. If that is empty (there was only a function call) all
|
|
Packit |
a4aae4 |
// of the variables in the intermediate DDS (i.e., the function
|
|
Packit |
a4aae4 |
// result) will be sent.
|
|
Packit |
a4aae4 |
fdds->mark_all(false);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
eval.parse_constraint(d_dap2ce, *fdds);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
fdds->tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (fdds->get_response_limit() != 0 && fdds->get_request_size(true) > fdds->get_response_limit()) {
|
|
Packit |
a4aae4 |
string msg = "The Request for " + long_to_string(dds.get_request_size(true) / 1024)
|
|
Packit |
a4aae4 |
+ "KB is too large; requests for this user are limited to "
|
|
Packit |
a4aae4 |
+ long_to_string(dds.get_response_limit() / 1024) + "KB.";
|
|
Packit |
a4aae4 |
throw Error(msg);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (with_mime_headers)
|
|
Packit |
a4aae4 |
set_mime_binary(data_stream, dods_data, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
DBG(cerr << "About to call dataset_constraint" << endl);
|
|
Packit |
a4aae4 |
dataset_constraint(data_stream, *fdds, eval, false);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
delete fdds;
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
else {
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
DBG(cerr << "Simple constraint" << endl);
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
eval.parse_constraint(d_dap2ce, dds); // Throws Error if the ce doesn't parse.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (dds.get_response_limit() != 0 && dds.get_request_size(true) > dds.get_response_limit()) {
|
|
Packit |
a4aae4 |
string msg = "The Request for " + long_to_string(dds.get_request_size(true) / 1024)
|
|
Packit |
a4aae4 |
+ "KB is too large; requests for this user are limited to "
|
|
Packit |
a4aae4 |
+ long_to_string(dds.get_response_limit() / 1024) + "KB.";
|
|
Packit |
a4aae4 |
throw Error(msg);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
if (with_mime_headers)
|
|
Packit |
a4aae4 |
set_mime_binary(data_stream, dods_data, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
dataset_constraint(data_stream, dds, eval);
|
|
Packit |
a4aae4 |
}
|
|
Packit |
a4aae4 |
|
|
Packit |
a4aae4 |
data_stream << flush;
|
|
Packit |
a4aae4 |
}
|