libdap  Updated for version 3.20.6
libdap4 is an implementation of OPeNDAP's DAP protocol.
DODSFilter.cc
1 
2 // -*- mode: c++; c-basic-offset:4 -*-
3 
4 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
5 // Access Protocol.
6 
7 // Copyright (c) 2002,2003 OPeNDAP, Inc.
8 // Author: James Gallagher <jgallagher@opendap.org>
9 //
10 // This library is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU Lesser General Public
12 // License as published by the Free Software Foundation; either
13 // version 2.1 of the License, or (at your option) any later version.
14 //
15 // This library is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 // Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public
21 // License along with this library; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 //
24 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
25 
26 // (c) COPYRIGHT URI/MIT 1997-1999
27 // Please read the full copyright statement in the file COPYRIGHT_URI.
28 //
29 // Authors:
30 // jhrg,jimg James Gallagher <jgallagher@gso.uri.edu>
31 
32 // Implementation of the DODSFilter class. This class is used to build dods
33 // filter programs which, along with a CGI program, comprise OPeNDAP servers.
34 // jhrg 8/26/97
35 
36 
37 #include "config.h"
38 
39 #include <signal.h>
40 
41 #ifndef WIN32
42 #include <unistd.h> // for getopt
43 #include <sys/wait.h>
44 #else
45 #include <io.h>
46 #include <fcntl.h>
47 #include <process.h>
48 #endif
49 
50 #include <iostream>
51 #include <sstream>
52 #include <string>
53 #include <algorithm>
54 #include <cstdlib>
55 #include <cstring>
56 
57 #ifdef HAVE_UUID_UUID_H
58 #include <uuid/uuid.h> // used to build CID header value for data ddx
59 #elif defined(HAVE_UUID_H)
60 #include <uuid.h>
61 #else
62 #error "Could not find UUID library header"
63 #endif
64 
65 #include <GetOpt.h>
66 
67 #include "DAS.h"
68 #include "DDS.h"
69 #include "debug.h"
70 #include "mime_util.h"
71 #include "Ancillary.h"
72 #include "util.h"
73 #include "escaping.h"
74 #include "DODSFilter.h"
75 #include "XDRStreamMarshaller.h"
76 #include "InternalErr.h"
77 
78 #ifndef WIN32
79 #include "SignalHandler.h"
80 #include "EventHandler.h"
81 #include "AlarmHandler.h"
82 #endif
83 
84 #define CRLF "\r\n" // Change here, expr-test.cc and DODSFilter.cc
85 
86 using namespace std;
87 
88 namespace libdap {
89 
90 const string usage =
91  "Usage: <handler name> -o <response> -u <url> [options ...] [data set]\n\
92  \n\
93  options: -o <response>: DAS, DDS, DataDDS, DDX, BLOB or Version (Required)\n\
94  -u <url>: The complete URL minus the CE (required for DDX)\n\
95  -c: Compress the response using the deflate algorithm.\n\
96  -e <expr>: When returning a DataDDS, use <expr> as the constraint.\n\
97  -v <version>: Use <version> as the version number\n\
98  -d <dir>: Look for ancillary file in <dir> (deprecated).\n\
99  -f <file>: Look for ancillary data in <file> (deprecated).\n\
100  -r <dir>: Use <dir> as a cache directory\n\
101  -l <time>: Conditional request; if data source is unchanged since\n\
102  <time>, return an HTTP 304 response.\n\
103  -t <seconds>: Timeout the handler after <seconds>.\n\
104  -h: This message.";
105 
170 DODSFilter::DODSFilter(int argc, char *argv[]) throw(Error)
171 {
172  initialize(argc, argv);
173 
174  DBG(cerr << "d_comp: " << d_comp << endl);
175  DBG(cerr << "d_dap2ce: " << d_dap2ce << endl);
176  DBG(cerr << "d_cgi_ver: " << d_cgi_ver << endl);
177  DBG(cerr << "d_response: " << d_response << endl);
178  DBG(cerr << "d_anc_dir: " << d_anc_dir << endl);
179  DBG(cerr << "d_anc_file: " << d_anc_file << endl);
180  DBG(cerr << "d_cache_dir: " << d_cache_dir << endl);
181  DBG(cerr << "d_conditional_request: " << d_conditional_request << endl);
182  DBG(cerr << "d_if_modified_since: " << d_if_modified_since << endl);
183  DBG(cerr << "d_url: " << d_url << endl);
184  DBG(cerr << "d_timeout: " << d_timeout << endl);
185 }
186 
187 DODSFilter::~DODSFilter()
188 {
189 }
190 
193 void
194 DODSFilter::initialize()
195 {
196  // Set default values. Don't use the C++ constructor initialization so
197  // that a subclass can have more control over this process.
198  d_comp = false;
199  d_bad_options = false;
200  d_conditional_request = false;
201  d_dataset = "";
202  d_dap2ce = "";
203  d_cgi_ver = "";
204  d_anc_dir = "";
205  d_anc_file = "";
206  d_cache_dir = "";
207  d_response = Unknown_Response;;
208  d_anc_das_lmt = 0;
209  d_anc_dds_lmt = 0;
210  d_if_modified_since = -1;
211  d_url = "";
212  d_program_name = "Unknown";
213  d_timeout = 0;
214 
215 #ifdef WIN32
216  // We want serving from win32 to behave in a manner
217  // similar to the UNIX way - no CR->NL terminated lines
218  // in files. Hence stdout goes to binary mode.
219  _setmode(_fileno(stdout), _O_BINARY);
220 #endif
221 }
222 
234 void
235 DODSFilter::initialize(int argc, char *argv[])
236 {
237  initialize();
238 
239  d_program_name = argv[0];
240 
241  // This should be specialized by a subclass. This may throw Error.
242  int next_arg = process_options(argc, argv);
243 
244  // Look at what's left after processing the command line options. Either
245  // there MUST be a dataset name OR the caller is asking for version
246  // information. If neither is true, then the options are bad.
247  if (next_arg < argc) {
248  d_dataset = argv[next_arg];
249  d_dataset = www2id(d_dataset, "%", "%20");
250  }
251  else if (get_response() != Version_Response)
252  print_usage(); // Throws Error
253 }
254 
263 int
264 DODSFilter::process_options(int argc, char *argv[])
265 {
266  DBG(cerr << "Entering process_options... ");
267 
268  int option_char;
269  GetOpt getopt (argc, argv, "ce: v: d: f: r: l: o: u: t: ");
270 
271  while ((option_char = getopt()) != -1) {
272  switch (option_char) {
273  case 'c': d_comp = true; break;
274  case 'e': set_ce(getopt.optarg); break;
275  case 'v': set_cgi_version(getopt.optarg); break;
276  case 'd': d_anc_dir = getopt.optarg; break;
277  case 'f': d_anc_file = getopt.optarg; break;
278  case 'r': d_cache_dir = getopt.optarg; break;
279  case 'o': set_response(getopt.optarg); break;
280  case 'u': set_URL(getopt.optarg); break;
281  case 't': d_timeout = atoi(getopt.optarg); break;
282  case 'l':
283  d_conditional_request = true;
284  d_if_modified_since
285  = static_cast<time_t>(strtol(getopt.optarg, NULL, 10));
286  break;
287  case 'h': print_usage();
288  break;
289  // exit(1);
290  // Removed 12/29/2011; exit should
291  // not be called by a library. NB:
292  // print_usage() throws Error.
293  default: print_usage(); // Throws Error
294  break;
295  }
296  }
297 
298  DBGN(cerr << "exiting." << endl);
299 
300  return getopt.optind; // return the index of the next argument
301 }
302 
307 bool
308 DODSFilter::is_conditional() const
309 {
310  return d_conditional_request;
311 }
312 
326 void
327 DODSFilter::set_cgi_version(string version)
328 {
329  d_cgi_ver = version;
330 }
331 
337 string
338 DODSFilter::get_cgi_version() const
339 {
340  return d_cgi_ver;
341 }
342 
349 string
350 DODSFilter::get_ce() const
351 {
352  return d_dap2ce;
353 }
354 
355 void
356 DODSFilter::set_ce(string _ce)
357 {
358  d_dap2ce = www2id(_ce, "%", "%20");
359 }
360 
369 string
370 DODSFilter::get_dataset_name() const
371 {
372  return d_dataset;
373 }
374 
375 void
376 DODSFilter::set_dataset_name(const string ds)
377 {
378  d_dataset = www2id(ds, "%", "%20");
379 }
380 
384 string
385 DODSFilter::get_URL() const
386 {
387  return d_url;
388 }
389 
392 void
393 DODSFilter::set_URL(const string &url)
394 {
395  if (url.find('?') != url.npos)
396  print_usage(); // Throws Error
397 
398  d_url = url;
399 }
400 
408 string
409 DODSFilter::get_dataset_version() const
410 {
411  return "";
412 }
413 
420 void DODSFilter::set_response(const string &r)
421 {
422  if (r == "DAS" || r == "das") {
423  d_response = DAS_Response;
424  d_action = "das" ;
425  }
426  else if (r == "DDS" || r == "dds") {
427  d_response = DDS_Response;
428  d_action = "dds" ;
429  }
430  else if (r == "DataDDS" || r == "dods") {
431  d_response = DataDDS_Response;
432  d_action = "dods" ;
433  }
434  else if (r == "DDX" || r == "ddx") {
435  d_response = DDX_Response;
436  d_action = "ddx" ;
437  }
438  else if (r == "DataDDX" || r == "dataddx") {
439  d_response = DataDDX_Response;
440  d_action = "dataddx" ;
441  }
442  else if (r == "Version") {
443  d_response = Version_Response;
444  d_action = "version" ;
445  }
446  else
447  print_usage(); // Throws Error
448 }
449 
452 DODSFilter::get_response() const
453 {
454  return d_response;
455 }
456 
458 string DODSFilter::get_action() const
459 {
460  return d_action;
461 }
462 
483 time_t
484 DODSFilter::get_dataset_last_modified_time() const
485 {
486  return last_modified_time(d_dataset);
487 }
488 
498 time_t
499 DODSFilter::get_das_last_modified_time(const string &anc_location) const
500 {
501  DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
502  << anc_location << "call faf(das) d_dataset=" << d_dataset
503  << " d_anc_file=" << d_anc_file << endl);
504 
505  string name
506  = Ancillary::find_ancillary_file(d_dataset, "das",
507  (anc_location == "") ? d_anc_dir : anc_location,
508  d_anc_file);
509 
510  return max((name != "") ? last_modified_time(name) : 0,
511  get_dataset_last_modified_time());
512 }
513 
521 time_t
522 DODSFilter::get_dds_last_modified_time(const string &anc_location) const
523 {
524  DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
525  << anc_location << "call faf(dds) d_dataset=" << d_dataset
526  << " d_anc_file=" << d_anc_file << endl);
527 
528  string name
529  = Ancillary::find_ancillary_file(d_dataset, "dds",
530  (anc_location == "") ? d_anc_dir : anc_location,
531  d_anc_file);
532 
533  return max((name != "") ? last_modified_time(name) : 0,
534  get_dataset_last_modified_time());
535 }
536 
550 time_t
551 DODSFilter::get_data_last_modified_time(const string &anc_location) const
552 {
553  DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
554  << anc_location << "call faf(both) d_dataset=" << d_dataset
555  << " d_anc_file=" << d_anc_file << endl);
556 
557  string dds_name
558  = Ancillary::find_ancillary_file(d_dataset, "dds",
559  (anc_location == "") ? d_anc_dir : anc_location,
560  d_anc_file);
561  string das_name
562  = Ancillary::find_ancillary_file(d_dataset, "das",
563  (anc_location == "") ? d_anc_dir : anc_location,
564  d_anc_file);
565 
566  time_t m = max((das_name != "") ? last_modified_time(das_name) : (time_t)0,
567  (dds_name != "") ? last_modified_time(dds_name) : (time_t)0);
568  // Note that this is a call to get_dataset_... not get_data_...
569  time_t n = get_dataset_last_modified_time();
570 
571  return max(m, n);
572 }
573 
581 time_t
582 DODSFilter::get_request_if_modified_since() const
583 {
584  return d_if_modified_since;
585 }
586 
593 string
594 DODSFilter::get_cache_dir() const
595 {
596  return d_cache_dir;
597 }
598 
603 void
604 DODSFilter::set_timeout(int t)
605 {
606  d_timeout = t;
607 }
608 
610 int
611 DODSFilter::get_timeout() const
612 {
613  return d_timeout;
614 }
615 
627 void
628 DODSFilter::establish_timeout(FILE *stream) const
629 {
630 #ifndef WIN32
631  if (d_timeout > 0) {
632  SignalHandler *sh = SignalHandler::instance();
633  EventHandler *old_eh = sh->register_handler(SIGALRM, new AlarmHandler(stream));
634  delete old_eh;
635  alarm(d_timeout);
636  }
637 #endif
638 }
639 
640 void
641 DODSFilter::establish_timeout(ostream &stream) const
642 {
643 #ifndef WIN32
644  if (d_timeout > 0) {
645  SignalHandler *sh = SignalHandler::instance();
646  EventHandler *old_eh = sh->register_handler(SIGALRM, new AlarmHandler(stream));
647  delete old_eh;
648  alarm(d_timeout);
649  }
650 #endif
651 }
652 
653 static const char *emessage = "DODS internal server error; usage error. Please report this to the dataset maintainer, or to the opendap-tech@opendap.org mailing list.";
654 
664 void
665 DODSFilter::print_usage() const
666 {
667  // Write a message to the WWW server error log file.
668  ErrMsgT(usage.c_str());
669 
670  throw Error(emessage);
671 }
672 
678 void
679 DODSFilter::send_version_info() const
680 {
681  do_version(d_cgi_ver, get_dataset_version());
682 }
683 
695 void
696 DODSFilter::send_das(FILE *out, DAS &das, const string &anc_location,
697  bool with_mime_headers) const
698 {
699  ostringstream oss;
700  send_das(oss, das, anc_location, with_mime_headers);
701  fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
702 }
703 
715 void
716 DODSFilter::send_das(ostream &out, DAS &das, const string &anc_location,
717  bool with_mime_headers) const
718 {
719  time_t das_lmt = get_das_last_modified_time(anc_location);
720  if (is_conditional()
721  && das_lmt <= get_request_if_modified_since()
722  && with_mime_headers) {
724  }
725  else {
726  if (with_mime_headers)
727  set_mime_text(out, dods_das, d_cgi_ver, x_plain, das_lmt);
728  das.print(out);
729  }
730  out << flush ;
731 }
732 
733 void
734 DODSFilter::send_das(DAS &das, const string &anc_location,
735  bool with_mime_headers) const
736 {
737  send_das(cout, das, anc_location, with_mime_headers);
738 }
739 
756 void
757 DODSFilter::send_dds(FILE *out, DDS &dds, ConstraintEvaluator &eval,
758  bool constrained,
759  const string &anc_location,
760  bool with_mime_headers) const
761 {
762  ostringstream oss;
763  send_dds(oss, dds, eval, constrained, anc_location, with_mime_headers);
764  fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
765 }
766 
783 void
784 DODSFilter::send_dds(ostream &out, DDS &dds, ConstraintEvaluator &eval,
785  bool constrained,
786  const string &anc_location,
787  bool with_mime_headers) const
788 {
789  // If constrained, parse the constraint. Throws Error or InternalErr.
790  if (constrained)
791  eval.parse_constraint(d_dap2ce, dds);
792 
793  if (eval.functional_expression())
794  throw Error("Function calls can only be used with data requests. To see the structure of the underlying data source, reissue the URL without the function.");
795 
796  time_t dds_lmt = get_dds_last_modified_time(anc_location);
797  if (is_conditional()
798  && dds_lmt <= get_request_if_modified_since()
799  && with_mime_headers) {
801  }
802  else {
803  if (with_mime_headers)
804  set_mime_text(out, dods_dds, d_cgi_ver, x_plain, dds_lmt);
805  if (constrained)
806  dds.print_constrained(out);
807  else
808  dds.print(out);
809  }
810 
811  out << flush ;
812 }
813 
814 void
815 DODSFilter::send_dds(DDS &dds, ConstraintEvaluator &eval,
816  bool constrained, const string &anc_location,
817  bool with_mime_headers) const
818 {
819  send_dds(cout, dds, eval, constrained, anc_location, with_mime_headers);
820 }
821 
822 // 'lmt' unused. Should it be used to supply a LMT or removed from the
823 // method? jhrg 8/9/05
824 void
825 DODSFilter::functional_constraint(BaseType &var, DDS &dds,
826  ConstraintEvaluator &eval, FILE *out) const
827 {
828  ostringstream oss;
829  functional_constraint(var, dds, eval, oss);
830  fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
831 }
832 
833 // 'lmt' unused. Should it be used to supply a LMT or removed from the
834 // method? jhrg 8/9/05
835 void
836 DODSFilter::functional_constraint(BaseType &var, DDS &dds,
837  ConstraintEvaluator &eval, ostream &out) const
838 {
839  out << "Dataset {\n" ;
840  var.print_decl(out, " ", true, false, true);
841  out << "} function_value;\n" ;
842  out << "Data:\n" ;
843 
844  out << flush ;
845 
846  // Grab a stream that encodes using XDR.
847  XDRStreamMarshaller m( out ) ;
848 
849  try {
850  // In the following call to serialize, suppress CE evaluation.
851  var.serialize(eval, dds, m, false);
852  }
853  catch (Error &e) {
854  throw;
855  }
856 }
857 
858 void
859 DODSFilter::dataset_constraint(DDS & dds, ConstraintEvaluator & eval,
860  FILE * out, bool ce_eval) const
861 {
862  ostringstream oss;
863  dataset_constraint(dds, eval, oss, ce_eval);
864  fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
865 }
866 
867 void
868 DODSFilter::dataset_constraint(DDS & dds, ConstraintEvaluator & eval,
869  ostream &out, bool ce_eval) const
870 {
871  // send constrained DDS
872  dds.print_constrained(out);
873  out << "Data:\n" ;
874  out << flush ;
875 
876  // Grab a stream that encodes using XDR.
877  XDRStreamMarshaller m( out ) ;
878 
879  try {
880  // Send all variables in the current projection (send_p())
881  for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++)
882  if ((*i)->send_p()) {
883  DBG(cerr << "Sending " << (*i)->name() << endl);
884  (*i)->serialize(eval, dds, m, ce_eval);
885  }
886  }
887  catch (Error & e) {
888  throw;
889  }
890 }
891 
892 void
893 DODSFilter::dataset_constraint_ddx(DDS & dds, ConstraintEvaluator & eval,
894  ostream &out, const string &boundary,
895  const string &start, bool ce_eval) const
896 {
897  // Write the MPM headers for the DDX (text/xml) part of the response
898  set_mime_ddx_boundary(out, boundary, start, dods_ddx);
899 
900  // Make cid
901  uuid_t uu;
902  uuid_generate(uu);
903  char uuid[37];
904  uuid_unparse(uu, &uuid[0]);
905  char domain[256];
906  if (getdomainname(domain, 255) != 0 || strlen(domain) == 0)
907  strncpy(domain, "opendap.org", 255);
908 
909  string cid = string(&uuid[0]) + "@" + string(&domain[0]);
910 
911  // Send constrained DDX with a data blob reference
912  dds.print_xml_writer(out, true, cid);
913 
914  // Write the MPM headers for the data part of the response.
915  set_mime_data_boundary(out, boundary, cid, dap4_data, binary);
916 
917  // Grab a stream that encodes using XDR.
918  XDRStreamMarshaller m( out ) ;
919 
920  try {
921  // Send all variables in the current projection (send_p())
922  for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++)
923  if ((*i)->send_p()) {
924  DBG(cerr << "Sending " << (*i)->name() << endl);
925  (*i)->serialize(eval, dds, m, ce_eval);
926  }
927  }
928  catch (Error & e) {
929  throw;
930  }
931 }
932 
949 void
950 DODSFilter::send_data(DDS & dds, ConstraintEvaluator & eval,
951  FILE * data_stream, const string & anc_location,
952  bool with_mime_headers) const
953 {
954  ostringstream oss;
955  send_data(dds, eval, oss, anc_location, with_mime_headers);
956  fwrite(oss.str().data(), sizeof(char), oss.str().length(), data_stream);
957 }
958 
975 void
976 DODSFilter::send_data(DDS & dds, ConstraintEvaluator & eval,
977  ostream & data_stream, const string & anc_location,
978  bool with_mime_headers) const
979 {
980  // If this is a conditional request and the server should send a 304
981  // response, do that and exit. Otherwise, continue on and send the full
982  // response.
983  time_t data_lmt = get_data_last_modified_time(anc_location);
984  if (is_conditional()
985  && data_lmt <= get_request_if_modified_since()
986  && with_mime_headers) {
987  set_mime_not_modified(data_stream);
988  return;
989  }
990  // Set up the alarm.
991  establish_timeout(data_stream);
992  dds.set_timeout(d_timeout);
993 
994  eval.parse_constraint(d_dap2ce, dds); // Throws Error if the ce doesn't
995  // parse.
996 
997  dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
998 
999  // Start sending the response...
1000 
1001  // Handle *functional* constraint expressions specially
1002 #if 0
1003  if (eval.functional_expression()) {
1004  // Get the result and then start sending the headers. This provides a
1005  // way to send errors back to the client w/o colliding with the
1006  // normal response headers. There's some duplication of code with this
1007  // and the else-clause.
1008  BaseType *var = eval.eval_function(dds, d_dataset);
1009  if (!var)
1010  throw Error(unknown_error, "Error calling the CE function.");
1011 
1012  if (with_mime_headers)
1013  set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
1014 
1015  data_stream << flush ;
1016 
1017  functional_constraint(*var, dds, eval, data_stream);
1018  delete var;
1019  var = 0;
1020  }
1021 #endif
1022  if (eval.function_clauses()) {
1023  DDS *fdds = eval.eval_function_clauses(dds);
1024  if (with_mime_headers)
1025  set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
1026 
1027  dataset_constraint(*fdds, eval, data_stream, false);
1028  delete fdds;
1029  }
1030  else {
1031  if (with_mime_headers)
1032  set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
1033 
1034  dataset_constraint(dds, eval, data_stream);
1035  }
1036 
1037  data_stream << flush ;
1038 }
1039 
1050 void
1051 DODSFilter::send_ddx(DDS &dds, ConstraintEvaluator &eval, FILE *out,
1052  bool with_mime_headers) const
1053 {
1054  ostringstream oss;
1055  send_ddx(dds, eval, oss, with_mime_headers);
1056  fwrite(oss.str().data(), sizeof(char), oss.str().length(), out);
1057 }
1058 
1069 void
1070 DODSFilter::send_ddx(DDS &dds, ConstraintEvaluator &eval, ostream &out,
1071  bool with_mime_headers) const
1072 {
1073  // If constrained, parse the constraint. Throws Error or InternalErr.
1074  if (!d_dap2ce.empty())
1075  eval.parse_constraint(d_dap2ce, dds);
1076 
1077  if (eval.functional_expression())
1078  throw Error("Function calls can only be used with data requests. To see the structure of the underlying data source, reissue the URL without the function.");
1079 
1080  time_t dds_lmt = get_dds_last_modified_time(d_anc_dir);
1081 
1082  // If this is a conditional request and the server should send a 304
1083  // response, do that and exit. Otherwise, continue on and send the full
1084  // response.
1085  if (is_conditional() && dds_lmt <= get_request_if_modified_since()
1086  && with_mime_headers) {
1087  set_mime_not_modified(out);
1088  return;
1089  }
1090  else {
1091  if (with_mime_headers)
1092  set_mime_text(out, dods_ddx, d_cgi_ver, x_plain, dds_lmt);
1093  dds.print_xml_writer(out, !d_dap2ce.empty(), "");
1094  }
1095 }
1096 
1117 void
1118 DODSFilter::send_data_ddx(DDS & dds, ConstraintEvaluator & eval,
1119  ostream & data_stream, const string &start,
1120  const string &boundary, const string & anc_location,
1121  bool with_mime_headers) const
1122 {
1123  // If this is a conditional request and the server should send a 304
1124  // response, do that and exit. Otherwise, continue on and send the full
1125  // response.
1126  time_t data_lmt = get_data_last_modified_time(anc_location);
1127  if (is_conditional()
1128  && data_lmt <= get_request_if_modified_since()
1129  && with_mime_headers) {
1130  set_mime_not_modified(data_stream);
1131  return;
1132  }
1133  // Set up the alarm.
1134  establish_timeout(data_stream);
1135  dds.set_timeout(d_timeout);
1136 
1137  eval.parse_constraint(d_dap2ce, dds); // Throws Error if the ce doesn't
1138  // parse.
1139 
1140  dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
1141 
1142  // Start sending the response...
1143 
1144  // Handle *functional* constraint expressions specially
1145 #if 0
1146  if (eval.functional_expression()) {
1147  BaseType *var = eval.eval_function(dds, d_dataset);
1148  if (!var)
1149  throw Error(unknown_error, "Error calling the CE function.");
1150 
1151  if (with_mime_headers)
1152  set_mime_multipart(data_stream, boundary, start, dods_data_ddx,
1153  d_cgi_ver, x_plain, data_lmt);
1154  data_stream << flush ;
1155  BaseTypeFactory btf;
1156  DDS var_dds(&btf, var->name());
1157  var->set_send_p(true);
1158  var_dds.add_var(var);
1159  serialize_dap2_data_ddx(var_dds, eval, data_stream, boundary, start);
1160 
1161  // functional_constraint_ddx(*var, dds, eval, data_stream, boundary);
1162  delete var;
1163  var = 0;
1164  }
1165 #endif
1166  if (eval.function_clauses()) {
1167  DDS *fdds = eval.eval_function_clauses(dds);
1168  if (with_mime_headers)
1169  set_mime_multipart(data_stream, boundary, start, dods_data_ddx,
1170  d_cgi_ver, x_plain, data_lmt);
1171  data_stream << flush ;
1172  dataset_constraint(*fdds, eval, data_stream, false);
1173  delete fdds;
1174  }
1175  else {
1176  if (with_mime_headers)
1177  set_mime_multipart(data_stream, boundary, start, dods_data_ddx,
1178  d_cgi_ver, x_plain, data_lmt);
1179  data_stream << flush ;
1180  dataset_constraint_ddx(dds, eval, data_stream, boundary, start);
1181  }
1182 
1183  data_stream << flush ;
1184 
1185  if (with_mime_headers)
1186  data_stream << CRLF << "--" << boundary << "--" << CRLF;
1187 }
1188 
1189 } // namespace libdap
1190 
virtual string name() const
Returns the name of the class instance.
Definition: BaseType.cc:320
virtual void print_decl(FILE *out, string space=" ", bool print_semi=true, bool constraint_info=false, bool constrained=false)
Print an ASCII representation of the variable structure.
Definition: BaseType.cc:1003
void ErrMsgT(const string &Msgt)
Logs an error message.
Definition: mime_util.cc:222
void print(FILE *out)
Print the entire DDS to the specified file.
Definition: DDS.cc:1023
time_t last_modified_time(const string &name)
Definition: mime_util.cc:95
Vars_iter var_begin()
Return an iterator to the first variable.
Definition: DDS.cc:799
STL namespace.
void set_mime_text(FILE *out, ObjectType type, const string &ver, EncodingType enc, const time_t last_modified)
Definition: mime_util.cc:381
void print_xml_writer(ostream &out, bool constrained, const string &blob="")
Definition: DDS.cc:1433
Definition: GetOpt.h:38
BaseType * eval_function(DDS &dds, const std::string &dataset)
Evaluate a function-valued constraint expression.
virtual void print(FILE *out, bool dereference=false)
Definition: DAS.cc:331
top level DAP object to house generic methods
Definition: AISConnect.cc:30
virtual bool serialize(ConstraintEvaluator &eval, DDS &dds, Marshaller &m, bool ce_eval=true)
Move data to the net, then remove them from the object.
Definition: BaseType.cc:937
virtual void set_send_p(bool state)
Definition: BaseType.cc:568
DDS * eval_function_clauses(DDS &dds)
Evaluate a function-valued constraint expression that contains several function calls.
void set_mime_binary(FILE *out, ObjectType type, const string &ver, EncodingType enc, const time_t last_modified)
Definition: mime_util.cc:623
void parse_constraint(const std::string &constraint, DDS &dds)
Parse the constraint expression given the current DDS.
void set_mime_not_modified(FILE *out)
Send a `Not Modified&#39; response.
Definition: mime_util.cc:1145
string www2id(const string &in, const string &escape, const string &except)
Definition: escaping.cc:220
Vars_iter var_end()
Return an iterator.
Definition: DDS.cc:811
Evaluate a constraint expression.
void tag_nested_sequences()
Traverse DDS, set Sequence leaf nodes.
Definition: DDS.cc:930
bool functional_expression()
Does the current constraint expression return a BaseType pointer? This method does not evaluate the c...
The basic data type for the DODS DAP types.
Definition: BaseType.h:117
Hold attribute data for a DAP2 dataset.
Definition: DAS.h:121
bool function_clauses()
Does the current constraint expression contain function clauses.
A class for error processing.
Definition: Error.h:92
EventHandler * register_handler(int signum, EventHandler *eh, bool override=false)
Marshaller that knows how serialize dap data objects to a C++ iostream using XDR. ...
bool do_version(const string &script_ver, const string &dataset_ver)
Send a version number.
Definition: mime_util.cc:189
void print_constrained(FILE *out)
Print a constrained DDS to the specified file.
Definition: DDS.cc:1332