libdap++  Updated for version 3.8.2
DODSFilter.cc
Go to the documentation of this file.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 static char rcsid[] not_used =
40  {"$Id: DODSFilter.cc 24281 2011-03-09 00:22:31Z jimg $"
41  };
42 
43 #include <signal.h>
44 
45 #ifndef WIN32
46 #include <unistd.h> // for getopt
47 #include <sys/wait.h>
48 #else
49 #include <io.h>
50 #include <fcntl.h>
51 #include <process.h>
52 #endif
53 
54 #include <iostream>
55 #include <string>
56 #include <algorithm>
57 #include <cstdlib>
58 #include <cstring>
59 
60 #include <uuid/uuid.h> // used to build CID header value for data ddx
61 
62 #include <GetOpt.h>
63 
64 #include "DAS.h"
65 #include "DDS.h"
66 #include "debug.h"
67 #include "mime_util.h"
68 #include "Ancillary.h"
69 #include "util.h"
70 #include "escaping.h"
71 #include "DODSFilter.h"
72 #if FILE_METHODS
73 #include "XDRFileMarshaller.h"
74 #endif
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 //#undef FILE_METHODS
87 
88 using namespace std;
89 
90 namespace libdap {
91 
92 const string usage =
93  "Usage: <handler name> -o <response> -u <url> [options ...] [data set]\n\
94  \n\
95  options: -o <response>: DAS, DDS, DataDDS, DDX, BLOB or Version (Required)\n\
96  -u <url>: The complete URL minus the CE (required for DDX)\n\
97  -c: Compress the response using the deflate algorithm.\n\
98  -e <expr>: When returning a DataDDS, use <expr> as the constraint.\n\
99  -v <version>: Use <version> as the version number\n\
100  -d <dir>: Look for ancillary file in <dir> (deprecated).\n\
101  -f <file>: Look for ancillary data in <file> (deprecated).\n\
102  -r <dir>: Use <dir> as a cache directory\n\
103  -l <time>: Conditional request; if data source is unchanged since\n\
104  <time>, return an HTTP 304 response.\n\
105  -t <seconds>: Timeout the handler after <seconds>.\n\
106  -h: This message.";
107 
172 DODSFilter::DODSFilter(int argc, char *argv[]) throw(Error)
173 {
174  initialize(argc, argv);
175 
176  DBG(cerr << "d_comp: " << d_comp << endl);
177  DBG(cerr << "d_ce: " << d_ce << endl);
178  DBG(cerr << "d_cgi_ver: " << d_cgi_ver << endl);
179  DBG(cerr << "d_response: " << d_response << endl);
180  DBG(cerr << "d_anc_dir: " << d_anc_dir << endl);
181  DBG(cerr << "d_anc_file: " << d_anc_file << endl);
182  DBG(cerr << "d_cache_dir: " << d_cache_dir << endl);
183  DBG(cerr << "d_conditional_request: " << d_conditional_request << endl);
184  DBG(cerr << "d_if_modified_since: " << d_if_modified_since << endl);
185  DBG(cerr << "d_url: " << d_url << endl);
186  DBG(cerr << "d_timeout: " << d_timeout << endl);
187 }
188 
189 DODSFilter::~DODSFilter()
190 {
191 }
192 
195 void
196 DODSFilter::initialize()
197 {
198  // Set default values. Don't use the C++ constructor initialization so
199  // that a subclass can have more control over this process.
200  d_comp = false;
201  d_bad_options = false;
202  d_conditional_request = false;
203  d_dataset = "";
204  d_ce = "";
205  d_cgi_ver = "";
206  d_anc_dir = "";
207  d_anc_file = "";
208  d_cache_dir = "";
209  d_response = Unknown_Response;;
210  d_anc_das_lmt = 0;
211  d_anc_dds_lmt = 0;
212  d_if_modified_since = -1;
213  d_url = "";
214  d_program_name = "Unknown";
215  d_timeout = 0;
216 
217 #ifdef WIN32
218  // We want serving from win32 to behave in a manner
219  // similar to the UNIX way - no CR->NL terminated lines
220  // in files. Hence stdout goes to binary mode.
221  _setmode(_fileno(stdout), _O_BINARY);
222 #endif
223 }
224 
236 void
237 DODSFilter::initialize(int argc, char *argv[])
238 {
239  initialize();
240 
241  d_program_name = argv[0];
242 
243  // This should be specialized by a subclass. This may throw Error.
244  int next_arg = process_options(argc, argv);
245 
246  // Look at what's left after processing the command line options. Either
247  // there MUST be a dataset name OR the caller is asking for version
248  // information. If neither is true, then the options are bad.
249  if (next_arg < argc) {
250  d_dataset = argv[next_arg];
251  d_dataset = www2id(d_dataset, "%", "%20");
252  }
253  else if (get_response() != Version_Response)
254  print_usage(); // Throws Error
255 }
256 
265 int
266 DODSFilter::process_options(int argc, char *argv[])
267 {
268  DBG(cerr << "Entering process_options... ");
269 
270  int option_char;
271  GetOpt getopt (argc, argv, "ce: v: d: f: r: l: o: u: t: ");
272 
273  while ((option_char = getopt()) != EOF) {
274  switch (option_char) {
275  case 'c': d_comp = true; break;
276  case 'e': set_ce(getopt.optarg); break;
277  case 'v': set_cgi_version(getopt.optarg); break;
278  case 'd': d_anc_dir = getopt.optarg; break;
279  case 'f': d_anc_file = getopt.optarg; break;
280  case 'r': d_cache_dir = getopt.optarg; break;
281  case 'o': set_response(getopt.optarg); break;
282  case 'u': set_URL(getopt.optarg); break;
283  case 't': d_timeout = atoi(getopt.optarg); break;
284  case 'l':
285  d_conditional_request = true;
286  d_if_modified_since
287  = static_cast<time_t>(strtol(getopt.optarg, NULL, 10));
288  break;
289  case 'h': print_usage(); exit(1);
290  default: print_usage(); // Throws Error
291  }
292  }
293 
294  DBGN(cerr << "exiting." << endl);
295 
296  return getopt.optind; // return the index of the next argument
297 }
298 
303 bool
304 DODSFilter::is_conditional() const
305 {
306  return d_conditional_request;
307 }
308 
322 void
323 DODSFilter::set_cgi_version(string version)
324 {
325  d_cgi_ver = version;
326 }
327 
333 string
334 DODSFilter::get_cgi_version() const
335 {
336  return d_cgi_ver;
337 }
338 
345 string
346 DODSFilter::get_ce() const
347 {
348  return d_ce;
349 }
350 
351 void
352 DODSFilter::set_ce(string _ce)
353 {
354  d_ce = www2id(_ce, "%", "%20");
355 }
356 
365 string
366 DODSFilter::get_dataset_name() const
367 {
368  return d_dataset;
369 }
370 
371 void
372 DODSFilter::set_dataset_name(const string ds)
373 {
374  d_dataset = www2id(ds, "%", "%20");
375 }
376 
380 string
381 DODSFilter::get_URL() const
382 {
383  return d_url;
384 }
385 
388 void
389 DODSFilter::set_URL(const string &url)
390 {
391  if (url.find('?') != url.npos)
392  print_usage(); // Throws Error
393 
394  d_url = url;
395 }
396 
404 string
405 DODSFilter::get_dataset_version() const
406 {
407  return "";
408 }
409 
416 void DODSFilter::set_response(const string &r)
417 {
418  if (r == "DAS" || r == "das") {
419  d_response = DAS_Response;
420  d_action = "das" ;
421  }
422  else if (r == "DDS" || r == "dds") {
423  d_response = DDS_Response;
424  d_action = "dds" ;
425  }
426  else if (r == "DataDDS" || r == "dods") {
427  d_response = DataDDS_Response;
428  d_action = "dods" ;
429  }
430  else if (r == "DDX" || r == "ddx") {
431  d_response = DDX_Response;
432  d_action = "ddx" ;
433  }
434  else if (r == "DataDDX" || r == "dataddx") {
435  d_response = DataDDX_Response;
436  d_action = "dataddx" ;
437  }
438  else if (r == "Version") {
439  d_response = Version_Response;
440  d_action = "version" ;
441  }
442  else
443  print_usage(); // Throws Error
444 }
445 
448 DODSFilter::get_response() const
449 {
450  return d_response;
451 }
452 
454 string DODSFilter::get_action() const
455 {
456  return d_action;
457 }
458 
479 time_t
480 DODSFilter::get_dataset_last_modified_time() const
481 {
482  return last_modified_time(d_dataset);
483 }
484 
494 time_t
495 DODSFilter::get_das_last_modified_time(const string &anc_location) const
496 {
497  DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
498  << anc_location << "call faf(das) d_dataset=" << d_dataset
499  << " d_anc_file=" << d_anc_file << endl);
500 
501  string name
502  = Ancillary::find_ancillary_file(d_dataset, "das",
503  (anc_location == "") ? d_anc_dir : anc_location,
504  d_anc_file);
505 
506  return max((name != "") ? last_modified_time(name) : 0,
507  get_dataset_last_modified_time());
508 }
509 
517 time_t
518 DODSFilter::get_dds_last_modified_time(const string &anc_location) const
519 {
520  DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
521  << anc_location << "call faf(dds) d_dataset=" << d_dataset
522  << " d_anc_file=" << d_anc_file << endl);
523 
524  string name
525  = Ancillary::find_ancillary_file(d_dataset, "dds",
526  (anc_location == "") ? d_anc_dir : anc_location,
527  d_anc_file);
528 
529  return max((name != "") ? last_modified_time(name) : 0,
530  get_dataset_last_modified_time());
531 }
532 
546 time_t
547 DODSFilter::get_data_last_modified_time(const string &anc_location) const
548 {
549  DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
550  << anc_location << "call faf(both) d_dataset=" << d_dataset
551  << " d_anc_file=" << d_anc_file << endl);
552 
553  string dds_name
554  = Ancillary::find_ancillary_file(d_dataset, "dds",
555  (anc_location == "") ? d_anc_dir : anc_location,
556  d_anc_file);
557  string das_name
558  = Ancillary::find_ancillary_file(d_dataset, "das",
559  (anc_location == "") ? d_anc_dir : anc_location,
560  d_anc_file);
561 
562  time_t m = max((das_name != "") ? last_modified_time(das_name) : (time_t)0,
563  (dds_name != "") ? last_modified_time(dds_name) : (time_t)0);
564  // Note that this is a call to get_dataset_... not get_data_...
565  time_t n = get_dataset_last_modified_time();
566 
567  return max(m, n);
568 }
569 
577 time_t
578 DODSFilter::get_request_if_modified_since() const
579 {
580  return d_if_modified_since;
581 }
582 
589 string
590 DODSFilter::get_cache_dir() const
591 {
592  return d_cache_dir;
593 }
594 
599 void
600 DODSFilter::set_timeout(int t)
601 {
602  d_timeout = t;
603 }
604 
606 int
607 DODSFilter::get_timeout() const
608 {
609  return d_timeout;
610 }
611 
612 #if FILE_METHODS
613 
624 void
625 DODSFilter::establish_timeout(FILE *stream) const
626 {
627 #ifndef WIN32
628  if (d_timeout > 0) {
629  SignalHandler *sh = SignalHandler::instance();
630  EventHandler *old_eh = sh->register_handler(SIGALRM, new AlarmHandler(stream));
631  delete old_eh;
632  alarm(d_timeout);
633  }
634 #endif
635 }
636 #endif
637 
638 // FIXME
639 void
640 DODSFilter::establish_timeout(ostream &stream) const
641 {
642 #ifndef WIN32
643  if (d_timeout > 0) {
644  SignalHandler *sh = SignalHandler::instance();
645  EventHandler *old_eh = sh->register_handler(SIGALRM, new AlarmHandler(stream));
646  delete old_eh;
647  alarm(d_timeout);
648  }
649 #endif
650 }
651 
652 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.";
653 
663 void
664 DODSFilter::print_usage() const
665 {
666  // Write a message to the WWW server error log file.
667  ErrMsgT(usage.c_str());
668 
669  throw Error(unknown_error, emessage);
670 }
671 
677 void
678 DODSFilter::send_version_info() const
679 {
680  do_version(d_cgi_ver, get_dataset_version());
681 }
682 
683 #if FILE_METHODS
684 
695 void
696 DODSFilter::send_das(FILE *out, DAS &das, const string &anc_location,
697  bool with_mime_headers) const
698 {
699  time_t das_lmt = get_das_last_modified_time(anc_location);
700  if (is_conditional()
701  && das_lmt <= get_request_if_modified_since()
702  && with_mime_headers) {
704  }
705  else {
706  if (with_mime_headers)
707  set_mime_text(out, dods_das, d_cgi_ver, x_plain, das_lmt);
708  das.print(out);
709  }
710  fflush(out) ;
711 }
712 #endif
713 
725 void
726 DODSFilter::send_das(ostream &out, DAS &das, const string &anc_location,
727  bool with_mime_headers) const
728 {
729  time_t das_lmt = get_das_last_modified_time(anc_location);
730  if (is_conditional()
731  && das_lmt <= get_request_if_modified_since()
732  && with_mime_headers) {
734  }
735  else {
736  if (with_mime_headers)
737  set_mime_text(out, dods_das, d_cgi_ver, x_plain, das_lmt);
738  das.print(out);
739  }
740  out << flush ;
741 }
742 
743 void
744 DODSFilter::send_das(DAS &das, const string &anc_location,
745  bool with_mime_headers) const
746 {
747  send_das(cout, das, anc_location, with_mime_headers);
748 }
749 
750 #if FILE_METHODS
751 
767 void
768 DODSFilter::send_dds(FILE *out, DDS &dds, ConstraintEvaluator &eval,
769  bool constrained,
770  const string &anc_location,
771  bool with_mime_headers) const
772 {
773  // If constrained, parse the constraint. Throws Error or InternalErr.
774  if (constrained)
775  eval.parse_constraint(d_ce, dds);
776 
777  if (eval.functional_expression())
778  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.");
779 
780  time_t dds_lmt = get_dds_last_modified_time(anc_location);
781  if (is_conditional()
782  && dds_lmt <= get_request_if_modified_since()
783  && with_mime_headers) {
785  }
786  else {
787  if (with_mime_headers)
788  set_mime_text(out, dods_dds, d_cgi_ver, x_plain, dds_lmt);
789  if (constrained)
790  dds.print_constrained(out);
791  else
792  dds.print(out);
793  }
794 
795  fflush(out) ;
796 }
797 #endif
798 
815 void
816 DODSFilter::send_dds(ostream &out, DDS &dds, ConstraintEvaluator &eval,
817  bool constrained,
818  const string &anc_location,
819  bool with_mime_headers) const
820 {
821  // If constrained, parse the constraint. Throws Error or InternalErr.
822  if (constrained)
823  eval.parse_constraint(d_ce, dds);
824 
825  if (eval.functional_expression())
826  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.");
827 
828  time_t dds_lmt = get_dds_last_modified_time(anc_location);
829  if (is_conditional()
830  && dds_lmt <= get_request_if_modified_since()
831  && with_mime_headers) {
833  }
834  else {
835  if (with_mime_headers)
836  set_mime_text(out, dods_dds, d_cgi_ver, x_plain, dds_lmt);
837  if (constrained)
838  dds.print_constrained(out);
839  else
840  dds.print(out);
841  }
842 
843  out << flush ;
844 }
845 
846 void
847 DODSFilter::send_dds(DDS &dds, ConstraintEvaluator &eval,
848  bool constrained, const string &anc_location,
849  bool with_mime_headers) const
850 {
851  send_dds(cout, dds, eval, constrained, anc_location, with_mime_headers);
852 }
853 
854 #if FILE_METHODS
855 // 'lmt' unused. Should it be used to supply a LMT or removed from the
856 // method? jhrg 8/9/05
857 void
858 DODSFilter::functional_constraint(BaseType &var, DDS &dds,
859  ConstraintEvaluator &eval, FILE *out) const
860 {
861  fprintf(out, "Dataset {\n");
862  var.print_decl(out, " ", true, false, true);
863  fprintf(out, "} function_value;\n");
864  fprintf(out, "Data:\n");
865 
866  fflush(out);
867 
868  XDRFileMarshaller m( out ) ;
869 
870  try {
871  // In the following call to serialize, suppress CE evaluation.
872  var.serialize(eval, dds, m, false);
873  }
874  catch (Error &e) {
875  throw;
876  }
877 }
878 #endif
879 
880 // 'lmt' unused. Should it be used to supply a LMT or removed from the
881 // method? jhrg 8/9/05
882 void
883 DODSFilter::functional_constraint(BaseType &var, DDS &dds,
884  ConstraintEvaluator &eval, ostream &out) const
885 {
886  out << "Dataset {\n" ;
887  var.print_decl(out, " ", true, false, true);
888  out << "} function_value;\n" ;
889  out << "Data:\n" ;
890 
891  out << flush ;
892 
893  // Grab a stream that encodes using XDR.
894  XDRStreamMarshaller m( out ) ;
895 
896  try {
897  // In the following call to serialize, suppress CE evaluation.
898  var.serialize(eval, dds, m, false);
899  }
900  catch (Error &e) {
901  throw;
902  }
903 }
904 
905 #if FILE_METHODS
906 void
907 DODSFilter::dataset_constraint(DDS & dds, ConstraintEvaluator & eval,
908  FILE * out, bool ce_eval) const
909 {
910  // send constrained DDS
911  dds.print_constrained(out);
912  fprintf(out, "Data:\n");
913  fflush(out);
914 
915  // Grab a stream that encodes using XDR.
916  XDRFileMarshaller m( out ) ;
917 
918  try {
919  // Send all variables in the current projection (send_p())
920  for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++)
921  if ((*i)->send_p()) {
922  DBG(cerr << "Sending " << (*i)->name() << endl);
923  (*i)->serialize(eval, dds, m, ce_eval);
924  }
925  }
926  catch (Error & e) {
927  throw;
928  }
929 }
930 #endif
931 
932 void
933 DODSFilter::dataset_constraint(DDS & dds, ConstraintEvaluator & eval,
934  ostream &out, bool ce_eval) const
935 {
936  // send constrained DDS
937  dds.print_constrained(out);
938  out << "Data:\n" ;
939  out << flush ;
940 
941  // Grab a stream that encodes using XDR.
942  XDRStreamMarshaller m( out ) ;
943 
944  try {
945  // Send all variables in the current projection (send_p())
946  for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++)
947  if ((*i)->send_p()) {
948  DBG(cerr << "Sending " << (*i)->name() << endl);
949  (*i)->serialize(eval, dds, m, ce_eval);
950  }
951  }
952  catch (Error & e) {
953  throw;
954  }
955 }
956 
957 void
958 DODSFilter::dataset_constraint_ddx(DDS & dds, ConstraintEvaluator & eval,
959  ostream &out, const string &boundary,
960  const string &start, bool ce_eval) const
961 {
962  // Write the MPM headers for the DDX (text/xml) part of the response
963  set_mime_ddx_boundary(out, boundary, start, dap4_ddx);
964 
965  // Make cid
966  uuid_t uu;
967  uuid_generate(uu);
968  char uuid[37];
969  uuid_unparse(uu, &uuid[0]);
970  char domain[256];
971  if (getdomainname(domain, 255) != 0 || strlen(domain) == 0)
972  strncpy(domain, "opendap.org", 255);
973 
974  string cid = string(&uuid[0]) + "@" + string(&domain[0]);
975 
976  // Send constrained DDX with a data blob reference
977  dds.print_xml(out, true, cid);
978 
979  // Write the MPM headers for the data part of the response.
980  set_mime_data_boundary(out, boundary, cid, dap4_data, binary);
981 
982  // Grab a stream that encodes using XDR.
983  XDRStreamMarshaller m( out ) ;
984 
985  try {
986  // Send all variables in the current projection (send_p())
987  for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++)
988  if ((*i)->send_p()) {
989  DBG(cerr << "Sending " << (*i)->name() << endl);
990  (*i)->serialize(eval, dds, m, ce_eval);
991  }
992  }
993  catch (Error & e) {
994  throw;
995  }
996 }
997 
998 #if FILE_METHODS
999 
1015 void
1016 DODSFilter::send_data(DDS & dds, ConstraintEvaluator & eval,
1017  FILE * data_stream, const string & anc_location,
1018  bool with_mime_headers) const
1019 {
1020  // If this is a conditional request and the server should send a 304
1021  // response, do that and exit. Otherwise, continue on and send the full
1022  // response.
1023  time_t data_lmt = get_data_last_modified_time(anc_location);
1024  if (is_conditional()
1025  && data_lmt <= get_request_if_modified_since()
1026  && with_mime_headers) {
1027  set_mime_not_modified(data_stream);
1028  return;
1029  }
1030  // Set up the alarm.
1031  establish_timeout(data_stream);
1032  dds.set_timeout(d_timeout);
1033 
1034  eval.parse_constraint(d_ce, dds); // Throws Error if the ce doesn't
1035  // parse.
1036 
1037  dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
1038 
1039  // Start sending the response...
1040 
1041  // Handle *functional* constraint expressions specially
1042 #if 0
1043  if (eval.functional_expression()) {
1044  // Get the result and then start sending the headers. This provides a
1045  // way to send errors back to the client w/o colliding with the
1046  // normal response headers. There's some duplication of code with this
1047  // and the else-clause.
1048  BaseType *var = eval.eval_function(dds, d_dataset);
1049  if (!var)
1050  throw Error(unknown_error, "Error calling the CE function.");
1051 
1052 #if COMPRESSION_FOR_SERVER3
1053  if (with_mime_headers)
1054  set_mime_binary(data_stream, dods_data, d_cgi_ver,
1055  (compress) ? deflate : x_plain, data_lmt);
1056  fflush(data_stream);
1057 
1058  int childpid;
1059  if (compress)
1060  data_stream = compressor(data_stream, childpid);
1061 #endif
1062  if (with_mime_headers)
1063  set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
1064 
1065  fflush(data_stream);
1066 
1067  functional_constraint(*var, dds, eval, data_stream);
1068  delete var;
1069  var = 0;
1070  }
1071 #endif
1072  if (eval.function_clauses()) {
1073  DDS *fdds = eval.eval_function_clauses(dds);
1074 
1075  if (with_mime_headers)
1076  set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
1077 
1078  dataset_constraint(*fdds, eval, data_stream, false);
1079  delete fdds;
1080  }
1081  else {
1082  if (with_mime_headers)
1083  set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
1084 
1085  dataset_constraint(dds, eval, data_stream);
1086  }
1087 
1088  fflush(data_stream);
1089 }
1090 #endif
1091 
1108 void
1109 DODSFilter::send_data(DDS & dds, ConstraintEvaluator & eval,
1110  ostream & data_stream, const string & anc_location,
1111  bool with_mime_headers) const
1112 {
1113  // If this is a conditional request and the server should send a 304
1114  // response, do that and exit. Otherwise, continue on and send the full
1115  // response.
1116  time_t data_lmt = get_data_last_modified_time(anc_location);
1117  if (is_conditional()
1118  && data_lmt <= get_request_if_modified_since()
1119  && with_mime_headers) {
1120  set_mime_not_modified(data_stream);
1121  return;
1122  }
1123  // Set up the alarm.
1124  establish_timeout(data_stream);
1125  dds.set_timeout(d_timeout);
1126 
1127  eval.parse_constraint(d_ce, dds); // Throws Error if the ce doesn't
1128  // parse.
1129 
1130  dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
1131 
1132  // Start sending the response...
1133 
1134  // Handle *functional* constraint expressions specially
1135 #if 0
1136  if (eval.functional_expression()) {
1137  // Get the result and then start sending the headers. This provides a
1138  // way to send errors back to the client w/o colliding with the
1139  // normal response headers. There's some duplication of code with this
1140  // and the else-clause.
1141  BaseType *var = eval.eval_function(dds, d_dataset);
1142  if (!var)
1143  throw Error(unknown_error, "Error calling the CE function.");
1144 
1145  if (with_mime_headers)
1146  set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
1147 
1148  data_stream << flush ;
1149 
1150  functional_constraint(*var, dds, eval, data_stream);
1151  delete var;
1152  var = 0;
1153  }
1154 #endif
1155  if (eval.function_clauses()) {
1156  DDS *fdds = eval.eval_function_clauses(dds);
1157  if (with_mime_headers)
1158  set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
1159 
1160  dataset_constraint(*fdds, eval, data_stream, false);
1161  delete fdds;
1162  }
1163  else {
1164  if (with_mime_headers)
1165  set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
1166 
1167  dataset_constraint(dds, eval, data_stream);
1168  }
1169 
1170  data_stream << flush ;
1171 }
1172 
1173 #if FILE_METHODS
1174 
1184 void
1185 DODSFilter::send_ddx(DDS &dds, ConstraintEvaluator &eval, FILE *out,
1186  bool with_mime_headers) const
1187 {
1188  // If constrained, parse the constraint. Throws Error or InternalErr.
1189  if (!d_ce.empty())
1190  eval.parse_constraint(d_ce, dds);
1191 
1192  if (eval.functional_expression())
1193  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.");
1194 
1195  time_t dds_lmt = get_dds_last_modified_time(d_anc_dir);
1196 
1197  // If this is a conditional request and the server should send a 304
1198  // response, do that and exit. Otherwise, continue on and send the full
1199  // response.
1200  if (is_conditional() && dds_lmt <= get_request_if_modified_since()
1201  && with_mime_headers) {
1202  set_mime_not_modified(out);
1203  return;
1204  }
1205  else {
1206  if (with_mime_headers)
1207  set_mime_text(out, dap4_ddx, d_cgi_ver, x_plain, dds_lmt);
1208  dds.print_xml(out, !d_ce.empty(), "");
1209  }
1210 }
1211 #endif
1212 
1223 void
1224 DODSFilter::send_ddx(DDS &dds, ConstraintEvaluator &eval, ostream &out,
1225  bool with_mime_headers) const
1226 {
1227  // If constrained, parse the constraint. Throws Error or InternalErr.
1228  if (!d_ce.empty())
1229  eval.parse_constraint(d_ce, dds);
1230 
1231  if (eval.functional_expression())
1232  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.");
1233 
1234  time_t dds_lmt = get_dds_last_modified_time(d_anc_dir);
1235 
1236  // If this is a conditional request and the server should send a 304
1237  // response, do that and exit. Otherwise, continue on and send the full
1238  // response.
1239  if (is_conditional() && dds_lmt <= get_request_if_modified_since()
1240  && with_mime_headers) {
1241  set_mime_not_modified(out);
1242  return;
1243  }
1244  else {
1245  if (with_mime_headers)
1246  set_mime_text(out, dap4_ddx, d_cgi_ver, x_plain, dds_lmt);
1247  dds.print_xml(out, !d_ce.empty(), "");
1248  }
1249 }
1250 
1271 void
1272 DODSFilter::send_data_ddx(DDS & dds, ConstraintEvaluator & eval,
1273  ostream & data_stream, const string &start,
1274  const string &boundary, const string & anc_location,
1275  bool with_mime_headers) const
1276 {
1277  // If this is a conditional request and the server should send a 304
1278  // response, do that and exit. Otherwise, continue on and send the full
1279  // response.
1280  time_t data_lmt = get_data_last_modified_time(anc_location);
1281  if (is_conditional()
1282  && data_lmt <= get_request_if_modified_since()
1283  && with_mime_headers) {
1284  set_mime_not_modified(data_stream);
1285  return;
1286  }
1287  // Set up the alarm.
1288  establish_timeout(data_stream);
1289  dds.set_timeout(d_timeout);
1290 
1291  eval.parse_constraint(d_ce, dds); // Throws Error if the ce doesn't
1292  // parse.
1293 
1294  dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
1295 
1296  // Start sending the response...
1297 
1298  // Handle *functional* constraint expressions specially
1299 #if 0
1300  if (eval.functional_expression()) {
1301  BaseType *var = eval.eval_function(dds, d_dataset);
1302  if (!var)
1303  throw Error(unknown_error, "Error calling the CE function.");
1304 
1305  if (with_mime_headers)
1306  set_mime_multipart(data_stream, boundary, start, dap4_data_ddx,
1307  d_cgi_ver, x_plain, data_lmt);
1308  data_stream << flush ;
1309  BaseTypeFactory btf;
1310  DDS var_dds(&btf, var->name());
1311  var->set_send_p(true);
1312  var_dds.add_var(var);
1313  dataset_constraint_ddx(var_dds, eval, data_stream, boundary, start);
1314 
1315  // functional_constraint_ddx(*var, dds, eval, data_stream, boundary);
1316  delete var;
1317  var = 0;
1318  }
1319 #endif
1320  if (eval.function_clauses()) {
1321  DDS *fdds = eval.eval_function_clauses(dds);
1322  if (with_mime_headers)
1323  set_mime_multipart(data_stream, boundary, start, dap4_data_ddx,
1324  d_cgi_ver, x_plain, data_lmt);
1325  data_stream << flush ;
1326  dataset_constraint(*fdds, eval, data_stream, false);
1327  delete fdds;
1328  }
1329  else {
1330  if (with_mime_headers)
1331  set_mime_multipart(data_stream, boundary, start, dap4_data_ddx,
1332  d_cgi_ver, x_plain, data_lmt);
1333  data_stream << flush ;
1334  dataset_constraint_ddx(dds, eval, data_stream, boundary, start);
1335  }
1336 
1337  data_stream << flush ;
1338 
1339  if (with_mime_headers)
1340  data_stream << CRLF << "--" << boundary << "--" << CRLF;
1341 }
1342 
1343 } // namespace libdap
1344 
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:853
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:800
void set_mime_data_boundary(ostream &strm, const string &boundary, const string &cid, ObjectType type, EncodingType enc)
Definition: mime_util.cc:708
time_t last_modified_time(const string &name)
Definition: mime_util.cc:98
#define not_used
Definition: config.h:850
BaseType * eval_function(DDS &dds, const string &dataset)
Evaluate a function-valued constraint expression.
Vars_iter var_begin()
Return an iterator to the first variable.
Definition: DDS.cc:630
#define DBGN(x)
Definition: debug.h:59
void parse_constraint(const string &constraint, DDS &dds)
Parse the constraint expression given the current DDS.
virtual bool serialize(ConstraintEvaluator &eval, DDS &dds, Marshaller &m, bool ce_eval=true)=0
Move data to the net.
void set_timeout(int t)
Definition: DDS.cc:695
void print_xml(FILE *out, bool constrained, const string &blob="")
Definition: DDS.cc:918
void set_mime_text(FILE *out, ObjectType type, const string &ver, EncodingType enc, const time_t last_modified)
Definition: mime_util.cc:376
virtual void print(FILE *out, bool dereference=false)
Definition: DAS.cc:365
const char * version
Definition: getdap.cc:64
#define unknown_error
Unknown error.
Definition: Error.h:60
#define DBG(x)
Definition: debug.h:58
virtual void set_send_p(bool state)
Definition: BaseType.cc:517
DDS * eval_function_clauses(DDS &dds)
Evaluate a function-valued constraint expression that contains several function calls.
void set_mime_ddx_boundary(ostream &strm, const string &boundary, const string &cid, ObjectType type, EncodingType enc)
Definition: mime_util.cc:695
std::vector< BaseType * >::iterator Vars_iter
Definition: DDS.h:214
#define CRLF
Definition: DODSFilter.cc:84
void set_mime_binary(FILE *out, ObjectType type, const string &ver, EncodingType enc, const time_t last_modified)
Definition: mime_util.cc:583
marshaller that knows how to marshall/serialize dap data objects to a file using XDR ...
string name() const
Returns the name of the class instance.
Definition: BaseType.cc:210
void set_mime_not_modified(FILE *out)
Send a `Not Modified' response.
Definition: mime_util.cc:968
string www2id(const string &in, const string &escape, const string &except)
Definition: escaping.cc:218
Vars_iter var_end()
Return an iterator.
Definition: DDS.cc:642
Evaluate a constraint expression.
void tag_nested_sequences()
Traverse DDS, set Sequence leaf nodes.
Definition: DDS.cc:710
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:190
Hold attribute data for a DAP2 dataset.
Definition: DAS.h:123
void set_mime_multipart(ostream &strm, const string &boundary, const string &start, ObjectType type, const string &version, EncodingType enc, const time_t last_modified)
Definition: mime_util.cc:661
bool function_clauses()
Does the current constraint expression return a DDS pointer?
A class for error processing.
Definition: Error.h:90
EventHandler * register_handler(int signum, EventHandler *eh, bool override=false)
marshaller that knows how to marshal/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:848
FILE * compressor(FILE *output, int &childpid)
void usage(string name)
Definition: getdap.cc:69