Blame tests/ResponseBuilder.cc

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
}