OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
BESDapResponseBuilder.cc
Go to the documentation of this file.
1 // -*- mode: c++; c-basic-offset:4 -*-
2 
3 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
4 // Access Protocol.
5 
6 // Copyright (c) 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 #include <uuid/uuid.h> // used to build CID header value for data ddx
31 
32 #ifndef WIN32
33 #include <sys/wait.h>
34 #else
35 #include <io.h>
36 #include <fcntl.h>
37 #include <process.h>
38 #endif
39 
40 #include <iostream>
41 #include <string>
42 #include <sstream>
43 #include <fstream>
44 
45 #include <cstring>
46 #include <ctime>
47 
48 //#define DODS_DEBUG
49 
50 #include <DAS.h>
51 #include <DDS.h>
52 #include <ConstraintEvaluator.h>
53 #include <DDXParserSAX2.h>
54 #include <Ancillary.h>
55 #include <XDRStreamMarshaller.h>
56 #include <XDRFileUnMarshaller.h>
57 #if 0
58 // #include <DMR.h>
59 // #include <XMLWriter.h>
60 #endif
61 #include <debug.h>
62 #include <mime_util.h> // for last_modified_time() and rfc_822_date()
63 #include <escaping.h>
64 #include <util.h>
65 #ifndef WIN32
66 #include <SignalHandler.h>
67 #include <EventHandler.h>
68 #include <AlarmHandler.h>
69 #endif
70 
71 #include "BESDapResponseCache.h"
72 #include "BESDapResponseBuilder.h"
73 #include "BESDebug.h"
74 
75 #define CRLF "\r\n" // Change here, expr-test.cc
76 #define DAP_PROTOCOL_VERSION "3.2"
77 
78 using namespace std;
79 using namespace libdap;
80 
84 {
85  // Set default values. Don't use the C++ constructor initialization so
86  // that a subclass can have more control over this process.
87  d_dataset = "";
88  d_ce = "";
89  d_btp_func_ce = "";
90  d_timeout = 0;
91 
92  d_default_protocol = DAP_PROTOCOL_VERSION;
93 
94  d_response_cache = 0;
95 }
96 
100 {
101  if (!d_response_cache)
102  d_response_cache = BESDapResponseCache::get_instance();
103 
104  return d_response_cache;
105 }
106 
108 {
109  if (d_response_cache) delete d_response_cache;
110 
111  // If an alarm was registered, delete it. The register code in SignalHandler
112  // always deletes the old alarm handler object, so only the one returned by
113  // remove_handler needs to be deleted at this point.
114  delete dynamic_cast<AlarmHandler*>(SignalHandler::instance()->remove_handler(SIGALRM));
115 }
116 
124 {
125  return d_ce;
126 }
127 
139 {
140  d_ce = www2id(_ce, "%", "%20");
141 }
142 
152 {
153  return d_dataset;
154 }
155 
167 {
168  d_dataset = www2id(ds, "%", "%20");
169 }
170 
177 {
178  d_timeout = t;
179 }
180 
183 {
184  return d_timeout;
185 }
186 
190 void BESDapResponseBuilder::establish_timeout(ostream &stream) const
191 {
192 #ifndef WIN32
193  if (d_timeout > 0) {
194  SignalHandler *sh = SignalHandler::instance();
195  EventHandler *old_eh = sh->register_handler(SIGALRM, new AlarmHandler(stream));
196  delete old_eh;
197  alarm(d_timeout);
198  }
199 #endif
200 }
201 
202 static string::size_type
203 find_closing_paren(const string &ce, string::size_type pos)
204 {
205  // Iterate over the string finding all ( or ) characters until the matching ) is found.
206  // For each ( found, increment count. When a ) is found and count is zero, it is the
207  // matching closing paren, otherwise, decrement count and keep looking.
208  int count = 1;
209  do {
210  pos = ce.find_first_of("()", pos + 1);
211  if (pos == string::npos)
212  throw Error(malformed_expr, "Expected to find a matching closing parenthesis in " + ce);
213 
214  if (ce[pos] == '(')
215  ++count;
216  else
217  --count; // must be ')'
218 
219  } while (count > 0);
220 
221  return pos;
222 }
223 
230 void
231 BESDapResponseBuilder::split_ce(ConstraintEvaluator &eval, const string &expr)
232 {
233  DBG(cerr << "Entering ResponseBuilder::split_ce" << endl);
234  string ce;
235  if (!expr.empty())
236  ce = expr;
237  else
238  ce = d_ce;
239 
240  string btp_function_ce = "";
241  string::size_type pos = 0;
242  DBG(cerr << "ce: " << ce << endl);
243 
244  // This hack assumes that the functions are listed first. Look for the first
245  // open paren and the last closing paren to accommodate nested function calls
246  string::size_type first_paren = ce.find("(", pos);
247  string::size_type closing_paren = string::npos;
248  if (first_paren != string::npos)
249  closing_paren = find_closing_paren(ce, first_paren); //ce.find(")", pos);
250 
251 
252  while (first_paren != string::npos && closing_paren != string::npos) {
253  // Maybe a BTP function; get the name of the potential function
254  string name = ce.substr(pos, first_paren-pos);
255  DBG(cerr << "name: " << name << endl);
256  // is this a BTP function
257  btp_func f;
258  if (eval.find_function(name, &f)) {
259  // Found a BTP function
260  if (!btp_function_ce.empty())
261  btp_function_ce += ",";
262  btp_function_ce += ce.substr(pos, closing_paren+1-pos);
263  ce.erase(pos, closing_paren+1-pos);
264  if (ce[pos] == ',')
265  ce.erase(pos, 1);
266  }
267  else {
268  pos = closing_paren + 1;
269  // exception?
270  if (pos < ce.length() && ce.at(pos) == ',')
271  ++pos;
272  }
273 
274  first_paren = ce.find("(", pos);
275  closing_paren = ce.find(")", pos);
276  }
277 
278  DBG(cerr << "Modified constraint: " << ce << endl);
279  DBG(cerr << "BTP Function part: " << btp_function_ce << endl);
280 
281  d_ce = ce;
282  d_btp_func_ce = btp_function_ce;
283 }
284 
299 void BESDapResponseBuilder::send_das(ostream &out, DAS &das, bool with_mime_headers) const
300 {
301  if (with_mime_headers)
302  set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset), "2.0");
303 
304  das.print(out);
305 
306  out << flush;
307 }
308 
326 void BESDapResponseBuilder::send_das(ostream &out, DDS &dds, ConstraintEvaluator &eval, bool constrained, bool with_mime_headers)
327 {
328  // Set up the alarm.
329  establish_timeout(out);
330  dds.set_timeout(d_timeout);
331 
332  if (!constrained) {
333  if (with_mime_headers)
334  set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset), "2.0");
335 
336  dds.print_das(out);
337  out << flush;
338 
339  return;
340  }
341 
342  split_ce(eval);
343 
344  // If there are functions, parse them and eval.
345  // Use that DDS and parse the non-function ce
346  // Serialize using the second ce and the second dds
347  if (!d_btp_func_ce.empty()) {
348  DDS *fdds = 0;
349  string cache_token = "";
350 
351  if (responseCache()) {
352  DBG(cerr << "Using the cache for the server function CE" << endl);
353  fdds = responseCache()->cache_dataset(dds, d_btp_func_ce, this, &eval, cache_token);
354  }
355  else {
356  DBG(cerr << "Cache not found; (re)calculating" << endl);
357  eval.parse_constraint(d_btp_func_ce, dds);
358  fdds = eval.eval_function_clauses(dds);
359  }
360 
361  if (with_mime_headers)
362  set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
363 
364  fdds->print_das(out);
365 
366  if (responseCache())
367  responseCache()->unlock_and_close(cache_token);
368 
369  delete fdds;
370  }
371  else {
372  DBG(cerr << "Simple constraint" << endl);
373 
374  eval.parse_constraint(d_ce, dds); // Throws Error if the ce doesn't parse.
375 
376  if (with_mime_headers)
377  set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
378 
379  dds.print_das(out);
380  }
381 
382  out << flush;
383 }
384 
403 void BESDapResponseBuilder::send_dds(ostream &out, DDS &dds, ConstraintEvaluator &eval, bool constrained,
404  bool with_mime_headers)
405 {
406  if (!constrained) {
407  if (with_mime_headers)
408  set_mime_text(out, dods_dds, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
409 
410  dds.print(out);
411  out << flush;
412  return;
413  }
414 
415  // Set up the alarm.
416  establish_timeout(out);
417  dds.set_timeout(d_timeout);
418 
419  // Split constraint into two halves
420  split_ce(eval);
421 
422  // If there are functions, parse them and eval.
423  // Use that DDS and parse the non-function ce
424  // Serialize using the second ce and the second dds
425  if (!d_btp_func_ce.empty()) {
426  string cache_token = "";
427  DDS *fdds = 0;
428 
429  if (responseCache()) {
430  DBG(cerr << "Using the cache for the server function CE" << endl);
431  fdds = responseCache()->cache_dataset(dds, d_btp_func_ce, this, &eval, cache_token);
432  }
433  else {
434  DBG(cerr << "Cache not found; (re)calculating" << endl);
435  eval.parse_constraint(d_btp_func_ce, dds);
436  fdds = eval.eval_function_clauses(dds);
437  }
438 
439  // Server functions might mark variables to use their read()
440  // methods. Clear that so the CE in d_ce will control what is
441  // sent. If that is empty (there was only a function call) all
442  // of the variables in the intermediate DDS (i.e., the function
443  // result) will be sent.
444  fdds->mark_all(false);
445 
446  eval.parse_constraint(d_ce, *fdds);
447 
448  if (with_mime_headers)
449  set_mime_text(out, dods_dds, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
450 
451  fdds->print_constrained(out);
452 
453  if (responseCache())
454  responseCache()->unlock_and_close(cache_token);
455 
456  delete fdds;
457  }
458  else {
459  DBG(cerr << "Simple constraint" << endl);
460 
461  eval.parse_constraint(d_ce, dds); // Throws Error if the ce doesn't parse.
462 
463  if (with_mime_headers)
464  set_mime_text(out, dods_dds, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
465 
466  dds.print_constrained(out);
467  }
468 
469  out << flush;
470 }
471 
475 void BESDapResponseBuilder::dataset_constraint(ostream &out, DDS & dds, ConstraintEvaluator & eval, bool ce_eval)
476 {
477  // send constrained DDS
478  DBG(cerr << "Inside dataset_constraint" << endl);
479 
480  dds.print_constrained(out);
481  out << "Data:\n";
482  out << flush;
483 
484  XDRStreamMarshaller m(out);
485 
486  try {
487  // Send all variables in the current projection (send_p())
488  for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++)
489  if ((*i)->send_p()) {
490  (*i)->serialize(eval, dds, m, ce_eval);
491  }
492  }
493  catch (Error & e) {
494  throw;
495  }
496 }
497 
504 void BESDapResponseBuilder::dataset_constraint_ddx(ostream &out, DDS &dds, ConstraintEvaluator &eval,
505  const string &boundary, const string &start, bool ce_eval)
506 {
507  // Write the MPM headers for the DDX (text/xml) part of the response
508  libdap::set_mime_ddx_boundary(out, boundary, start, dap4_ddx, x_plain);
509 
510  // Make cid
511  uuid_t uu;
512  uuid_generate(uu);
513  char uuid[37];
514  uuid_unparse(uu, &uuid[0]);
515  char domain[256];
516  if (getdomainname(domain, 255) != 0 || strlen(domain) == 0)
517  strncpy(domain, "opendap.org", 255);
518 
519  string cid = string(&uuid[0]) + "@" + string(&domain[0]);
520  // Send constrained DDX with a data blob reference
521  dds.print_xml_writer(out, true, cid);
522 
523  // write the data part mime headers here
524  set_mime_data_boundary(out, boundary, cid, dap4_data, x_plain);
525 
526  XDRStreamMarshaller m(out);
527 
528  // Send all variables in the current projection (send_p()). In DAP4,
529  // all of the top-level variables are serialized with their checksums.
530  // Internal variables are not.
531  for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++) {
532  if ((*i)->send_p()) {
533  (*i)->serialize(eval, dds, m, ce_eval);
534  }
535  }
536 }
537 
554 void BESDapResponseBuilder::send_data(ostream &data_stream, DDS &dds, ConstraintEvaluator &eval, bool with_mime_headers)
555 {
556 
557  // cerr << "***** BESDapResponseBuilder::send_data() - BEGIN" << endl;
558  // Set up the alarm.
559  establish_timeout(data_stream);
560  dds.set_timeout(d_timeout);
561 
562  // Split constraint into two halves
563  split_ce(eval);
564 
565  // If there are functions, parse them and eval.
566  // Use that DDS and parse the non-function ce
567  // Serialize using the second ce and the second dds
568  if (!d_btp_func_ce.empty()) {
569  BESDEBUG("dap", "Found function(s) in CE: " << d_btp_func_ce << endl);
570  string cache_token = "";
571  DDS *fdds = 0;
572 
573  if (responseCache()) {
574  BESDEBUG("dap", "Using the cache for the server function CE" << endl);
575  fdds = responseCache()->cache_dataset(dds, d_btp_func_ce, this, &eval, cache_token);
576  }
577  else {
578  BESDEBUG("dap", "Cache not found; (re)calculating" << endl);
579  eval.parse_constraint(d_btp_func_ce, dds);
580  fdds = eval.eval_function_clauses(dds);
581  }
582 
583  DBG(fdds->print_constrained(cerr));
584 
585  // Server functions might mark variables to use their read()
586  // methods. Clear that so the CE in d_ce will control what is
587  // sent. If that is empty (there was only a function call) all
588  // of the variables in the intermediate DDS (i.e., the function
589  // result) will be sent.
590  fdds->mark_all(false);
591 
592  eval.parse_constraint(d_ce, *fdds);
593 
594  fdds->tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
595 
596  if (fdds->get_response_limit() != 0 && fdds->get_request_size(true) > fdds->get_response_limit()) {
597  string msg = "The Request for " + long_to_string(dds.get_request_size(true) / 1024)
598  + "KB is too large; requests for this user are limited to "
599  + long_to_string(dds.get_response_limit() / 1024) + "KB.";
600  throw Error(msg);
601  }
602 
603  if (with_mime_headers)
604  set_mime_binary(data_stream, dods_data, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
605 
606  DBG(cerr << "About to call dataset_constraint" << endl);
607  dataset_constraint(data_stream, *fdds, eval, false);
608 
609  if (responseCache())
610  responseCache()->unlock_and_close(cache_token);
611 
612  delete fdds;
613  }
614  else {
615  BESDEBUG("dap", "Simple constraint" << endl);
616 
617  eval.parse_constraint(d_ce, dds); // Throws Error if the ce doesn't parse.
618 
619  dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
620 
621  if (dds.get_response_limit() != 0 && dds.get_request_size(true) > dds.get_response_limit()) {
622  string msg = "The Request for " + long_to_string(dds.get_request_size(true) / 1024)
623  + "KB is too large; requests for this user are limited to "
624  + long_to_string(dds.get_response_limit() / 1024) + "KB.";
625  throw Error(msg);
626  }
627 
628  if (with_mime_headers)
629  set_mime_binary(data_stream, dods_data, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
630 
631  dataset_constraint(data_stream, dds, eval);
632  }
633 
634  data_stream << flush;
635  // cerr << "***** BESDapResponseBuilder::send_data() - END" << endl;
636 
637 }
638 
652 void BESDapResponseBuilder::send_ddx(ostream &out, DDS &dds, ConstraintEvaluator &eval, bool with_mime_headers)
653 {
654  if (d_ce.empty()) {
655  if (with_mime_headers)
656  set_mime_text(out, dap4_ddx, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
657 
658  dds.print_xml_writer(out, false /*constrained */, "");
659  //dds.print(out);
660  out << flush;
661  return;
662  }
663 
664  // Set up the alarm.
665  establish_timeout(out);
666  dds.set_timeout(d_timeout);
667 
668  // Split constraint into two halves
669  split_ce(eval);
670 
671  // If there are functions, parse them and eval.
672  // Use that DDS and parse the non-function ce
673  // Serialize using the second ce and the second dds
674  if (!d_btp_func_ce.empty()) {
675  string cache_token = "";
676  DDS *fdds = 0;
677 
678  if (responseCache()) {
679  DBG(cerr << "Using the cache for the server function CE" << endl);
680  fdds = responseCache()->cache_dataset(dds, d_btp_func_ce, this, &eval, cache_token);
681  }
682  else {
683  DBG(cerr << "Cache not found; (re)calculating" << endl);
684  eval.parse_constraint(d_btp_func_ce, dds);
685  fdds = eval.eval_function_clauses(dds);
686  }
687 
688  // Server functions might mark variables to use their read()
689  // methods. Clear that so the CE in d_ce will control what is
690  // sent. If that is empty (there was only a function call) all
691  // of the variables in the intermediate DDS (i.e., the function
692  // result) will be sent.
693  fdds->mark_all(false);
694 
695  eval.parse_constraint(d_ce, *fdds);
696 
697  if (with_mime_headers)
698  set_mime_text(out, dap4_ddx, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
699 
700  fdds->print_constrained(out);
701 
702  if (responseCache())
703  responseCache()->unlock_and_close(cache_token);
704 
705  delete fdds;
706  }
707  else {
708  DBG(cerr << "Simple constraint" << endl);
709 
710  eval.parse_constraint(d_ce, dds); // Throws Error if the ce doesn't parse.
711 
712  if (with_mime_headers)
713  set_mime_text(out, dap4_ddx, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
714 
715  //dds.print_constrained(out);
716  dds.print_xml_writer(out, true, "");
717  }
718 
719  out << flush;
720 }
721 
722 #if 0
723 void BESDapResponseBuilder::send_dmr(ostream &out, DMR &dmr, ConstraintEvaluator &/*eval*/, bool constrained,
724  bool with_mime_headers)
725 {
726  if (!constrained) {
727  if (with_mime_headers)
728  set_mime_text(out, dap4_dmr, x_plain, last_modified_time(d_dataset), dmr.dap_version());
729 
730  XMLWriter xml;
731  dmr.print_dap4(xml, constrained);
732  out << xml.get_doc();
733  out << flush;
734  return;
735  }
736 
737  // FIXME Add support for constraints
738 #if 0
739  // Set up the alarm.
740  establish_timeout(out);
741  // dds.set_timeout(d_timeout);
742 
743  // Split constraint into two halves
744  split_ce(eval);
745 
746  // If there are functions, parse them and eval.
747  // Use that DDS and parse the non-function ce
748  // Serialize using the second ce and the second dds
749  if (!d_btp_func_ce.empty()) {
750  string cache_token = "";
751  DMR *fdmr = 0;
752 
753  if (responseCache()) {
754  DBG(cerr << "Using the cache for the server function CE" << endl);
755  fdmr = responseCache()->cache_dataset(dmr, d_btp_func_ce, this, &eval, cache_token);
756  }
757  else {
758  DBG(cerr << "Cache not found; (re)calculating" << endl);
759  eval.parse_constraint(d_btp_func_ce, dmr);
760  fdmr = eval.eval_function_clauses(dmr);
761  }
762 
763  // Server functions might mark variables to use their read()
764  // methods. Clear that so the CE in d_ce will control what is
765  // sent. If that is empty (there was only a function call) all
766  // of the variables in the intermediate DDS (i.e., the function
767  // result) will be sent.
768  fdmr->mark_all(false);
769 
770  eval.parse_constraint(d_ce, *fdmr);
771 
772  if (with_mime_headers)
773  set_mime_text(out, dap4_dmr, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
774 
775  fdmr->print_constrained(out);
776 
777  if (responseCache())
778  responseCache()->unlock_and_close(cache_token);
779 
780  delete fdmr;
781  }
782  else {
783  DBG(cerr << "Simple constraint" << endl);
784 
785  eval.parse_constraint(d_ce, dmr); // Throws Error if the ce doesn't parse.
786 
787  if (with_mime_headers)
788  set_mime_text(out, dap4_dmr, x_plain, last_modified_time(d_dataset), dmr.dap_version());
789 
790  dmr.print_constrained(out);
791  }
792 #endif
793 
794  out << flush;
795 }
796 
797 void BESDapResponseBuilder::send_dap4_data(ostream & data_stream, DMR & dmr, ConstraintEvaluator & eval, bool with_mime_headers)
798 {
799 // FIXME Fill in this placeholder - get DMR working first then add CE support and then
800  // get this working
801 #if 0
802  // Set up the alarm.
803  establish_timeout(data_stream);
804  //dds.set_timeout(d_timeout);
805 #if 0
806  eval.parse_constraint(d_ce, dmr); // Throws Error if the ce doesn't parse.
807 #endif
808  if (dds.get_response_limit() != 0 && dds.get_request_size(true) > dds.get_response_limit()) {
809  string msg = "The Request for " + long_to_string(dds.get_request_size(true) / 1024)
810  + "KB is too large; requests for this user are limited to "
811  + long_to_string(dds.get_response_limit() / 1024) + "KB.";
812  throw Error(msg);
813  }
814 
815  //dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
816 
817  // Start sending the response...
818 
819  // Handle *functional* constraint expressions specially
820  if (eval.function_clauses()) {
821  // We could unique_ptr<DDS> here to avoid memory leaks if
822  // dataset_constraint_ddx() throws an exception.
823  DDS *fdds = eval.eval_function_clauses(dds);
824  try {
825  if (with_mime_headers)
826  set_mime_multipart(data_stream, boundary, start, dap4_data_ddx, x_plain, last_modified_time(d_dataset));
827  data_stream << flush;
828  dataset_constraint_ddx(data_stream, *fdds, eval, boundary, start);
829  }
830  catch (...) {
831  delete fdds;
832  throw;
833  }
834  delete fdds;
835  }
836  else {
837  if (with_mime_headers)
838  set_mime_multipart(data_stream, boundary, start, dap4_data_ddx, x_plain, last_modified_time(d_dataset));
839  data_stream << flush;
840  dataset_constraint_ddx(data_stream, dds, eval, boundary, start);
841  }
842 
843  data_stream << flush;
844 
845  if (with_mime_headers)
846  data_stream << CRLF << "--" << boundary << "--" << CRLF;
847 #endif
848 }
849 #endif
850 
868 void BESDapResponseBuilder::send_data_ddx(ostream & data_stream, DDS & dds, ConstraintEvaluator & eval, const string &start,
869  const string &boundary, bool with_mime_headers)
870 {
871  // Set up the alarm.
872  establish_timeout(data_stream);
873  dds.set_timeout(d_timeout);
874 
875  eval.parse_constraint(d_ce, dds); // Throws Error if the ce doesn't parse.
876 
877  if (dds.get_response_limit() != 0 && dds.get_request_size(true) > dds.get_response_limit()) {
878  string msg = "The Request for " + long_to_string(dds.get_request_size(true) / 1024)
879  + "KB is too large; requests for this user are limited to "
880  + long_to_string(dds.get_response_limit() / 1024) + "KB.";
881  throw Error(msg);
882  }
883 
884  dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
885 
886  // Start sending the response...
887 
888  // Handle *functional* constraint expressions specially
889  if (eval.function_clauses()) {
890  // We could unique_ptr<DDS> here to avoid memory leaks if
891  // dataset_constraint_ddx() throws an exception.
892  DDS *fdds = eval.eval_function_clauses(dds);
893  try {
894  if (with_mime_headers)
895  set_mime_multipart(data_stream, boundary, start, dap4_data_ddx, x_plain, last_modified_time(d_dataset));
896  data_stream << flush;
897  dataset_constraint_ddx(data_stream, *fdds, eval, boundary, start);
898  }
899  catch (...) {
900  delete fdds;
901  throw;
902  }
903  delete fdds;
904  }
905  else {
906  if (with_mime_headers)
907  set_mime_multipart(data_stream, boundary, start, dap4_data_ddx, x_plain, last_modified_time(d_dataset));
908  data_stream << flush;
909  dataset_constraint_ddx(data_stream, dds, eval, boundary, start);
910  }
911 
912  data_stream << flush;
913 
914  if (with_mime_headers)
915  data_stream << CRLF << "--" << boundary << "--" << CRLF;
916 }
917 
virtual void dataset_constraint(std::ostream &out, libdap::DDS &dds, libdap::ConstraintEvaluator &eval, bool ce_eval=true)
Build/return the BLOB part of the DAP2 data response.
virtual std::string get_ce() const
Return the entire constraint expression in a string.
#define CRLF
static BESDapResponseCache * get_instance()
Get the default instance of the BESDapResponseCache object.
This class is used to cache DAP2 response objects.
virtual void send_das(std::ostream &out, libdap::DAS &das, bool with_mime_headers=true) const
virtual void send_data(std::ostream &data_stream, libdap::DDS &dds, libdap::ConstraintEvaluator &eval, bool with_mime_headers=true)
Send the data in the DDS object back to the client program.
virtual BESDapResponseCache * responseCache()
Lazy getter for the ResponseCache.
void initialize()
Called when initializing a ResponseBuilder that's not going to be passed command line arguments...
virtual void split_ce(libdap::ConstraintEvaluator &eval, const std::string &expr="")
Split the CE so that the server functions that compute new values are separated into their own string...
#define DAP_PROTOCOL_VERSION
void set_timeout(int timeout=0)
Set the server's timeout value.
virtual void set_dataset_name(const std::string _dataset)
Set the dataset name, which is a string used to access the dataset on the machine running the server...
virtual void send_ddx(std::ostream &out, libdap::DDS &dds, libdap::ConstraintEvaluator &eval, bool with_mime_headers=true)
Send the DDX response.
virtual void send_data_ddx(std::ostream &data_stream, libdap::DDS &dds, libdap::ConstraintEvaluator &eval, const std::string &start, const std::string &boundary, bool with_mime_headers=true)
Send the data in the DDS object back to the client program.
int get_timeout() const
Get the server's timeout value.
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
virtual void dataset_constraint_ddx(std::ostream &out, libdap::DDS &dds, libdap::ConstraintEvaluator &eval, const std::string &boundary, const std::string &start, bool ce_eval=true)
Build/return the DDX and the BLOB part of the DAP3.x data response.
virtual void set_ce(std::string _ce)
Set the constraint expression.
virtual void establish_timeout(std::ostream &stream) const
Use values of this instance to establish a timeout alarm for the server.
virtual std::string get_dataset_name() const
The ``dataset name'' is the filename or other string that the filter program will use to access the d...
virtual void send_dds(std::ostream &out, libdap::DDS &dds, libdap::ConstraintEvaluator &eval, bool constrained=false, bool with_mime_headers=true)
This function formats and prints an ASCII representation of a DDS on stdout.