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 25112 2011-12-29 21:44:54Z 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  // 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  }
295  }
296 
297  DBGN(cerr << "exiting." << endl);
298 
299  return getopt.optind; // return the index of the next argument
300 }
301 
306 bool
307 DODSFilter::is_conditional() const
308 {
309  return d_conditional_request;
310 }
311 
325 void
326 DODSFilter::set_cgi_version(string version)
327 {
328  d_cgi_ver = version;
329 }
330 
336 string
337 DODSFilter::get_cgi_version() const
338 {
339  return d_cgi_ver;
340 }
341 
348 string
349 DODSFilter::get_ce() const
350 {
351  return d_ce;
352 }
353 
354 void
355 DODSFilter::set_ce(string _ce)
356 {
357  d_ce = www2id(_ce, "%", "%20");
358 }
359 
368 string
369 DODSFilter::get_dataset_name() const
370 {
371  return d_dataset;
372 }
373 
374 void
375 DODSFilter::set_dataset_name(const string ds)
376 {
377  d_dataset = www2id(ds, "%", "%20");
378 }
379 
383 string
384 DODSFilter::get_URL() const
385 {
386  return d_url;
387 }
388 
391 void
392 DODSFilter::set_URL(const string &url)
393 {
394  if (url.find('?') != url.npos)
395  print_usage(); // Throws Error
396 
397  d_url = url;
398 }
399 
407 string
408 DODSFilter::get_dataset_version() const
409 {
410  return "";
411 }
412 
419 void DODSFilter::set_response(const string &r)
420 {
421  if (r == "DAS" || r == "das") {
422  d_response = DAS_Response;
423  d_action = "das" ;
424  }
425  else if (r == "DDS" || r == "dds") {
426  d_response = DDS_Response;
427  d_action = "dds" ;
428  }
429  else if (r == "DataDDS" || r == "dods") {
430  d_response = DataDDS_Response;
431  d_action = "dods" ;
432  }
433  else if (r == "DDX" || r == "ddx") {
434  d_response = DDX_Response;
435  d_action = "ddx" ;
436  }
437  else if (r == "DataDDX" || r == "dataddx") {
438  d_response = DataDDX_Response;
439  d_action = "dataddx" ;
440  }
441  else if (r == "Version") {
442  d_response = Version_Response;
443  d_action = "version" ;
444  }
445  else
446  print_usage(); // Throws Error
447 }
448 
451 DODSFilter::get_response() const
452 {
453  return d_response;
454 }
455 
457 string DODSFilter::get_action() const
458 {
459  return d_action;
460 }
461 
482 time_t
483 DODSFilter::get_dataset_last_modified_time() const
484 {
485  return last_modified_time(d_dataset);
486 }
487 
497 time_t
498 DODSFilter::get_das_last_modified_time(const string &anc_location) const
499 {
500  DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
501  << anc_location << "call faf(das) d_dataset=" << d_dataset
502  << " d_anc_file=" << d_anc_file << endl);
503 
504  string name
505  = Ancillary::find_ancillary_file(d_dataset, "das",
506  (anc_location == "") ? d_anc_dir : anc_location,
507  d_anc_file);
508 
509  return max((name != "") ? last_modified_time(name) : 0,
510  get_dataset_last_modified_time());
511 }
512 
520 time_t
521 DODSFilter::get_dds_last_modified_time(const string &anc_location) const
522 {
523  DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
524  << anc_location << "call faf(dds) d_dataset=" << d_dataset
525  << " d_anc_file=" << d_anc_file << endl);
526 
527  string name
528  = Ancillary::find_ancillary_file(d_dataset, "dds",
529  (anc_location == "") ? d_anc_dir : anc_location,
530  d_anc_file);
531 
532  return max((name != "") ? last_modified_time(name) : 0,
533  get_dataset_last_modified_time());
534 }
535 
549 time_t
550 DODSFilter::get_data_last_modified_time(const string &anc_location) const
551 {
552  DBG(cerr << "DODSFilter::get_das_last_modified_time(anc_location="
553  << anc_location << "call faf(both) d_dataset=" << d_dataset
554  << " d_anc_file=" << d_anc_file << endl);
555 
556  string dds_name
557  = Ancillary::find_ancillary_file(d_dataset, "dds",
558  (anc_location == "") ? d_anc_dir : anc_location,
559  d_anc_file);
560  string das_name
561  = Ancillary::find_ancillary_file(d_dataset, "das",
562  (anc_location == "") ? d_anc_dir : anc_location,
563  d_anc_file);
564 
565  time_t m = max((das_name != "") ? last_modified_time(das_name) : (time_t)0,
566  (dds_name != "") ? last_modified_time(dds_name) : (time_t)0);
567  // Note that this is a call to get_dataset_... not get_data_...
568  time_t n = get_dataset_last_modified_time();
569 
570  return max(m, n);
571 }
572 
580 time_t
581 DODSFilter::get_request_if_modified_since() const
582 {
583  return d_if_modified_since;
584 }
585 
592 string
593 DODSFilter::get_cache_dir() const
594 {
595  return d_cache_dir;
596 }
597 
602 void
603 DODSFilter::set_timeout(int t)
604 {
605  d_timeout = t;
606 }
607 
609 int
610 DODSFilter::get_timeout() const
611 {
612  return d_timeout;
613 }
614 
615 #if FILE_METHODS
616 
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 #endif
640 
641 // FIXME
642 void
643 DODSFilter::establish_timeout(ostream &stream) const
644 {
645 #ifndef WIN32
646  if (d_timeout > 0) {
647  SignalHandler *sh = SignalHandler::instance();
648  EventHandler *old_eh = sh->register_handler(SIGALRM, new AlarmHandler(stream));
649  delete old_eh;
650  alarm(d_timeout);
651  }
652 #endif
653 }
654 
655 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.";
656 
666 void
667 DODSFilter::print_usage() const
668 {
669  // Write a message to the WWW server error log file.
670  ErrMsgT(usage.c_str());
671 
672  throw Error(emessage);
673 }
674 
680 void
681 DODSFilter::send_version_info() const
682 {
683  do_version(d_cgi_ver, get_dataset_version());
684 }
685 
686 #if FILE_METHODS
687 
698 void
699 DODSFilter::send_das(FILE *out, DAS &das, const string &anc_location,
700  bool with_mime_headers) const
701 {
702  time_t das_lmt = get_das_last_modified_time(anc_location);
703  if (is_conditional()
704  && das_lmt <= get_request_if_modified_since()
705  && with_mime_headers) {
707  }
708  else {
709  if (with_mime_headers)
710  set_mime_text(out, dods_das, d_cgi_ver, x_plain, das_lmt);
711  das.print(out);
712  }
713  fflush(out) ;
714 }
715 #endif
716 
728 void
729 DODSFilter::send_das(ostream &out, DAS &das, const string &anc_location,
730  bool with_mime_headers) const
731 {
732  time_t das_lmt = get_das_last_modified_time(anc_location);
733  if (is_conditional()
734  && das_lmt <= get_request_if_modified_since()
735  && with_mime_headers) {
737  }
738  else {
739  if (with_mime_headers)
740  set_mime_text(out, dods_das, d_cgi_ver, x_plain, das_lmt);
741  das.print(out);
742  }
743  out << flush ;
744 }
745 
746 void
747 DODSFilter::send_das(DAS &das, const string &anc_location,
748  bool with_mime_headers) const
749 {
750  send_das(cout, das, anc_location, with_mime_headers);
751 }
752 
753 #if FILE_METHODS
754 
770 void
771 DODSFilter::send_dds(FILE *out, DDS &dds, ConstraintEvaluator &eval,
772  bool constrained,
773  const string &anc_location,
774  bool with_mime_headers) const
775 {
776  // If constrained, parse the constraint. Throws Error or InternalErr.
777  if (constrained)
778  eval.parse_constraint(d_ce, dds);
779 
780  if (eval.functional_expression())
781  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.");
782 
783  time_t dds_lmt = get_dds_last_modified_time(anc_location);
784  if (is_conditional()
785  && dds_lmt <= get_request_if_modified_since()
786  && with_mime_headers) {
788  }
789  else {
790  if (with_mime_headers)
791  set_mime_text(out, dods_dds, d_cgi_ver, x_plain, dds_lmt);
792  if (constrained)
793  dds.print_constrained(out);
794  else
795  dds.print(out);
796  }
797 
798  fflush(out) ;
799 }
800 #endif
801 
818 void
819 DODSFilter::send_dds(ostream &out, DDS &dds, ConstraintEvaluator &eval,
820  bool constrained,
821  const string &anc_location,
822  bool with_mime_headers) const
823 {
824  // If constrained, parse the constraint. Throws Error or InternalErr.
825  if (constrained)
826  eval.parse_constraint(d_ce, dds);
827 
828  if (eval.functional_expression())
829  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.");
830 
831  time_t dds_lmt = get_dds_last_modified_time(anc_location);
832  if (is_conditional()
833  && dds_lmt <= get_request_if_modified_since()
834  && with_mime_headers) {
836  }
837  else {
838  if (with_mime_headers)
839  set_mime_text(out, dods_dds, d_cgi_ver, x_plain, dds_lmt);
840  if (constrained)
841  dds.print_constrained(out);
842  else
843  dds.print(out);
844  }
845 
846  out << flush ;
847 }
848 
849 void
850 DODSFilter::send_dds(DDS &dds, ConstraintEvaluator &eval,
851  bool constrained, const string &anc_location,
852  bool with_mime_headers) const
853 {
854  send_dds(cout, dds, eval, constrained, anc_location, with_mime_headers);
855 }
856 
857 #if FILE_METHODS
858 // 'lmt' unused. Should it be used to supply a LMT or removed from the
859 // method? jhrg 8/9/05
860 void
861 DODSFilter::functional_constraint(BaseType &var, DDS &dds,
862  ConstraintEvaluator &eval, FILE *out) const
863 {
864  fprintf(out, "Dataset {\n");
865  var.print_decl(out, " ", true, false, true);
866  fprintf(out, "} function_value;\n");
867  fprintf(out, "Data:\n");
868 
869  fflush(out);
870 
871  XDRFileMarshaller m( out ) ;
872 
873  try {
874  // In the following call to serialize, suppress CE evaluation.
875  var.serialize(eval, dds, m, false);
876  }
877  catch (Error &e) {
878  throw;
879  }
880 }
881 #endif
882 
883 // 'lmt' unused. Should it be used to supply a LMT or removed from the
884 // method? jhrg 8/9/05
885 void
886 DODSFilter::functional_constraint(BaseType &var, DDS &dds,
887  ConstraintEvaluator &eval, ostream &out) const
888 {
889  out << "Dataset {\n" ;
890  var.print_decl(out, " ", true, false, true);
891  out << "} function_value;\n" ;
892  out << "Data:\n" ;
893 
894  out << flush ;
895 
896  // Grab a stream that encodes using XDR.
897  XDRStreamMarshaller m( out ) ;
898 
899  try {
900  // In the following call to serialize, suppress CE evaluation.
901  var.serialize(eval, dds, m, false);
902  }
903  catch (Error &e) {
904  throw;
905  }
906 }
907 
908 #if FILE_METHODS
909 void
910 DODSFilter::dataset_constraint(DDS & dds, ConstraintEvaluator & eval,
911  FILE * out, bool ce_eval) const
912 {
913  // send constrained DDS
914  dds.print_constrained(out);
915  fprintf(out, "Data:\n");
916  fflush(out);
917 
918  // Grab a stream that encodes using XDR.
919  XDRFileMarshaller m( out ) ;
920 
921  try {
922  // Send all variables in the current projection (send_p())
923  for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++)
924  if ((*i)->send_p()) {
925  DBG(cerr << "Sending " << (*i)->name() << endl);
926  (*i)->serialize(eval, dds, m, ce_eval);
927  }
928  }
929  catch (Error & e) {
930  throw;
931  }
932 }
933 #endif
934 
935 void
936 DODSFilter::dataset_constraint(DDS & dds, ConstraintEvaluator & eval,
937  ostream &out, bool ce_eval) const
938 {
939  // send constrained DDS
940  dds.print_constrained(out);
941  out << "Data:\n" ;
942  out << flush ;
943 
944  // Grab a stream that encodes using XDR.
945  XDRStreamMarshaller m( out ) ;
946 
947  try {
948  // Send all variables in the current projection (send_p())
949  for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++)
950  if ((*i)->send_p()) {
951  DBG(cerr << "Sending " << (*i)->name() << endl);
952  (*i)->serialize(eval, dds, m, ce_eval);
953  }
954  }
955  catch (Error & e) {
956  throw;
957  }
958 }
959 
960 void
961 DODSFilter::dataset_constraint_ddx(DDS & dds, ConstraintEvaluator & eval,
962  ostream &out, const string &boundary,
963  const string &start, bool ce_eval) const
964 {
965  // Write the MPM headers for the DDX (text/xml) part of the response
966  set_mime_ddx_boundary(out, boundary, start, dap4_ddx);
967 
968  // Make cid
969  uuid_t uu;
970  uuid_generate(uu);
971  char uuid[37];
972  uuid_unparse(uu, &uuid[0]);
973  char domain[256];
974  if (getdomainname(domain, 255) != 0 || strlen(domain) == 0)
975  strncpy(domain, "opendap.org", 255);
976 
977  string cid = string(&uuid[0]) + "@" + string(&domain[0]);
978 
979  // Send constrained DDX with a data blob reference
980  dds.print_xml(out, true, cid);
981 
982  // Write the MPM headers for the data part of the response.
983  set_mime_data_boundary(out, boundary, cid, dap4_data, binary);
984 
985  // Grab a stream that encodes using XDR.
986  XDRStreamMarshaller m( out ) ;
987 
988  try {
989  // Send all variables in the current projection (send_p())
990  for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++)
991  if ((*i)->send_p()) {
992  DBG(cerr << "Sending " << (*i)->name() << endl);
993  (*i)->serialize(eval, dds, m, ce_eval);
994  }
995  }
996  catch (Error & e) {
997  throw;
998  }
999 }
1000 
1001 #if FILE_METHODS
1002 
1018 void
1019 DODSFilter::send_data(DDS & dds, ConstraintEvaluator & eval,
1020  FILE * data_stream, const string & anc_location,
1021  bool with_mime_headers) const
1022 {
1023  // If this is a conditional request and the server should send a 304
1024  // response, do that and exit. Otherwise, continue on and send the full
1025  // response.
1026  time_t data_lmt = get_data_last_modified_time(anc_location);
1027  if (is_conditional()
1028  && data_lmt <= get_request_if_modified_since()
1029  && with_mime_headers) {
1030  set_mime_not_modified(data_stream);
1031  return;
1032  }
1033  // Set up the alarm.
1034  establish_timeout(data_stream);
1035  dds.set_timeout(d_timeout);
1036 
1037  eval.parse_constraint(d_ce, dds); // Throws Error if the ce doesn't
1038  // parse.
1039 
1040  dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
1041 
1042  // Start sending the response...
1043 
1044  // Handle *functional* constraint expressions specially
1045 #if 0
1046  if (eval.functional_expression()) {
1047  // Get the result and then start sending the headers. This provides a
1048  // way to send errors back to the client w/o colliding with the
1049  // normal response headers. There's some duplication of code with this
1050  // and the else-clause.
1051  BaseType *var = eval.eval_function(dds, d_dataset);
1052  if (!var)
1053  throw Error(unknown_error, "Error calling the CE function.");
1054 
1055 #if COMPRESSION_FOR_SERVER3
1056  if (with_mime_headers)
1057  set_mime_binary(data_stream, dods_data, d_cgi_ver,
1058  (compress) ? deflate : x_plain, data_lmt);
1059  fflush(data_stream);
1060 
1061  int childpid;
1062  if (compress)
1063  data_stream = compressor(data_stream, childpid);
1064 #endif
1065  if (with_mime_headers)
1066  set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
1067 
1068  fflush(data_stream);
1069 
1070  functional_constraint(*var, dds, eval, data_stream);
1071  delete var;
1072  var = 0;
1073  }
1074 #endif
1075  if (eval.function_clauses()) {
1076  DDS *fdds = eval.eval_function_clauses(dds);
1077 
1078  if (with_mime_headers)
1079  set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
1080 
1081  dataset_constraint(*fdds, eval, data_stream, false);
1082  delete fdds;
1083  }
1084  else {
1085  if (with_mime_headers)
1086  set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
1087 
1088  dataset_constraint(dds, eval, data_stream);
1089  }
1090 
1091  fflush(data_stream);
1092 }
1093 #endif
1094 
1111 void
1112 DODSFilter::send_data(DDS & dds, ConstraintEvaluator & eval,
1113  ostream & data_stream, const string & anc_location,
1114  bool with_mime_headers) const
1115 {
1116  // If this is a conditional request and the server should send a 304
1117  // response, do that and exit. Otherwise, continue on and send the full
1118  // response.
1119  time_t data_lmt = get_data_last_modified_time(anc_location);
1120  if (is_conditional()
1121  && data_lmt <= get_request_if_modified_since()
1122  && with_mime_headers) {
1123  set_mime_not_modified(data_stream);
1124  return;
1125  }
1126  // Set up the alarm.
1127  establish_timeout(data_stream);
1128  dds.set_timeout(d_timeout);
1129 
1130  eval.parse_constraint(d_ce, dds); // Throws Error if the ce doesn't
1131  // parse.
1132 
1133  dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
1134 
1135  // Start sending the response...
1136 
1137  // Handle *functional* constraint expressions specially
1138 #if 0
1139  if (eval.functional_expression()) {
1140  // Get the result and then start sending the headers. This provides a
1141  // way to send errors back to the client w/o colliding with the
1142  // normal response headers. There's some duplication of code with this
1143  // and the else-clause.
1144  BaseType *var = eval.eval_function(dds, d_dataset);
1145  if (!var)
1146  throw Error(unknown_error, "Error calling the CE function.");
1147 
1148  if (with_mime_headers)
1149  set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
1150 
1151  data_stream << flush ;
1152 
1153  functional_constraint(*var, dds, eval, data_stream);
1154  delete var;
1155  var = 0;
1156  }
1157 #endif
1158  if (eval.function_clauses()) {
1159  DDS *fdds = eval.eval_function_clauses(dds);
1160  if (with_mime_headers)
1161  set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
1162 
1163  dataset_constraint(*fdds, eval, data_stream, false);
1164  delete fdds;
1165  }
1166  else {
1167  if (with_mime_headers)
1168  set_mime_binary(data_stream, dods_data, d_cgi_ver, x_plain, data_lmt);
1169 
1170  dataset_constraint(dds, eval, data_stream);
1171  }
1172 
1173  data_stream << flush ;
1174 }
1175 
1176 #if FILE_METHODS
1177 
1187 void
1188 DODSFilter::send_ddx(DDS &dds, ConstraintEvaluator &eval, FILE *out,
1189  bool with_mime_headers) const
1190 {
1191  // If constrained, parse the constraint. Throws Error or InternalErr.
1192  if (!d_ce.empty())
1193  eval.parse_constraint(d_ce, dds);
1194 
1195  if (eval.functional_expression())
1196  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.");
1197 
1198  time_t dds_lmt = get_dds_last_modified_time(d_anc_dir);
1199 
1200  // If this is a conditional request and the server should send a 304
1201  // response, do that and exit. Otherwise, continue on and send the full
1202  // response.
1203  if (is_conditional() && dds_lmt <= get_request_if_modified_since()
1204  && with_mime_headers) {
1205  set_mime_not_modified(out);
1206  return;
1207  }
1208  else {
1209  if (with_mime_headers)
1210  set_mime_text(out, dap4_ddx, d_cgi_ver, x_plain, dds_lmt);
1211  dds.print_xml(out, !d_ce.empty(), "");
1212  }
1213 }
1214 #endif
1215 
1226 void
1227 DODSFilter::send_ddx(DDS &dds, ConstraintEvaluator &eval, ostream &out,
1228  bool with_mime_headers) const
1229 {
1230  // If constrained, parse the constraint. Throws Error or InternalErr.
1231  if (!d_ce.empty())
1232  eval.parse_constraint(d_ce, dds);
1233 
1234  if (eval.functional_expression())
1235  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.");
1236 
1237  time_t dds_lmt = get_dds_last_modified_time(d_anc_dir);
1238 
1239  // If this is a conditional request and the server should send a 304
1240  // response, do that and exit. Otherwise, continue on and send the full
1241  // response.
1242  if (is_conditional() && dds_lmt <= get_request_if_modified_since()
1243  && with_mime_headers) {
1244  set_mime_not_modified(out);
1245  return;
1246  }
1247  else {
1248  if (with_mime_headers)
1249  set_mime_text(out, dap4_ddx, d_cgi_ver, x_plain, dds_lmt);
1250  dds.print_xml(out, !d_ce.empty(), "");
1251  }
1252 }
1253 
1274 void
1275 DODSFilter::send_data_ddx(DDS & dds, ConstraintEvaluator & eval,
1276  ostream & data_stream, const string &start,
1277  const string &boundary, const string & anc_location,
1278  bool with_mime_headers) const
1279 {
1280  // If this is a conditional request and the server should send a 304
1281  // response, do that and exit. Otherwise, continue on and send the full
1282  // response.
1283  time_t data_lmt = get_data_last_modified_time(anc_location);
1284  if (is_conditional()
1285  && data_lmt <= get_request_if_modified_since()
1286  && with_mime_headers) {
1287  set_mime_not_modified(data_stream);
1288  return;
1289  }
1290  // Set up the alarm.
1291  establish_timeout(data_stream);
1292  dds.set_timeout(d_timeout);
1293 
1294  eval.parse_constraint(d_ce, dds); // Throws Error if the ce doesn't
1295  // parse.
1296 
1297  dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
1298 
1299  // Start sending the response...
1300 
1301  // Handle *functional* constraint expressions specially
1302 #if 0
1303  if (eval.functional_expression()) {
1304  BaseType *var = eval.eval_function(dds, d_dataset);
1305  if (!var)
1306  throw Error(unknown_error, "Error calling the CE function.");
1307 
1308  if (with_mime_headers)
1309  set_mime_multipart(data_stream, boundary, start, dap4_data_ddx,
1310  d_cgi_ver, x_plain, data_lmt);
1311  data_stream << flush ;
1312  BaseTypeFactory btf;
1313  DDS var_dds(&btf, var->name());
1314  var->set_send_p(true);
1315  var_dds.add_var(var);
1316  dataset_constraint_ddx(var_dds, eval, data_stream, boundary, start);
1317 
1318  // functional_constraint_ddx(*var, dds, eval, data_stream, boundary);
1319  delete var;
1320  var = 0;
1321  }
1322 #endif
1323  if (eval.function_clauses()) {
1324  DDS *fdds = eval.eval_function_clauses(dds);
1325  if (with_mime_headers)
1326  set_mime_multipart(data_stream, boundary, start, dap4_data_ddx,
1327  d_cgi_ver, x_plain, data_lmt);
1328  data_stream << flush ;
1329  dataset_constraint(*fdds, eval, data_stream, false);
1330  delete fdds;
1331  }
1332  else {
1333  if (with_mime_headers)
1334  set_mime_multipart(data_stream, boundary, start, dap4_data_ddx,
1335  d_cgi_ver, x_plain, data_lmt);
1336  data_stream << flush ;
1337  dataset_constraint_ddx(dds, eval, data_stream, boundary, start);
1338  }
1339 
1340  data_stream << flush ;
1341 
1342  if (with_mime_headers)
1343  data_stream << CRLF << "--" << boundary << "--" << CRLF;
1344 }
1345 
1346 } // namespace libdap
1347