bes Updated for version 3.21.1
The Backend Server (BES) is the lower two tiers of the Hyrax data server
BESDapResponseBuilder.cc
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) 2011 OPeNDAP, Inc.
7// Author: James Gallagher <jgallagher@opendap.org>
8//
9// This library is free software; you can redistribute it and/or
10// modify it under the terms of the GNU Lesser General Public
11// License as published by the Free Software Foundation; either
12// version 2.1 of the License, or (at your option) any later version.
13//
14// This library is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17// Lesser General Public License for more details.
18//
19// You should have received a copy of the GNU Lesser General Public
20// License along with this library; if not, write to the Free Software
21// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22//
23// You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
24
25#include "config.h"
26
27#include <signal.h>
28#include <unistd.h>
29#include <sys/stat.h>
30
31#ifdef HAVE_UUID_UUID_H
32#include <uuid/uuid.h> // used to build CID header value for data ddx
33#elif defined(HAVE_UUID_H)
34#include <uuid.h>
35#else
36#error "Could not find UUID library header"
37#endif
38
39
40#ifndef WIN32
41#include <sys/wait.h>
42#else
43#include <io.h>
44#include <fcntl.h>
45#include <process.h>
46#endif
47
48#include <iostream>
49#include <string>
50#include <sstream>
51#include <fstream>
52
53#include <cstring>
54#include <ctime>
55
56//#define DODS_DEBUG
57#define CLEAR_LOCAL_DATA
58#undef USE_LOCAL_TIMEOUT_SCHEME
59
60#include <libdap/DAS.h>
61#include <libdap/DDS.h>
62#include <libdap/Structure.h>
63#include <libdap/ConstraintEvaluator.h>
64#include <libdap/DDXParserSAX2.h>
65#include <libdap/Ancillary.h>
66#include <libdap/XDRStreamMarshaller.h>
67#include <libdap/XDRFileUnMarshaller.h>
68
69#include <libdap/DMR.h>
70#include <libdap/D4Group.h>
71#include <libdap/D4Attributes.h>
72#include <libdap/XMLWriter.h>
73#include <libdap/D4AsyncUtil.h>
74#include <libdap/D4StreamMarshaller.h>
75#include <libdap/chunked_ostream.h>
76#include <libdap/chunked_istream.h>
77#include <libdap/D4ConstraintEvaluator.h>
78#include <libdap/D4FunctionEvaluator.h>
79#include <libdap/D4BaseTypeFactory.h>
80
81#include <libdap/ServerFunctionsList.h>
82
83#include <libdap/mime_util.h> // for last_modified_time() and rfc_822_date()
84#include <libdap/escaping.h>
85#include <libdap/util.h>
86
87#include "DapUtils.h"
88
89#if USE_LOCAL_TIMEOUT_SCHEME
90#ifndef WIN32
91#include <libdap/SignalHandler.h>
92#include <libdap/EventHandler.h>
93#include <libdap/AlarmHandler.h>
94#endif
95#endif
96
97#include "TheBESKeys.h"
98#include "BESDapResponseBuilder.h"
99#include "BESContextManager.h"
100#include "BESDapFunctionResponseCache.h"
101#include "BESStoredDapResultCache.h"
102
103
104#include "BESResponseObject.h"
105#include "BESDDSResponse.h"
106#include "BESDataDDSResponse.h"
107#include "BESDMRResponse.h"
108#include "BESDataHandlerInterface.h"
109#include "BESInternalFatalError.h"
110#include "BESSyntaxUserError.h"
111#include "BESDataNames.h"
112
113#include "BESRequestHandler.h"
114#include "BESRequestHandlerList.h"
115#include "BESNotFoundError.h"
116
117#include "BESUtil.h"
118#include "BESDebug.h"
119#include "BESLog.h"
120#include "BESStopWatch.h"
121#include "DapFunctionUtils.h"
122#include "RequestServiceTimer.h"
123
124using namespace std;
125using namespace libdap;
126
127// @TODO make this std::endl (Is that hard?)
128const string CRLF = "\r\n"; // Change here, expr-test.cc
129const string BES_KEY_TIMEOUT_CANCEL = "BES.CancelTimeoutOnSend";
130
131#define MODULE "dap"
132#define prolog std::string("BESDapResponseBuilder::").append(__func__).append("() - ")
133
134BESDapResponseBuilder::~BESDapResponseBuilder()
135{
136#if USE_LOCAL_TIMEOUT_SCHEME
137 // If an alarm was registered, delete it. The register code in SignalHandler
138 // always deletes the old alarm handler object, so only the one returned by
139 // remove_handler needs to be deleted at this point.
140 delete dynamic_cast<AlarmHandler*>(SignalHandler::instance()->remove_handler(SIGALRM));
141#endif
142}
143
151{
152 return d_dap2ce;
153}
154
166{
167 d_dap2ce = www2id(_ce, "%", "%20");
168}
169
174{
175 return d_dap4ce;
176}
177
189{
190 d_dap4ce = www2id(_ce, "%", "%20");
191}
192
197{
198 return d_dap4function;
199}
200
213{
214 d_dap4function = www2id(_func, "%", "%20");
215}
216
217std::string BESDapResponseBuilder::get_store_result() const
218{
219 return d_store_result;
220}
221
222void BESDapResponseBuilder::set_store_result(const std::string &_sr)
223{
224 d_store_result = _sr;
225 BESDEBUG(MODULE, prolog << "store_result: " << _sr << endl);
226}
227
228std::string BESDapResponseBuilder::get_async_accepted() const
229{
230 return d_async_accepted;
231}
232
233void BESDapResponseBuilder::set_async_accepted(const std::string &_aa)
234{
235 d_async_accepted = _aa;
236 BESDEBUG(MODULE, prolog << "set_async_accepted() - async_accepted: " << _aa << endl);
237}
238
248{
249 return d_dataset;
250}
251
263{
264 d_dataset = www2id(ds, "%", "%20");
265}
266
281static string::size_type find_closing_paren(const string &ce, string::size_type pos)
282{
283 // Iterate over the string finding all ( or ) characters until the matching ) is found.
284 // For each ( found, increment count. When a ) is found and count is zero, it is the
285 // matching closing paren, otherwise, decrement count and keep looking.
286 int count = 1;
287 do {
288 pos = ce.find_first_of("()", pos + 1);
289 if (pos == string::npos){
290 stringstream msg;
291 msg << "Expected to find a matching closing parenthesis in: " << ce;
292 throw BESSyntaxUserError(msg.str(),__FILE__,__LINE__);
293 }
294
295 if (ce[pos] == '(')
296 ++count;
297 else
298 --count; // must be ')'
299
300 } while (count > 0);
301
302 return pos;
303}
304
311void BESDapResponseBuilder::split_ce(ConstraintEvaluator &eval, const string &expr)
312{
313 BESDEBUG(MODULE, prolog << "source expression: " << expr << endl);
314
315 string ce;
316 if (!expr.empty())
317 ce = expr;
318 else
319 ce = d_dap2ce;
320
321 string btp_function_ce;
322 string::size_type pos = 0;
323
324 // This hack assumes that the functions are listed first. Look for the first
325 // open paren and the last closing paren to accommodate nested function calls
326 string::size_type first_paren = ce.find("(", pos);
327 string::size_type closing_paren = string::npos;
328 if (first_paren != string::npos) closing_paren = find_closing_paren(ce, first_paren); //ce.find(")", pos);
329
330 while (first_paren != string::npos && closing_paren != string::npos) {
331 // Maybe a BTP function; get the name of the potential function
332 string name = ce.substr(pos, first_paren - pos);
333
334 // is this a BTP function
335 btp_func f;
336 if (eval.find_function(name, &f)) {
337 // Found a BTP function
338 if (!btp_function_ce.empty()) btp_function_ce += ",";
339 btp_function_ce += ce.substr(pos, closing_paren + 1 - pos);
340 ce.erase(pos, closing_paren + 1 - pos);
341 if (ce[pos] == ',') ce.erase(pos, 1);
342 }
343 else {
344 pos = closing_paren + 1;
345 // exception?
346 if (pos < ce.size() && ce.at(pos) == ',') ++pos;
347 }
348
349 first_paren = ce.find("(", pos);
350 closing_paren = ce.find(")", pos);
351 }
352
353 d_dap2ce = ce;
354 d_btp_func_ce = btp_function_ce;
355
356 BESDEBUG(MODULE, prolog << "Modified constraint: " << d_dap2ce << endl);
357 BESDEBUG(MODULE, prolog << "BTP Function part: " << btp_function_ce << endl);
358 BESDEBUG(MODULE, prolog << "END" << endl);
359}
360
361
362
377void BESDapResponseBuilder::send_das(ostream &out, DAS &das, bool with_mime_headers) const
378{
379 if (with_mime_headers) set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset), "2.0");
380
381 dap_utils::throw_for_dap4_typed_attrs(&das, __FILE__, __LINE__);
382
383 das.print(out);
384
385 out << flush;
386}
387
405void BESDapResponseBuilder::send_das(ostream &out, DDS **dds, ConstraintEvaluator &eval, bool constrained,
406 bool with_mime_headers)
407{
408#if USE_LOCAL_TIMEOUT_SCHEME
409 // Set up the alarm.
410 establish_timeout(out);
411 dds.set_timeout(d_timeout);
412#endif
413
414 // Verify the request hasn't exceeded bes_timeout.
415 RequestServiceTimer::TheTimer()->throw_if_timeout_expired(prolog +" ERROR: bes-timeout expired before transmit", __FILE__, __LINE__);
416
417 if (!constrained) {
418 if (with_mime_headers) set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset), "2.0");
419
421 (*dds)->mark_all(true);
422 dap_utils::throw_for_dap4_typed_vars_or_attrs(*dds, __FILE__, __LINE__);
423
424 (*dds)->print_das(out);
425 out << flush;
426
427 return;
428 }
429
430 split_ce(eval);
431
432 // If there are functions, parse them and eval.
433 // Use that DDS and parse the non-function ce
434 // Serialize using the second ce and the second dds
435 if (!d_btp_func_ce.empty()) {
436 ConstraintEvaluator func_eval;
437 BESDapFunctionResponseCache *responseCache = BESDapFunctionResponseCache::get_instance();
438
439 DDS *fdds = 0; // nullptr
440 if (responseCache && responseCache->can_be_cached(*dds, get_btp_func_ce())) {
441 fdds = responseCache->get_or_cache_dataset(*dds, get_btp_func_ce());
442 }
443 else {
444 func_eval.parse_constraint(get_btp_func_ce(), **dds);
445 fdds = func_eval.eval_function_clauses(**dds);
446 }
447
448 delete *dds; *dds = 0;
449 *dds = fdds;
450
451 if (with_mime_headers)
452 set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset), (*dds)->get_dap_version());
453
454 RequestServiceTimer::TheTimer()->throw_if_timeout_expired(prolog +"ERROR: bes-timeout expired before transmit", __FILE__, __LINE__);
456
457 (*dds)->print_das(out);
458 }
459 else {
460 eval.parse_constraint(d_dap2ce, **dds); // Throws Error if the ce doesn't parse.
461
462 dap_utils::throw_for_dap4_typed_vars_or_attrs(*dds, __FILE__, __LINE__);
463
464 if (with_mime_headers)
465 set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset), (*dds)->get_dap_version());
466
467 RequestServiceTimer::TheTimer()->throw_if_timeout_expired(prolog +"ERROR: bes-timeout expired before transmit", __FILE__, __LINE__);
469
470 (*dds)->print_das(out);
471 }
472
473 out << flush;
474}
475
476
495void BESDapResponseBuilder::send_dds(ostream &out, DDS **dds, ConstraintEvaluator &eval, bool constrained,
496 bool with_mime_headers)
497{
498 if (!constrained) {
499 if (with_mime_headers)
500 set_mime_text(out, dods_dds, x_plain, last_modified_time(d_dataset), (*dds)->get_dap_version());
501
502 // Verify the request hasn't exceeded bes_timeout, and disable timeout if allowed.
503 RequestServiceTimer::TheTimer()->throw_if_timeout_expired(prolog +"ERROR: bes-timeout expired before transmit", __FILE__, __LINE__);
505
506 dap_utils::throw_for_dap4_typed_vars_or_attrs(*dds, __FILE__, __LINE__);
507
508 (*dds)->print(out);
509 out << flush;
510 return;
511 }
512
513#if USE_LOCAL_TIMEOUT_SCHEME
514 // Set up the alarm.
515 establish_timeout(out);
516 dds.set_timeout(d_timeout);
517#endif
518
519 // Split constraint into two halves
520 split_ce(eval);
521
522 // If there are functions, parse them and eval.
523 // Use that DDS and parse the non-function ce
524 // Serialize using the second ce and the second dds
525 if (!d_btp_func_ce.empty()) {
526 BESDEBUG(MODULE,prolog << "Found function(s) in CE: " << get_btp_func_ce() << endl);
527 ConstraintEvaluator func_eval;
528
529 BESDapFunctionResponseCache *responseCache = BESDapFunctionResponseCache::get_instance();
530
531 DDS *fdds = nullptr;
532 if (responseCache && responseCache->can_be_cached(*dds, get_btp_func_ce())) {
533 fdds = responseCache->get_or_cache_dataset(*dds, get_btp_func_ce());
534 }
535 else {
536 func_eval.parse_constraint(get_btp_func_ce(), **dds);
537 fdds = func_eval.eval_function_clauses(**dds);
538 }
539
540 delete *dds; *dds = nullptr;
541 *dds = fdds;
542
543 // Server functions might mark variables to use their read()
544 // methods. Clear that so the CE in d_dap2ce will control what is
545 // sent. If that is empty (there was only a function call) all
546 // the variables in the intermediate DDS (i.e., the function
547 // result) will be sent.
548 (*dds)->mark_all(false);
549
550 // This next step utilizes a well known static method (so really it's a function;),
551 // promote_function_output_structures() to look for
552 // one or more top level Structures whose name indicates (by way of ending with
553 // "_unwrap") that their contents should be promoted (aka moved) to the top level.
554 // This is in support of a hack around the current API where server side functions
555 // may only return a single DAP object and not a collection of objects. The name suffix
556 // "_unwrap" is used as a signal from the function to the the various response
557 // builders and transmitters that the representation needs to be altered before
558 // transmission, and that in fact is what happens in our friend
559 // promote_function_output_structures()
560 promote_function_output_structures(*dds);
561
562 eval.parse_constraint(d_dap2ce, **dds);
563
564 if (with_mime_headers)
565 set_mime_text(out, dods_dds, x_plain, last_modified_time(d_dataset), (*dds)->get_dap_version());
566
567 // Verify the request hasn't exceeded bes_timeout, and disable timeout if allowed.
568 RequestServiceTimer::TheTimer()->throw_if_timeout_expired(prolog +"ERROR: bes-timeout expired before transmit", __FILE__, __LINE__);
570
571 (*dds)->print_constrained(out);
572 }
573 else {
574 BESDEBUG(MODULE, prolog << "Simple constraint: " << d_dap2ce << endl);
575 eval.parse_constraint(d_dap2ce, **dds); // Throws Error if the ce doesn't parse.
576 dap_utils::throw_for_dap4_typed_vars_or_attrs(*dds, __FILE__, __LINE__); // Throws error if dap4 types will be in the response.
577
578 if (with_mime_headers)
579 set_mime_text(out, dods_dds, x_plain, last_modified_time(d_dataset),(*dds)->get_dap_version());
580
581 // Verify the request hasn't exceeded bes_timeout, and disable timeout if allowed.
582 RequestServiceTimer::TheTimer()->throw_if_timeout_expired(prolog +"ERROR: bes-timeout expired before transmit", __FILE__, __LINE__);
584
585 (*dds)->print_constrained(out);
586 }
587
588 out << flush;
589}
590
591#ifdef DAP2_STORED_RESULTS
606bool BESDapResponseBuilder::store_dap2_result(ostream &out, DDS &dds, ConstraintEvaluator &eval)
607{
608 if (get_store_result().empty()) return false;
609
610 string serviceUrl = get_store_result();
611
612 XMLWriter xmlWrtr;
613 D4AsyncUtil d4au;
614
615 // FIXME Keys should be read in initialize(). Also, I think the D4AsyncUtil should
616 // be removed from libdap - it is much more about how the BES processes these kinds
617 // of operations. Change this when working on the response caching for ODSIP. But...
618 // do we really need to put the style sheet in the bes.conf file? Should it be baked
619 // into the code (because we don't want people to change it)?
620 bool found;
621 string *stylesheet_ref = 0, ss_ref_value;
622 TheBESKeys::TheKeys()->get_value(D4AsyncUtil::STYLESHEET_REFERENCE_KEY, ss_ref_value, found);
623 if (found && !ss_ref_value.empty()) {
624 stylesheet_ref = &ss_ref_value;
625 }
626
627 BESStoredDapResultCache *resultCache = BESStoredDapResultCache::get_instance();
628 if (resultCache == NULL) {
629
635 string msg = "The Stored Result request cannot be serviced. ";
636 msg += "Unable to acquire StoredResultCache instance. ";
637 msg += "This is most likely because the StoredResultCache is not (correctly) configured.";
638
639 BESDEBUG(MODULE, prolog << "[WARNING] " << msg << endl);
640
641 d4au.writeD4AsyncResponseRejected(xmlWrtr, UNAVAILABLE, msg, stylesheet_ref);
642 out << xmlWrtr.get_doc();
643 out << flush;
644
645 BESDEBUG(MODULE,prolog << "Sent AsyncRequestRejected" << endl);
646 }
647 else if (get_async_accepted().size() != 0) {
648
652 BESDEBUG(MODULE, prolog << "serviceUrl="<< serviceUrl << endl);
653
654 BESStoredDapResultCache *resultCache = BESStoredDapResultCache::get_instance();
655 string storedResultId = "";
656 storedResultId = resultCache->store_dap2_result(dds, get_ce(), this, &eval);
657
658 BESDEBUG(MODULE, prolog << "storedResultId='"<< storedResultId << "'" << endl);
659
660 string targetURL = BESUtil::assemblePath(serviceUrl, storedResultId);
661 BESDEBUG(MODULE, prolog << "targetURL='"<< targetURL << "'" << endl);
662
663 XMLWriter xmlWrtr;
664 d4au.writeD4AsyncAccepted(xmlWrtr, 0, 0, targetURL, stylesheet_ref);
665 out << xmlWrtr.get_doc();
666 out << flush;
667
668 BESDEBUG(MODULE, prolog << "Sent DAP4 AsyncAccepted response" << endl);
669 }
670 else {
675 d4au.writeD4AsyncRequired(xmlWrtr, 0, 0, stylesheet_ref);
676 out << xmlWrtr.get_doc();
677 out << flush;
678
679 BESDEBUG(MODULE, prolog << "Sent DAP4 AsyncRequired response" << endl);
680 }
681
682 return true;
683
684}
685#endif
686
690void BESDapResponseBuilder::serialize_dap2_data_dds(ostream &out, DDS **dds, ConstraintEvaluator &eval, bool ce_eval)
691{
692 BES_STOPWATCH_START(MODULE, prolog + "Timer");
693 // Verify the request hasn't exceeded bes_timeout, and disable timeout if allowed.
694 RequestServiceTimer::TheTimer()->throw_if_timeout_expired(prolog +"ERROR: bes-timeout expired before transmit", __FILE__, __LINE__);
696
697 BESDEBUG(MODULE, prolog << "BEGIN" << endl);
698
699 (*dds)->print_constrained(out);
700 out << "Data:\n";
701 out << flush;
702
703 XDRStreamMarshaller m(out);
704
705 // Send all variables in the current projection (send_p())
706 for (auto i = (*dds)->var_begin(); i != (*dds)->var_end(); i++) {
707 if ((*i)->send_p()) {
708 RequestServiceTimer::TheTimer()->throw_if_timeout_expired(prolog +"ERROR: bes-timeout expired before transmit " + (*i)->name(), __FILE__, __LINE__);
709 (*i)->serialize(eval, **dds, m, ce_eval);
710#ifdef CLEAR_LOCAL_DATA
711 (*i)->clear_local_data();
712#endif
713 }
714 }
715
716 BESDEBUG(MODULE, prolog << "END" << endl);
717}
718
719#ifdef DAP2_STORED_RESULTS
728void BESDapResponseBuilder::serialize_dap2_data_ddx(ostream &out, DDS **dds, ConstraintEvaluator &eval,
729 const string &boundary, const string &start, bool ce_eval)
730{
731 BESDEBUG(MODULE, prolog << "BEGIN" << endl);
732
733 // Write the MPM headers for the DDX (text/xml) part of the response
734 libdap::set_mime_ddx_boundary(out, boundary, start, dods_ddx, x_plain);
735
736 // Make cid
737 uuid_t uu;
738 uuid_generate(uu);
739 char uuid[37];
740 uuid_unparse(uu, uuid.data());
741 char domain[256];
742 if (getdomainname(domain, 255) != 0 || strlen(domain) == 0) strncpy(domain, "opendap.org", 255);
743
744 string cid = string(uuid.data()) + "@" + string(domain.data());
745
746 // Send constrained DDX with a data blob reference.
747 // Note: CID passed but ignored jhrg 10/20/15
748 (*dds)->print_xml_writer(out, true, cid);
749
750 // write the data part mime headers here
751 set_mime_data_boundary(out, boundary, cid, dods_data_ddx /* old value dap4_data*/, x_plain);
752
753 XDRStreamMarshaller m(out);
754
755 conditional_timeout_cancel();
756
757
758 // Send all variables in the current projection (send_p()).
759 for (DDS::Vars_iter i = (*dds)->var_begin(); i != (*dds)->var_end(); i++) {
760 if ((*i)->send_p()) {
761 (*i)->serialize(eval, **dds, m, ce_eval);
762#ifdef CLEAR_LOCAL_DATA
763 (*i)->clear_local_data();
764#endif
765 }
766 }
767
768 BESDEBUG(MODULE, prolog << "END" << endl);
769}
770#endif
771
788#if 0
789void BESDapResponseBuilder::remove_timeout() const
790{
791#if USE_LOCAL_TIMEOUT_SCHEME
792 alarm(0);
793#endif
794}
795#endif
796
810libdap::DDS *
812{
813 BESDEBUG(MODULE, prolog << "BEGIN"<< endl);
814
815 dhi.first_container();
816
817 auto bdds = dynamic_cast<BESDDSResponse *>(obj);
818 if (!bdds) throw BESInternalFatalError("Expected a BESDDSResponse instance", __FILE__, __LINE__);
819
820 DDS *dds = bdds->get_dds();
821
822 set_dataset_name(dds->filename());
823 set_ce(dhi.data[POST_CONSTRAINT]);
824 set_async_accepted(dhi.data[ASYNC]);
825 set_store_result(dhi.data[STORE_RESULT]);
826
827 ConstraintEvaluator &eval = bdds->get_ce();
828
829 // Split constraint into two halves
830 split_ce(eval);
831
832 // If there are functions, parse them and eval.
833 // Use that DDS and parse the non-function ce
834 // Serialize using the second ce and the second dds
835 if (!d_btp_func_ce.empty()) {
836 BESDapFunctionResponseCache *responseCache = BESDapFunctionResponseCache::get_instance();
837
838 ConstraintEvaluator func_eval;
839 DDS *fdds = nullptr;
840 if (responseCache && responseCache->can_be_cached(dds, get_btp_func_ce())) {
841 fdds = responseCache->get_or_cache_dataset(dds, get_btp_func_ce());
842 }
843 else {
844 func_eval.parse_constraint(get_btp_func_ce(), *dds);
845 fdds = func_eval.eval_function_clauses(*dds);
846 }
847
848 delete dds; // Delete so that we can ...
849 bdds->set_dds(fdds); // Transfer management responsibility
850 dds = fdds;
851
852 dds->mark_all(false);
853
854 promote_function_output_structures(dds);
855 }
856
857 eval.parse_constraint(d_dap2ce, *dds); // Throws Error if the ce doesn't parse.
858 BESDEBUG(MODULE, prolog << "END"<< endl);
859
860 return dds;
861}
862
880libdap::DDS *
882{
883 BES_STOPWATCH_START(MODULE, prolog + "Timer");
884
885 BESDEBUG(MODULE, prolog << "BEGIN"<< endl);
886
887 dhi.first_container();
888
889 auto bdds = dynamic_cast<BESDataDDSResponse *>(obj);
890 if (!bdds) throw BESInternalFatalError("Expected a BESDataDDSResponse instance", __FILE__, __LINE__);
891
892 DDS *dds = bdds->get_dds();
893
894 set_dataset_name(dds->filename());
895 set_ce(dhi.data[POST_CONSTRAINT]);
896 set_async_accepted(dhi.data[ASYNC]);
897 set_store_result(dhi.data[STORE_RESULT]);
898
899
900 // This function is used by all fileout modules and they need to include the attributes in data access.
901 // So obtain the attributes if necessary. KY 2019-10-30
902 if(!bdds->get_ia_flag()) {
903 BESRequestHandler *besRH = BESRequestHandlerList::TheList()->find_handler(dhi.container->get_container_type());
904 besRH->add_attributes(dhi);
905 }
906
907 ConstraintEvaluator &eval = bdds->get_ce();
908
909 // Split constraint into two halves; stores the function and non-function parts in this instance.
910 split_ce(eval);
911
912 // If there are functions, parse them and eval.
913 // Use that DDS and parse the non-function ce
914 // Serialize using the second ce and the second dds
915 if (!get_btp_func_ce().empty()) {
916 BESDEBUG(MODULE,prolog << "Found function(s) in CE: " << get_btp_func_ce() << endl);
917
918 BESDapFunctionResponseCache *responseCache = BESDapFunctionResponseCache::get_instance();
919
920 ConstraintEvaluator func_eval;
921 DDS *fdds = nullptr;
922 if (responseCache && responseCache->can_be_cached(dds, get_btp_func_ce())) {
923 fdds = responseCache->get_or_cache_dataset(dds, get_btp_func_ce());
924 }
925 else {
926 func_eval.parse_constraint(get_btp_func_ce(), *dds);
927 fdds = func_eval.eval_function_clauses(*dds);
928 }
929
930 delete dds; // Delete so that we can ...
931 bdds->set_dds(fdds); // Transfer management responsibility
932 dds = fdds;
933
934 // Server functions might mark (i.e. setting send_p) so variables will use their read()
935 // methods. Clear that so the CE in d_dap2ce will control what is
936 // sent. If that is empty (there was only a function call) all
937 // the variables in the intermediate DDS (i.e., the function
938 // result) will be sent.
939 dds->mark_all(false);
940
941 // Look for one or more top level Structures whose name indicates (by way of ending with
942 // "_uwrap") that their contents should be moved to the top level.
943 //
944 // This is in support of a hack around the current API where server side functions
945 // may only return a single DAP object and not a collection of objects. The name suffix
946 // "_unwrap" is used as a signal from the function to the the various response
947 // builders and transmitters that the representation needs to be altered before
948 // transmission, and that in fact is what happens in our friend
949 // promote_function_output_structures()
950 promote_function_output_structures(dds);
951 }
952
953 // evaluate the rest of the CE - the part that follows the function calls.
954 eval.parse_constraint(get_ce(), *dds);
955
956 dds->tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
957
958 dap_utils::throw_if_too_big(*dds, __FILE__, __LINE__);
959
960
961 // Iterate through the variables in the DataDDS and read
962 // in the data if the variable has its send flag set.
963 for (auto i = dds->var_begin(), e = dds->var_end(); i != e; ++i) {
964 if ((*i)->send_p()) {
965 try {
966 (*i)->intern_data(eval, *dds);
967 }
968 catch(std::exception &e) {
969 throw BESSyntaxUserError(string("Caught a C++ standard exception while working on '") + (*i)->name() + "' The error was: " + e.what(), __FILE__, __LINE__);
970 }
971 }
972 }
973
974 BESDEBUG(MODULE, prolog << "END"<< endl);
975
976 return dds;
977}
978
991void BESDapResponseBuilder::send_dap2_data(ostream &data_stream, DDS **dds, ConstraintEvaluator &eval,
992 bool with_mime_headers)
993{
994 BESDEBUG(MODULE, prolog << "BEGIN"<< endl);
995
996#if USE_LOCAL_TIMEOUT_SCHEME
997 // Set up the alarm.
998 establish_timeout(data_stream);
999 dds.set_timeout(d_timeout);
1000#endif
1001
1002 // Split constraint into two halves
1003 split_ce(eval);
1004
1005 // If there are functions, parse them and eval.
1006 // Use that DDS and parse the non-function ce
1007 // Serialize using the second ce and the second dds
1008 if (!get_btp_func_ce().empty()) {
1009 BESDEBUG(MODULE,prolog << "Found function(s) in CE: " << get_btp_func_ce() << endl);
1010
1011 BESDapFunctionResponseCache *response_cache = BESDapFunctionResponseCache::get_instance();
1012
1013 ConstraintEvaluator func_eval;
1014 DDS *fdds = nullptr;
1015 if (response_cache && response_cache->can_be_cached(*dds, get_btp_func_ce())) {
1016 fdds = response_cache->get_or_cache_dataset(*dds, get_btp_func_ce());
1017 }
1018 else {
1019 func_eval.parse_constraint(get_btp_func_ce(), **dds);
1020 fdds = func_eval.eval_function_clauses(**dds);
1021 }
1022
1023 delete *dds; *dds = nullptr;
1024 *dds = fdds;
1025
1026 (*dds)->mark_all(false);
1027
1028 promote_function_output_structures(*dds);
1029
1030 // evaluate the rest of the CE - the part that follows the function calls.
1031 eval.parse_constraint(get_ce(), **dds);
1032
1033 (*dds)->tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
1034
1035 dap_utils::throw_if_too_big(**dds, __FILE__, __LINE__);
1036
1037 if (with_mime_headers)
1038 set_mime_binary(data_stream, dods_data, x_plain, last_modified_time(d_dataset), (*dds)->get_dap_version());
1039
1040#if STORE_DAP2_RESULT_FEATURE
1041 // This means: if we are not supposed to store the result, then serialize it.
1042 if (!store_dap2_result(data_stream, **dds, eval)) {
1043 serialize_dap2_data_dds(data_stream, dds, eval, true /* was 'false'. jhrg 3/10/15 */);
1044 }
1045#else
1046 serialize_dap2_data_dds(data_stream, dds, eval, true /* was 'false'. jhrg 3/10/15 */);
1047#endif
1048
1049 }
1050 else {
1051 BESDEBUG(MODULE, prolog << "Simple constraint" << endl);
1052
1053 eval.parse_constraint(get_ce(), **dds); // Throws Error if the ce doesn't parse.
1054 (*dds)->tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
1055
1056 dap_utils::throw_for_dap4_typed_vars_or_attrs(*dds, __FILE__, __LINE__);
1057 dap_utils::throw_if_too_big(**dds, __FILE__, __LINE__);
1058
1059 if (with_mime_headers)
1060 set_mime_binary(data_stream, dods_data, x_plain, last_modified_time(d_dataset), (*dds)->get_dap_version());
1061
1062#if STORE_DAP2_RESULT_FEATURE
1063 // This means: if we are not supposed to store the result, then serialize it.
1064 if (!store_dap2_result(data_stream, **dds, eval)) {
1065 serialize_dap2_data_dds(data_stream, dds, eval);
1066 }
1067#else
1068 serialize_dap2_data_dds(data_stream, dds, eval);
1069#endif
1070 }
1071
1072 dap_utils::log_response_and_memory_size(prolog, dds);
1073
1074#if 0
1075 // This change addresses an issue on OSX where the size returned by getrusage() is in bytes, not kb.
1076 // The BESUtil::get_current_memory_usage() function sorts the problem. jhrg 4/6/22
1077 struct rusage usage;
1078 int usage_val;
1079 usage_val = getrusage(RUSAGE_SELF, &usage);
1080
1081 if (usage_val == 0){ //if getrusage() was successful, out put both sizes
1082 long mem_size = usage.ru_maxrss; // get the max size (man page says it is in kilobytes)
1083 INFO_LOG(prolog << "request size: "<< req_size << "KB -|- memory used by process: " << mem_size << "KB" << endl);
1084 }
1085 else { //if the getrusage() wasn't successful, only output the request size
1086 INFO_LOG(prolog << "request size: "<< req_size << "KB" << endl );
1087 }
1088#endif
1089
1090 data_stream << flush;
1091
1092 BESDEBUG(MODULE, prolog << "END"<< endl);
1093
1094}
1095
1096void BESDapResponseBuilder::send_dap2_data(BESDataHandlerInterface &dhi, DDS **dds, ConstraintEvaluator &eval,
1097 bool with_mime_headers)
1098{
1099 BESDEBUG(MODULE, prolog << "BEGIN"<< endl);
1100
1101 ostream & data_stream = dhi.get_output_stream();
1102#if USE_LOCAL_TIMEOUT_SCHEME
1103 // Set up the alarm.
1104 establish_timeout(data_stream);
1105 dds.set_timeout(d_timeout);
1106#endif
1107
1108 // Split constraint into two halves
1109 split_ce(eval);
1110
1111 // If there are functions, parse them and eval.
1112 // Use that DDS and parse the non-function ce
1113 // Serialize using the second ce and the second dds
1114 if (!get_btp_func_ce().empty()) {
1115 BESDEBUG(MODULE, prolog << "Found function(s) in CE: " << get_btp_func_ce() << endl);
1116
1117 // Server-side functions need to include the attributes in data access.
1118 // So obtain the attributes if necessary. KY 2019-10-30
1119 {
1120 BESResponseObject *response = dhi.response_handler->get_response_object();
1121 auto *bdds = dynamic_cast<BESDataDDSResponse *> (response);
1122 if (!bdds)
1123 throw BESInternalError("cast error", __FILE__, __LINE__);
1124
1125 if(!bdds->get_ia_flag()) {
1126 BESRequestHandler *besRH = BESRequestHandlerList::TheList()->find_handler(dhi.container->get_container_type());
1127 besRH->add_attributes(dhi);
1128 }
1129 }
1130
1131 BESDapFunctionResponseCache *response_cache = BESDapFunctionResponseCache::get_instance();
1132 ConstraintEvaluator func_eval;
1133 DDS *fdds = nullptr;
1134 if (response_cache && response_cache->can_be_cached(*dds, get_btp_func_ce())) {
1135 fdds = response_cache->get_or_cache_dataset(*dds, get_btp_func_ce());
1136 }
1137 else {
1138 func_eval.parse_constraint(get_btp_func_ce(), **dds);
1139 fdds = func_eval.eval_function_clauses(**dds);
1140 }
1141
1142 delete *dds;
1143 *dds = nullptr;
1144 *dds = fdds;
1145
1146 (*dds)->mark_all(false);
1147
1148 promote_function_output_structures(*dds);
1149
1150 // evaluate the rest of the CE - the part that follows the function calls.
1151 eval.parse_constraint(get_ce(), **dds);
1152
1153 (*dds)->tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
1154
1155 dap_utils::throw_if_too_big(**dds, __FILE__, __LINE__);
1156
1157 if (with_mime_headers)
1158 set_mime_binary(data_stream, dods_data, x_plain, last_modified_time(d_dataset), (*dds)->get_dap_version());
1159
1160#if STORE_DAP2_RESULT_FEATURE
1161 // This means: if we are not supposed to store the result, then serialize it.
1162 if (!store_dap2_result(data_stream, **dds, eval)) {
1163 serialize_dap2_data_dds(data_stream, dds, eval, true /* was 'false'. jhrg 3/10/15 */);
1164 }
1165#else
1166 serialize_dap2_data_dds(data_stream, dds, eval, true /* was 'false'. jhrg 3/10/15 */);
1167#endif
1168
1169 }
1170 else {
1171 BESDEBUG(MODULE, prolog << "Simple constraint" << endl);
1172
1173 eval.parse_constraint(get_ce(), **dds); // Throws Error if the ce doesn't parse.
1174 (*dds)->tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
1175
1176 dap_utils::throw_for_dap4_typed_vars_or_attrs(*dds, __FILE__, __LINE__);
1177 dap_utils::throw_if_too_big(**dds, __FILE__, __LINE__);
1178
1179 if (with_mime_headers)
1180 set_mime_binary(data_stream, dods_data, x_plain, last_modified_time(d_dataset), (*dds)->get_dap_version());
1181
1182#if STORE_DAP2_RESULT_FEATURE
1183 // This means: if we are not supposed to store the result, then serialize it.
1184 if (!store_dap2_result(data_stream, **dds, eval)) {
1185 serialize_dap2_data_dds(data_stream, dds, eval);
1186 }
1187#else
1188 serialize_dap2_data_dds(data_stream, dds, eval);
1189#endif
1190 }
1191
1192 dap_utils::log_response_and_memory_size(prolog,dds);
1193
1194#if 0
1195 struct rusage usage;
1196 int usage_val;
1197 usage_val = getrusage(RUSAGE_SELF, &usage);
1198
1199 if (usage_val == 0){ //if getrusage() was successful, out put both sizes
1200 long mem_size = usage.ru_maxrss; // get the max size (man page says it is in kilobytes)
1201 INFO_LOG(prolog << "request size: "<< req_size << "KB -|- memory used by process: " << mem_size << "KB" << endl );
1202 }
1203 else { //if the getrusage() wasn't successful, only output the request size
1204 INFO_LOG(prolog << "request size: "<< req_size << "KB" << endl );
1205 }
1206#endif
1207
1208 data_stream << flush;
1209
1210 BESDEBUG(MODULE, prolog << "END"<< endl);
1211
1212}
1226void BESDapResponseBuilder::send_ddx(ostream &out, DDS **dds, ConstraintEvaluator &eval, bool with_mime_headers)
1227{
1228 // Verify the request hasn't exceeded bes_timeout, and disable timeout if allowed.
1229 RequestServiceTimer::TheTimer()->throw_if_timeout_expired(prolog +"ERROR: bes-timeout expired before transmit", __FILE__, __LINE__);
1231
1232 if (d_dap2ce.empty()) {
1233 if (with_mime_headers)
1234 set_mime_text(out, dods_ddx, x_plain, last_modified_time(d_dataset), (*dds)->get_dap_version());
1235
1236 (*dds)->print_xml_writer(out, false /*constrained */, "");
1237 //dds.print(out);
1238 out << flush;
1239 return;
1240 }
1241
1242#if USE_LOCAL_TIMEOUT_SCHEME
1243 // Set up the alarm.
1244 establish_timeout(out);
1245 dds.set_timeout(d_timeout);
1246#endif
1247
1248 // Split constraint into two halves
1249 split_ce(eval);
1250
1251 // If there are functions, parse them and eval.
1252 // Use that DDS and parse the non-function ce
1253 // Serialize using the second ce and the second dds
1254 if (!d_btp_func_ce.empty()) {
1255 BESDapFunctionResponseCache *response_cache = BESDapFunctionResponseCache::get_instance();
1256
1257 ConstraintEvaluator func_eval;
1258 DDS *fdds = nullptr;
1259 if (response_cache && response_cache->can_be_cached(*dds, get_btp_func_ce())) {
1260 fdds = response_cache->get_or_cache_dataset(*dds, get_btp_func_ce());
1261 }
1262 else {
1263 func_eval.parse_constraint(get_btp_func_ce(), **dds);
1264 fdds = func_eval.eval_function_clauses(**dds);
1265 }
1266
1267 delete *dds; *dds = 0;
1268 *dds = fdds;
1269
1270 (*dds)->mark_all(false);
1271
1272 promote_function_output_structures(*dds);
1273
1274 eval.parse_constraint(d_dap2ce, **dds);
1275
1276 if (with_mime_headers)
1277 set_mime_text(out, dods_ddx, x_plain, last_modified_time(d_dataset), (*dds)->get_dap_version());
1278
1279 // Verify the request hasn't exceeded bes_timeout, and disable timeout if allowed.
1280 RequestServiceTimer::TheTimer()->throw_if_timeout_expired(prolog +"ERROR: bes-timeout expired before transmit", __FILE__, __LINE__);
1282
1283 (*dds)->print_xml_writer(out, true, "");
1284 }
1285 else {
1286 eval.parse_constraint(d_dap2ce, **dds); // Throws Error if the ce doesn't parse.
1287
1288 if (with_mime_headers)
1289 set_mime_text(out, dods_ddx, x_plain, last_modified_time(d_dataset), (*dds)->get_dap_version());
1290
1291 // Verify the request hasn't exceeded bes_timeout, and disable timeout if allowed.
1292 RequestServiceTimer::TheTimer()->throw_if_timeout_expired(prolog +"ERROR: bes-timeout expired before transmit", __FILE__, __LINE__);
1294
1295 // dds.print_constrained(out);
1296 (*dds)->print_xml_writer(out, true, "");
1297 }
1298
1299 out << flush;
1300}
1301
1302void BESDapResponseBuilder::send_dmr(ostream &out, DMR &dmr, bool with_mime_headers)
1303{
1304 // If the CE is not empty, parse it. The projections, etc., are set as a side effect.
1305 // If the parser returns false, the expression did not parse. The parser may also
1306 // throw Error
1307 if (!d_dap4ce.empty()) {
1308
1309 BESDEBUG(MODULE, prolog << "Parsing DAP4 constraint: '"<< d_dap4ce << "'"<< endl);
1310
1311 D4ConstraintEvaluator parser(&dmr);
1312 bool parse_ok = parser.parse(d_dap4ce);
1313 if (!parse_ok){
1314 stringstream msg;
1315 msg << "Failed to parse the provided DAP4 server-side function expression: " << d_dap4function;
1316 throw BESSyntaxUserError(msg.str(),__FILE__,__LINE__);
1317 }
1318 }
1319 // with an empty CE, send everything. Even though print_dap4() and serialize()
1320 // don't need this, other code may depend on send_p being set. This may change
1321 // if DAP4 has a separate function evaluation phase. jhrg 11/25/13
1322 else {
1323 dmr.root()->set_send_p(true);
1324 }
1325
1326 if (with_mime_headers) set_mime_text(out, dap4_dmr, x_plain, last_modified_time(d_dataset), dmr.dap_version());
1327
1328 // Verify the request hasn't exceeded bes_timeout, and disable timeout if allowed.
1329 RequestServiceTimer::TheTimer()->throw_if_timeout_expired(prolog +"ERROR: bes-timeout expired before transmit", __FILE__, __LINE__);
1331
1332 BESDEBUG(MODULE, prolog << "dmr.request_xml_base(): '"<< dmr.request_xml_base() << "' (dmr: " << (void *) &dmr << ")" << endl);
1333
1334 if (dmr.get_utf8_xml_encoding()) {
1335 auto xml = XMLWriter(" ","UTF-8");
1336 dmr.print_dap4(xml, /*constrained &&*/!d_dap4ce.empty() /* true == constrained */);
1337 out << xml.get_doc() << flush;
1338 }
1339 else {
1340 XMLWriter xml;
1341 dmr.print_dap4(xml, /*constrained &&*/!d_dap4ce.empty() /* true == constrained */);
1342 out << xml.get_doc() << flush;
1343 }
1344}
1345
1346void BESDapResponseBuilder::send_dap4_data_using_ce(ostream &out, DMR &dmr, bool with_mime_headers)
1347{
1348 if (!d_dap4ce.empty()) {
1349 BESDEBUG(MODULE , "BESDapResponseBuilder::send_dap4_data_using_ce() - expression constraint is not empty. " <<endl);
1350 D4ConstraintEvaluator parser(&dmr);
1351 bool parse_ok = parser.parse(d_dap4ce);
1352 if (!parse_ok){
1353 stringstream msg;
1354 msg << "Failed to parse the provided DAP4 server-side function expression: " << d_dap4function;
1355 throw BESSyntaxUserError(msg.str(),__FILE__,__LINE__);
1356 }
1357 }
1358 // with an empty CE, send everything. Even though print_dap4() and serialize()
1359 // don't need this, other code may depend on send_p being set. This may change
1360 // if DAP4 has a separate function evaluation phase. jhrg 11/25/13
1361 else {
1362 dmr.set_ce_empty(true);
1363 dmr.root()->set_send_p(true);
1364 }
1365
1366 dap_utils::log_response_and_memory_size(prolog, dmr);
1367 dap_utils::throw_if_too_big(dmr, __FILE__, __LINE__);
1368
1369 // The following block is for debugging purpose. KY 05/13/2020
1370#if !NDEBUG
1371 for (auto i = dmr.root()->var_begin(), e = dmr.root()->var_end(); i != e; ++i) {
1372 BESDEBUG(MODULE , prolog << (*i)->name() << endl);
1373 if ((*i)->send_p()) {
1374 BESDEBUG(MODULE , prolog << "Obtain data- " << (*i)->name() << endl);
1375 D4Attributes *d4_attrs = (*i)->attributes();
1376 BESDEBUG(MODULE , prolog << "Number of attributes " << d4_attrs << endl);
1377 for (auto ii = d4_attrs->attribute_begin(), ee = d4_attrs->attribute_end(); ii != ee; ++ii) {
1378 BESDEBUG(MODULE ,prolog << "Attribute name is " << (*ii)->name() << endl);
1379 }
1380 }
1381 }
1382#endif
1383
1384 if (!store_dap4_result(out, dmr)) {
1385 serialize_dap4_data(out, dmr, with_mime_headers);
1386 }
1387}
1388
1398{
1399 BES_STOPWATCH_START(MODULE, prolog + "Timer");
1400 if (!d_dap4ce.empty()) {
1401 BESDEBUG(MODULE , prolog << "Expression constraint is not empty. " <<endl);
1402 D4ConstraintEvaluator parser(&dmr);
1403 bool parse_ok = parser.parse(d_dap4ce);
1404 if (!parse_ok){
1405 stringstream msg;
1406 msg << "Failed to parse the provided DAP4 server-side function expression: " << d_dap4function;
1407 throw BESSyntaxUserError(msg.str(),__FILE__,__LINE__);
1408 }
1409 }
1410 // with an empty CE, send everything. Even though print_dap4() and serialize()
1411 // don't need this, other code may depend on send_p being set. This may change
1412 // if DAP4 has a separate function evaluation phase. jhrg 11/25/13
1413 else {
1414 dmr.set_ce_empty(true);
1415 dmr.root()->set_send_p(true);
1416 }
1417 dap_utils::throw_if_too_big(dmr, __FILE__, __LINE__);
1418}
1419
1420void BESDapResponseBuilder::send_dap4_data(ostream &out, DMR &dmr, bool with_mime_headers)
1421{
1422 // If a function was passed in with this request, evaluate it and use that DMR
1423 // for the remainder of this request.
1424 // TODO Add caching for these function invocations
1425 if (!d_dap4function.empty()) {
1426 D4BaseTypeFactory d4_factory;
1427 DMR function_result(&d4_factory, "function_results");
1428
1429 // Function modules load their functions onto this list. The list is
1430 // part of libdap, not the BES.
1431 if (!ServerFunctionsList::TheList()) {
1432 stringstream msg;
1433 msg << "The function expression could not be evaluated because ";
1434 msg << "there are no server-side functions defined on this server.";
1435 throw BESSyntaxUserError(msg.str(),__FILE__,__LINE__);
1436 }
1437
1438 D4FunctionEvaluator parser(&dmr, ServerFunctionsList::TheList());
1439 bool parse_ok = parser.parse(d_dap4function);
1440 if (!parse_ok){
1441 stringstream msg;
1442 msg << "Failed to parse the provided DAP4 server-side function expression: " << d_dap4function;
1443 throw BESSyntaxUserError(msg.str(),__FILE__,__LINE__);
1444 }
1445 parser.eval(&function_result);
1446
1447 // Now use the results of running the functions for the remainder of the
1448 // send_data operation.
1449 send_dap4_data_using_ce(out, function_result, with_mime_headers);
1450 }
1451 else {
1452 send_dap4_data_using_ce(out, dmr, with_mime_headers);
1453 }
1454}
1455
1459void BESDapResponseBuilder::serialize_dap4_data(std::ostream &out, libdap::DMR &dmr, bool with_mime_headers)
1460{
1461 BES_STOPWATCH_START(MODULE, prolog + "Timer");
1462 BESDEBUG(MODULE, prolog << "BEGIN" << endl);
1463
1464 if (with_mime_headers) set_mime_binary(out, dap4_data, x_plain, last_modified_time(d_dataset), dmr.dap_version());
1465
1466 BESDEBUG(MODULE, prolog << "dmr.request_xml_base(): \"" << dmr.request_xml_base() << "\""<< endl);
1467
1468 // Write the DMR
1469 XMLWriter xml;
1470 if (dmr.get_utf8_xml_encoding())
1471 xml = XMLWriter(" ","UTF-8");
1472 dmr.print_dap4(xml, !d_dap4ce.empty());
1473
1474 // now make the chunked output stream; set the size to be at least chunk_size
1475 // but make sure that the whole of the xml plus the CRLF can fit in the first
1476 // chunk. (+2 for the CRLF bytes).
1477 chunked_ostream cos(out, max((unsigned int) CHUNK_SIZE, xml.get_doc_size() + 2));
1478
1479 // Verify the request hasn't exceeded bes_timeout, and disable timeout if allowed.
1480 RequestServiceTimer::TheTimer()->throw_if_timeout_expired(prolog +"ERROR: bes-timeout expired before transmit", __FILE__, __LINE__);
1482
1483 // using flush means that the DMR and CRLF are in the first chunk.
1484 cos << xml.get_doc() << CRLF << flush;
1485
1486 // Write the data, chunked with checksums
1487 D4StreamMarshaller m(cos);
1488 dmr.root()->serialize(m, dmr, !d_dap4ce.empty());
1489#ifdef CLEAR_LOCAL_DATA
1490 dmr.root()->clear_local_data();
1491#endif
1492 cos << flush;
1493
1494 BESDEBUG(MODULE, prolog << "END" << endl);
1495}
1496
1511bool BESDapResponseBuilder::store_dap4_result(ostream &out, libdap::DMR &dmr)
1512{
1513 if (!get_store_result().empty()) {
1514 string serviceUrl = get_store_result();
1515
1516 D4AsyncUtil d4au;
1517 XMLWriter xmlWrtr;
1518 if (dmr.get_utf8_xml_encoding())
1519 xmlWrtr = XMLWriter(" ","UTF-8");
1520
1521 // FIXME See above comment for store dap2 result
1522 bool found;
1523 string *stylesheet_ref = nullptr, ss_ref_value;
1524 TheBESKeys::TheKeys()->get_value(D4AsyncUtil::STYLESHEET_REFERENCE_KEY, ss_ref_value, found);
1525 if (found && !ss_ref_value.empty()) {
1526 stylesheet_ref = &ss_ref_value;
1527 }
1528
1530 if (resultCache == nullptr) {
1531
1537 string msg = "The Stored Result request cannot be serviced. ";
1538 msg += "Unable to acquire StoredResultCache instance. ";
1539 msg += "This is most likely because the StoredResultCache is not (correctly) configured.";
1540
1541 BESDEBUG(MODULE, prolog << "[WARNING] " << msg << endl);
1542 d4au.writeD4AsyncResponseRejected(xmlWrtr, UNAVAILABLE, msg, stylesheet_ref);
1543 out << xmlWrtr.get_doc();
1544 out << flush;
1545 BESDEBUG(MODULE, prolog << "Sent AsyncRequestRejected" << endl);
1546
1547 return true;
1548 }
1549
1550 if (!get_async_accepted().empty()) {
1551
1555 BESDEBUG(MODULE, prolog << "serviceUrl="<< serviceUrl << endl);
1556
1557 string storedResultId;
1558 storedResultId = resultCache->store_dap4_result(dmr, get_ce(), this);
1559
1560 BESDEBUG(MODULE,prolog << "storedResultId='"<< storedResultId << "'" << endl);
1561
1562 string targetURL = BESUtil::assemblePath(serviceUrl, storedResultId);
1563 BESDEBUG(MODULE, prolog << "targetURL='"<< targetURL << "'" << endl);
1564
1565 d4au.writeD4AsyncAccepted(xmlWrtr, 0, 0, targetURL, stylesheet_ref);
1566 out << xmlWrtr.get_doc();
1567 out << flush;
1568 BESDEBUG(MODULE, prolog << "Sent AsyncAccepted" << endl);
1569
1570 }
1571 else {
1576 d4au.writeD4AsyncRequired(xmlWrtr, 0, 0, stylesheet_ref);
1577 out << xmlWrtr.get_doc();
1578 out << flush;
1579 BESDEBUG(MODULE, prolog << "Sent AsyncAccepted" << endl);
1580 }
1581
1582 return true;
1583 }
1584
1585 return false;
1586}
1587
1606libdap::DMR *
1608{
1609 BES_STOPWATCH_START(MODULE, prolog + "Timer");
1610 BESDEBUG(MODULE , prolog << "BEGIN" << endl);
1611
1612 unique_ptr<DMR> dmr = setup_dap4_intern_data(obj, dhi);
1613
1614 intern_dap4_data_grp(dmr->root());
1615
1616 return dmr.release();
1617}
1618
1619libdap::DMR *
1620BESDapResponseBuilder::process_dap4_dmr(BESResponseObject *obj, BESDataHandlerInterface &dhi)
1621{
1622 BES_STOPWATCH_START(MODULE, prolog + "Timer");
1623 BESDEBUG(MODULE , prolog << "BEGIN" << endl);
1624
1625 unique_ptr<DMR> dmr = setup_dap4_intern_data(obj, dhi);
1626
1627 return dmr.release();
1628}
1629
1630unique_ptr<DMR>
1631BESDapResponseBuilder::setup_dap4_intern_data(BESResponseObject *obj, BESDataHandlerInterface &dhi)
1632{
1633 dhi.first_container();
1634
1635 auto bdmr = dynamic_cast<BESDMRResponse *>(obj);
1636 if (!bdmr) throw BESInternalFatalError("Expected a BESDMRResponse instance", __FILE__, __LINE__);
1637
1638 unique_ptr<DMR> dmr(bdmr->get_dmr());
1639 // TL;DR Set the DMR managed by the BESResponseObject to nullptr to avoid calling ~DMR
1640 // twice on the same object.
1641 bdmr->set_dmr(nullptr);
1642 // Why this is here: In the past we designed the BESResponseObject class hierarchy to
1643 // manage the response object, which effectively means delete it when the BES is done
1644 // processing the request. We pass nullptr to set_dmr so that the BESResponseObject
1645 // does not call ~DMR since unique_ptr<> will do that for us.
1646
1647 // Set the correct context by following intern_dap2_data()
1648 set_dataset_name(dmr->filename());
1649 set_dap4ce(dhi.data[DAP4_CONSTRAINT]);
1650 set_dap4function(dhi.data[DAP4_FUNCTION]);
1651 set_async_accepted(dhi.data[ASYNC]);
1652 set_store_result(dhi.data[STORE_RESULT]);
1653
1654 if (!d_dap4function.empty()) {
1655 D4BaseTypeFactory d4_factory;
1656 unique_ptr<DMR> function_result(new DMR(&d4_factory, "function_results"));
1657
1658 // Function modules load their functions onto this list. The list is
1659 // part of libdap, not the BES.
1660 if (!ServerFunctionsList::TheList()) {
1661 stringstream msg;
1662 msg << "The function expression could not be evaluated because ";
1663 msg << "there are no server-side functions defined on this server.";
1664 throw BESSyntaxUserError(msg.str(),__FILE__,__LINE__);
1665 }
1666
1667 D4FunctionEvaluator parser(dmr.get(), ServerFunctionsList::TheList());
1668 bool parse_ok = parser.parse(d_dap4function);
1669 if (!parse_ok){
1670 stringstream msg;
1671 msg << "Failed to parse the provided DAP4 server-side function expression: " << d_dap4function;
1672 throw BESSyntaxUserError(msg.str(),__FILE__,__LINE__);
1673 }
1674
1675 parser.eval(function_result.get());
1676
1677 // Now use the results of running the functions for the remainder of the
1678 // send_data operation.
1679 dap4_process_ce_for_intern_data(*function_result);
1680
1681 return function_result;
1682 }
1683 else {
1684 BESDEBUG(MODULE , prolog << "Processing the constraint expression. " << endl);
1686 return dmr;
1687 }
1688}
1689
1690void BESDapResponseBuilder::intern_dap4_data_grp(libdap::D4Group* grp) {
1691 for (auto i = grp->var_begin(), e = grp->var_end(); i != e; ++i) {
1692 BESDEBUG(MODULE , "BESDapResponseBuilder::intern_dap4_data() - "<< (*i)->name() <<endl);
1693 if ((*i)->send_p()) {
1694 BESDEBUG(MODULE , "BESDapResponseBuilder::intern_dap4_data() Obtain data- "<< (*i)->name() <<endl);
1695 (*i)->intern_data();
1696 }
1697 }
1698
1699 for (auto gi = grp->grp_begin(), ge = grp->grp_end(); gi != ge; ++gi) {
1700 BESDEBUG(MODULE , "BESDapResponseBuilder::intern_dap4_data() group- "<< (*gi)->name() <<endl);
1701 intern_dap4_data_grp(*gi);
1702 }
1703}
std::string get_container_type() const
retrieve the type of data this container holds, such as cedar or netcdf.
Holds a DDS object within the BES.
Cache the results from server functions.
virtual libdap::DDS * get_or_cache_dataset(libdap::DDS *dds, const std::string &constraint)
Return a DDS loaded with data that can be serialized back to a client.
virtual std::string get_dataset_name() const
Get the dataset name.
virtual libdap::DMR * intern_dap4_data(BESResponseObject *obj, BESDataHandlerInterface &dhi)
std::string d_dap4function
DAP4 Constraint expression.
virtual std::string get_dap4function() const
Get the DAP4 server side function expression.
virtual void set_dataset_name(const std::string &_dataset)
Set the dataset pathname.
virtual void split_ce(libdap::ConstraintEvaluator &eval, const std::string &expr="")
virtual std::string get_ce() const
Get the constraint expression.
std::string d_dap2ce
Name of the dataset/database.
virtual libdap::DDS * process_dap2_dds(BESResponseObject *obj, BESDataHandlerInterface &dhi)
Transmit data.
virtual void serialize_dap4_data(std::ostream &out, libdap::DMR &dmr, bool with_mime_headers=true)
virtual libdap::DDS * intern_dap2_data(BESResponseObject *obj, BESDataHandlerInterface &dhi)
virtual void send_dds(std::ostream &out, libdap::DDS **dds, libdap::ConstraintEvaluator &eval, bool constrained=false, bool with_mime_headers=true)
Transmit a DDS.
virtual void set_dap4function(const std::string &_func)
virtual std::string get_dap4ce() const
Get the DAP4 constraint expression.
virtual void dap4_process_ce_for_intern_data(libdap::DMR &dmr)
Parse the DAP4 CE and throw if the request is too large.
virtual void set_dap4ce(const std::string &_ce)
std::string d_async_accepted
Version string for the library's default protocol version.
virtual void send_ddx(std::ostream &out, libdap::DDS **dds, libdap::ConstraintEvaluator &eval, bool with_mime_headers=true)
virtual void serialize_dap2_data_dds(std::ostream &out, libdap::DDS **dds, libdap::ConstraintEvaluator &eval, bool ce_eval=true)
virtual bool store_dap4_result(std::ostream &out, libdap::DMR &dmr)
virtual void set_ce(std::string _ce)
std::string d_btp_func_ce
DAP4 Server Side Function expression.
std::string d_dap4ce
DAP2 Constraint expression.
Represents an OPeNDAP DataDDS DAP2 data object within the BES.
Structure storing information used by the BES to handle the request.
std::map< std::string, std::string > data
the map of string data that will be required for the current request.
void first_container()
set the container pointer to the first container in the containers list
BESContainer * container
pointer to current container in this interface
exception thrown if an internal error is found and is fatal to the BES
Represents a specific data type request handler.
virtual BESResponseObject * get_response_object()
return the current response object
Abstract base class representing a specific set of information in response to a request to the BES.
virtual string store_dap4_result(libdap::DMR &dmr, const string &constraint, BESDapResponseBuilder *rb)
static BESStoredDapResultCache * get_instance()
error thrown if there is a user syntax error in the request or any other user error
static void conditional_timeout_cancel()
Checks if the timeout alarm should be canceled based on the value of the BES key BES....
Definition BESUtil.cc:898
static std::string assemblePath(const std::string &firstPart, const std::string &secondPart, bool leadingSlash=false, bool trailingSlash=false)
Assemble path fragments making sure that they are separated by a single '/' character.
Definition BESUtil.cc:804
static RequestServiceTimer * TheTimer()
Return a pointer to a singleton timer instance. If an instance does not exist it will create and init...
void throw_if_timeout_expired(const std::string &message, const std::string &file, const int line)
Checks the RequestServiceTimer to determine if the time spent servicing the request at this point has...
void get_value(const std::string &s, std::string &val, bool &found)
Retrieve the value of a given key, if set.
static TheBESKeys * TheKeys()
Access to the singleton.
Definition TheBESKeys.cc:85