libdap Updated for version 3.21.1
libdap4 is an implementation of OPeNDAP's DAP protocol.
D4ResponseBuilder.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "DDXParserSAX2.h"
51#include "D4Group.h"
52
53#include "D4ResponseBuilder.h"
54#include "XDRStreamMarshaller.h"
55#include "XDRFileUnMarshaller.h"
56
57#include "DAPCache3.h"
58
59#include "debug.h"
60#include "mime_util.h" // for last_modified_time() and rfc_822_date()
61#include "escaping.h"
62#include "util.h"
63
64#ifdef DAP4
65#include "D4StreamMarshaller.h"
66#endif
67
68#ifndef WIN32
69#include "SignalHandler.h"
70#include "EventHandler.h"
71#include "AlarmHandler.h"
72#endif
73
74#define CRLF "\r\n" // Change here, expr-test.cc
75#define FUNCTION_CACHE "/tmp/dap_functions_cache/"
76#define FUNCTION_CACHE_PREFIX "f"
77// Cache size in megabytes; 20,000M -> 20GB
78#define FUNCTION_CACHE_SIZE 20000
79
80using namespace std;
81
82namespace libdap {
83
87
91{
92 // Set default values. Don't use the C++ constructor initialization so
93 // that a subclass can have more control over this process.
94 d_dataset = "";
95 d_dap4ce = "";
96 d_dap4_btp_func_expr = "";
97 d_timeout = 0;
98
100
101 // Cache size is given in megabytes and later converted to bytes
102 // for internal use.
103 d_cache = 0;
104
105 // Without this, the directory becomes a low-budget config param since
106 // the cache will only be used if the directory exists.
107 // TODO fix this mess by adding a real config param in bes.conf
108#if 0
109 if (!dir_writable(FUNCTION_CACHE))
110 mkdir(FUNCTION_CACHE, 0777);
111#endif
112
114 DBG(cerr << "the FUNCTION_CACHE directory (" << FUNCTION_CACHE <<") exists" << endl);
116 }
117 else {
118 DBG(cerr << "the FUNCTION_CACHE directory (" << FUNCTION_CACHE <<") does not exist - not caching" << endl);
119 }
120
121#ifdef WIN32
122 // We want serving from win32 to behave in a manner
123 // similar to the UNIX way - no CR->NL terminated lines
124 // in files. Hence stdout goes to binary mode.
125 _setmode(_fileno(stdout), _O_BINARY);
126#endif
127}
128
129# if 0
136string D4ResponseBuilder::get_ce() const
137{
138 return d_dap4ce;
139}
140
141void D4ResponseBuilder::set_ce(string _ce)
142{
143 d_dap4ce = www2id(_ce, "%", "%20");
144}
145
154string D4ResponseBuilder::get_dataset_name() const
155{
156 return d_dataset;
157}
158
159void D4ResponseBuilder::set_dataset_name(const string ds)
160{
161 d_dataset = www2id(ds, "%", "%20");
162}
163#endif
164
165#if 0
171{
172 d_timeout = t;
173}
174
176int D4ResponseBuilder::get_timeout() const
177{
178 return d_timeout;
179}
180#endif
187void
189{
190#ifndef WIN32
191 if (d_timeout > 0) {
193 EventHandler *old_eh = sh->register_handler(SIGALRM, new AlarmHandler(stream));
194 delete old_eh;
195
196 alarm(d_timeout);
197 }
198#endif
199}
200
202void
204{
205#ifndef WIN32
206 alarm(0);
207#endif
208}
209
217void
219{
220 string ce;
221 if (!expr.empty())
222 ce = expr;
223 else
224 ce = d_dap4ce;
225
226 string btp_function_ce = "";
227 string::size_type pos = 0;
228 DBG(cerr << "ce: " << ce << endl);
229
230 string::size_type first_paren = ce.find("(", pos);
231 string::size_type closing_paren = ce.find(")", pos);
232 while (first_paren != string::npos && closing_paren != string::npos) {
233 // Maybe a BTP function; get the name of the potential function
234 string name = ce.substr(pos, first_paren-pos);
235 DBG(cerr << "name: " << name << endl);
236 // is this a BTP function
237 btp_func f;
238 if (eval.find_function(name, &f)) {
239 // Found a BTP function
240 if (!btp_function_ce.empty())
241 btp_function_ce += ",";
242 btp_function_ce += ce.substr(pos, closing_paren+1-pos);
243 ce.erase(pos, closing_paren+1-pos);
244 if (ce[pos] == ',')
245 ce.erase(pos, 1);
246 }
247 else {
248 pos = closing_paren + 1;
249 // exception?
250 if (pos < ce.length() && ce.at(pos) == ',')
251 ++pos;
252 }
253
254 first_paren = ce.find("(", pos);
255 closing_paren = ce.find(")", pos);
256 }
257
258 DBG(cerr << "Modified constraint: " << ce << endl);
259 DBG(cerr << "BTP Function part: " << btp_function_ce << endl);
260
261 d_dap4ce = ce;
262 d_dap4_btp_func_expr = btp_function_ce;
263}
264
272static string
273build_cache_file_name(const string &dataset, const string &ce)
274{
275 DBG(cerr << "build_cache_file_name: dataset: " << dataset << ", ce: " << ce << endl);
276
277 string name = dataset + "#" + ce;
278 string::size_type pos = name.find_first_of("/(),\"\'");
279 while (pos != string::npos) {
280 name.replace(pos, 1, "#", 1);
281 pos = name.find_first_of("/()\"\'");
282 }
283
284 DBG(cerr << "build_cache_file_name: name: " << name << endl);
285
286 return name;
287}
288
289#if 0
290static bool cached_data_ddx_exists(const string &cache_file_name)
291{
292 ifstream icache_file(cache_file_name.c_str()); // closes on return
293
294 return !icache_file.fail() && !icache_file.bad() && !icache_file.eof();
295}
296#endif
306bool D4ResponseBuilder::is_valid(const string &cache_file_name)
307{
308 // If the cached response is zero bytes in size, it's not valid.
309 // (hmmm...)
310
311 off_t entry_size = 0;
312 time_t entry_time = 0;
313 struct stat buf;
314 if (stat(cache_file_name.c_str(), &buf) == 0) {
315 entry_size = buf.st_size;
316 entry_time = buf.st_mtime;
317 }
318 else {
319 return false;
320 }
321
322 if (entry_size == 0)
323 return false;
324
325 time_t dataset_time = entry_time;
326 if (stat(d_dataset.c_str(), &buf) == 0) {
327 dataset_time = buf.st_mtime;
328 }
329
330 // Trick: if the d_dataset is not a file, stat() returns error and
331 // the times stay equal and the code uses the cache entry.
332
333 // TODO Fix this so that the code can get a LMT from the correct
334 // handler.
335 if (dataset_time > entry_time)
336 return false;
337
338 return true;
339}
340
355 string &cache_token)
356{
357 DBG(cerr << "Found function(s) in CE: " << d_dap4_btp_func_expr << endl);
358
359 // These are used for the cached or newly created DDS object
360 BaseTypeFactory factory;
361 DDS *fdds;
362
363 // Get the cache filename for this thing. Do not use the default
364 // name mangling; instead use what build_cache_file_name() does.
365 string cache_file_name = d_cache->get_cache_file_name(build_cache_file_name(d_dataset, d_dap4_btp_func_expr), false);
366 int fd;
367 try {
368 // If the object in the cache is not valid, remove it. The read_lock will
369 // then fail and the code will drop down to the create_and_lock() call.
370 // is_valid() tests for a non-zero object and for d_dateset newer than
371 // the cached object.
372 if (!is_valid(cache_file_name))
373 d_cache->purge_file(cache_file_name);
374
375 if (d_cache->get_read_lock(cache_file_name, fd)) {
376 DBG(cerr << "function ce - cached hit: " << cache_file_name << endl );
377 fdds = get_cached_data_ddx(cache_file_name, &factory);
378 }
379
380 // If here, the cache_file_name could not be locked for read access;
381 // try to build it. First make an empty file and get an exclusive lock on it.
382 // TODO Make this an 'else if'?
383 if (d_cache->create_and_lock(cache_file_name, fd)) {
384 DBG(cerr << "function ce - caching " << cache_file_name << endl );
385
386 eval.parse_constraint(d_dap4_btp_func_expr, dds);
387 fdds = eval.eval_function_clauses(dds);
388
389 // TODO cache it using fd. Since this is advisory locking, this will work...
390 // Improve?
391 cache_data_ddx(cache_file_name, *fdds);
392
393 // Change the exclusive lock on the new file to a shared lock. This keeps
394 // other processes from purging the new file and ensures that the reading
395 // process can use it.
396 d_cache->exclusive_to_shared_lock(fd);
397
398 // Now update the total cache size info and purge if needed. The new file's
399 // name is passed into the purge method because this process cannot detect its
400 // own lock on the file.
401 unsigned long long size = d_cache->update_cache_info(cache_file_name);
402 if (d_cache->cache_too_big(size))
403 d_cache->update_and_purge(cache_file_name);
404 }
405 // get_read_lock() returns immediately if the file does not exist,
406 // but blocks waiting to get a shared lock if the file does exist.
407 else if (d_cache->get_read_lock(cache_file_name, fd)) {
408 DBG(cerr << "function ce - cached hit: " << cache_file_name << endl );
409 fdds = get_cached_data_ddx(cache_file_name, &factory);
410 }
411 else {
412 throw InternalErr(__FILE__, __LINE__, "Cache error during function invocation.");
413 }
414 }
415 catch (...) {
416 DBG(cerr << "caught exception, unlocking cache and re-throw." << endl );
417 // I think this call is not needed. jhrg 10/23/12
418 d_cache->unlock_cache();
419 throw;
420 }
421
422 cache_token = cache_file_name; // Set this value-result parameter
423 return fdds;
424}
425
426#if 0
439void D4ResponseBuilder::send_das(ostream &out, DAS &das, bool with_mime_headers) const
440{
441 if (with_mime_headers)
443
444 das.print(out);
445
446 out << flush;
447}
448
466void D4ResponseBuilder::send_das(ostream &out, DDS &dds, ConstraintEvaluator &eval, bool constrained, bool with_mime_headers)
467{
468 // Set up the alarm.
470 dds.set_timeout(d_timeout);
471
472 if (!constrained) {
473 if (with_mime_headers)
475
476 dds.print_das(out);
477 out << flush;
478
479 return;
480 }
481
482 split_ce(eval);
483
484 // If there are functions, parse them and eval.
485 // Use that DDS and parse the non-function ce
486 // Serialize using the second ce and the second dds
487 if (!d_dap4_btp_func_expr.empty()) {
488#if 0
489 DBG(cerr << "Found function(s) in CE: " << d_dap4_btp_func_expr << endl);
490
491 // These are used for the cached or newly created DDS object
492 BaseTypeFactory factory;
493 DDS *fdds;
494
495 // Get the cache filename for this thing. Do not use the default
496 // name mangling; instead use what build_cache_file_name() does.
497 string cache_file_name = d_cache->get_cache_file_name(build_cache_file_name(d_dataset, d_dap4_btp_func_expr), false);
498 int fd;
499 try {
500 // If the object in the cache is not valid, remove it. The read_lock will
501 // then fail and the code will drop down to the create_and_lock() call.
502 // is_valid() tests for a non-zero object and for d_dateset newer than
503 // the cached object.
504 if (!is_valid(cache_file_name))
505 d_cache->purge_file(cache_file_name);
506
507 if (d_cache->get_read_lock(cache_file_name, fd)) {
508 DBG(cerr << "function ce - cached hit: " << cache_file_name << endl );
509 fdds = get_cached_dap2_data_ddx(cache_file_name, &factory);
510 }
511
512 // If here, the cache_file_name could not be locked for read access;
513 // try to build it. First make an empty file and get an exclusive lock on it.
514 // TODO Make this an 'else if'?
515 if (d_cache->create_and_lock(cache_file_name, fd)) {
516 DBG(cerr << "function ce - caching " << cache_file_name << endl );
517
518 eval.parse_constraint(d_dap4_btp_func_expr, dds);
519 fdds = eval.eval_function_clauses(dds);
520
521 // TODO cache it using fd. Since this is advisory locking, this will work...
522 // Improve?
523 cache_data_ddx(cache_file_name, *fdds);
524
525 // Change the exclusive lock on the new file to a shared lock. This keeps
526 // other processes from purging the new file and ensures that the reading
527 // process can use it.
528 d_cache->exclusive_to_shared_lock(fd);
529
530 // Now update the total cache size info and purge if needed. The new file's
531 // name is passed into the purge method because this process cannot detect its
532 // own lock on the file.
533 unsigned long long size = d_cache->update_cache_info(cache_file_name);
534 if (d_cache->cache_too_big(size))
535 d_cache->update_and_purge(cache_file_name);
536 }
537 else if (d_cache->get_read_lock(cache_file_name, fd)) {
538 DBG(cerr << "function ce - cached hit: " << cache_file_name << endl );
539 fdds = get_cached_dap2_data_ddx(cache_file_name, &factory);
540 }
541 else {
542 throw InternalErr(__FILE__, __LINE__, "Cache error during function invocation.");
543 }
544 }
545 catch (...) {
546 DBG(cerr << "caught exception, unlocking cache and re-throw." << endl );
547 // I think this call is not needed. jhrg 10/23/12
548 d_cache->unlock_cache();
549 throw;
550 }
551#endif
552 DDS *fdds = 0;
553 string cache_token = "";
554
555 if (d_cache) {
556 DBG(cerr << "Using the cache for the server function CE" << endl);
557 fdds = read_cached_dataset(dds, eval, cache_token);
558 }
559 else {
560 DBG(cerr << "Cache not found; (re)calculating" << endl);
561 eval.parse_constraint(d_dap4_btp_func_expr, dds);
562 fdds = eval.eval_function_clauses(dds);
563 }
564
565 if (with_mime_headers)
566 set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
567
568 fdds->print_das(out);
569
570 if (d_cache)
571 d_cache->unlock_and_close(cache_token);
572
573 delete fdds;
574 }
575 else {
576 DBG(cerr << "Simple constraint" << endl);
577
578 eval.parse_constraint(d_dap4ce, dds); // Throws Error if the ce doesn't parse.
579
580 if (with_mime_headers)
581 set_mime_text(out, dods_das, x_plain, last_modified_time(d_dataset), dds.get_dap_version());
582
583 dds.print_das(out);
584 }
585
586 out << flush;
587}
588#endif
606void D4ResponseBuilder::send_dds(ostream &out, DDS &dds, ConstraintEvaluator &eval, bool constrained,
607 bool with_mime_headers)
608{
609 if (!constrained) {
610 if (with_mime_headers)
612
613 dds.print(out);
614 out << flush;
615 return;
616 }
617
618 // Set up the alarm.
621
622 // Split constraint into two halves
623 split_ce(eval);
624
625 // If there are functions, parse them and eval.
626 // Use that DDS and parse the non-function ce
627 // Serialize using the second ce and the second dds
628 if (!d_dap4_btp_func_expr.empty()) {
629#if 0
630 DBG(cerr << "Found function(s) in CE: " << d_dap4_btp_func_expr << endl);
631
632 // These are used for the cached or newly created DDS object
633 BaseTypeFactory factory;
634 DDS *fdds;
635
636 // Get the cache filename for this thing. Do not use the default
637 // name mangling; instead use what build_cache_file_name() does.
638 string cache_file_name = d_cache->get_cache_file_name(build_cache_file_name(d_dataset, d_dap4_btp_func_expr), false);
639 int fd;
640 try {
641 // If the object in the cache is not valid, remove it. The read_lock will
642 // then fail and the code will drop down to the create_and_lock() call.
643 // is_valid() tests for a non-zero object and for d_dateset newer than
644 // the cached object.
645 if (!is_valid(cache_file_name))
646 d_cache->purge_file(cache_file_name);
647
648 if (d_cache->get_read_lock(cache_file_name, fd)) {
649 DBG(cerr << "function ce - cached hit: " << cache_file_name << endl );
650 fdds = get_cached_dap2_data_ddx(cache_file_name, &factory);
651 }
652
653 // If here, the cache_file_name could not be locked for read access;
654 // try to build it. First make an empty file and get an exclusive lock on it.
655 if (d_cache->create_and_lock(cache_file_name, fd)) {
656 DBG(cerr << "function ce - caching " << cache_file_name << endl );
657
658 eval.parse_constraint(d_dap4_btp_func_expr, dds);
659 fdds = eval.eval_function_clauses(dds);
660
661 // TODO cache it using fd. Since this is advisory locking, this will work...
662 // Improve?
663 cache_data_ddx(cache_file_name, *fdds);
664
665 // Change the exclusive lock on the new file to a shared lock. This keeps
666 // other processes from purging the new file and ensures that the reading
667 // process can use it.
668 d_cache->exclusive_to_shared_lock(fd);
669
670 // Now update the total cache size info and purge if needed. The new file's
671 // name is passed into the purge method because this process cannot detect its
672 // own lock on the file.
673 unsigned long long size = d_cache->update_cache_info(cache_file_name);
674 if (d_cache->cache_too_big(size))
675 d_cache->update_and_purge(cache_file_name);
676 }
677 else if (d_cache->get_read_lock(cache_file_name, fd)) {
678 DBG(cerr << "function ce - cached hit: " << cache_file_name << endl );
679 fdds = get_cached_dap2_data_ddx(cache_file_name, &factory);
680 }
681 else {
682 throw InternalErr(__FILE__, __LINE__, "Cache error during function invocation.");
683 }
684 }
685 catch (...) {
686 DBG(cerr << "caught exception, unlocking cache and re-throw." << endl );
687 // I think this call is not needed. jhrg 10/23/12
688 d_cache->unlock_cache();
689 throw;
690 }
691#endif
692 string cache_token = "";
693 DDS *fdds = 0;
694
695 if (d_cache) {
696 DBG(cerr << "Using the cache for the server function CE" << endl);
697 fdds = read_cached_dataset(dds, eval, cache_token);
698 }
699 else {
700 DBG(cerr << "Cache not found; (re)calculating" << endl);
701 eval.parse_constraint(d_dap4_btp_func_expr, dds);
702 fdds = eval.eval_function_clauses(dds);
703 }
704
705 // Server functions might mark variables to use their read()
706 // methods. Clear that so the CE in d_dap4ce will control what is
707 // sent. If that is empty (there was only a function call) all
708 // of the variables in the intermediate DDS (i.e., the function
709 // result) will be sent.
710 fdds->mark_all(false);
711
712 eval.parse_constraint(d_dap4ce, *fdds);
713
714 if (with_mime_headers)
716
717 fdds->print_constrained(out);
718
719 if (d_cache)
720 d_cache->unlock_and_close(cache_token);
721
722 delete fdds;
723 }
724 else {
725 DBG(cerr << "Simple constraint" << endl);
726
727 eval.parse_constraint(d_dap4ce, dds); // Throws Error if the ce doesn't parse.
728
729 if (with_mime_headers)
731
732 dds.print_constrained(out);
733 }
734
735 out << flush;
736}
737
741void D4ResponseBuilder::dataset_constraint(ostream &out, DDS & dds, ConstraintEvaluator & eval, bool ce_eval)
742{
743 // send constrained DDS
744 DBG(cerr << "Inside dataset_constraint" << endl);
745
746 dds.print_constrained(out);
747 out << "Data:\n";
748 out << flush;
749
750#ifdef CHECKSUMS
751 // Grab a stream that encodes using XDR.
752 D4StreamMarshaller m(out, true);
753#else
754 XDRStreamMarshaller m(out);
755#endif
756
757 try {
758 // Send all variables in the current projection (send_p())
759 for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++)
760 if ((*i)->send_p()) {
761 DBG(cerr << "Sending " << (*i)->name() << endl);
762#ifdef CHECKSUMS
763 if ((*i)->type() != dods_structure_c && (*i)->type() != dods_grid_c)
764 m.reset_checksum();
765
766 (*i)->serialize(eval, dds, m, ce_eval);
767
768 if ((*i)->type() != dods_structure_c && (*i)->type() != dods_grid_c)
769 cerr << (*i)->name() << ": " << m.get_checksum() << endl;
770#else
771 (*i)->serialize(eval, dds, m, ce_eval);
772#endif
773 }
774 }
775 catch (Error & e) {
776 throw;
777 }
778}
779
784 const string &boundary, const string &start, bool ce_eval)
785{
786 // Write the MPM headers for the DDX (text/xml) part of the response
787 set_mime_ddx_boundary(out, boundary, start);
788
789 // Make cid
790 uuid_t uu;
791 uuid_generate(uu);
792 char uuid[37];
793 uuid_unparse(uu, uuid.data());
794 char domain[256];
795 if (getdomainname(domain, 255) != 0 || strlen(domain) == 0)
796 strncpy(domain, "opendap.org", 255);
797
798 string cid = string(uuid.data()) + "@" + string(domain.data());
799
800 // Send constrained DDX with a data blob reference
801 dds.print_xml_writer(out, true, cid);
802
803 // Grab a stream that encodes for DAP4
804#ifdef DAP4
805 D4StreamMarshaller m(out);
806
807 // Write the MPM headers for the data part of the response.
808 set_mime_data_boundary(out, boundary, cid, m.get_endian(), 0);
809#else
810 XDRStreamMarshaller m(out);
811#endif // DAP4
812
813
814 // Send all variables in the current projection (send_p()). In DAP4,
815 // all of the top-level variables are serialized with their checksums.
816 // Internal variables are not.
817 // TODO When Group support is added to libdap, this will need to be
818 // generalized so that all variables in the top-levels of all the
819 // groups will have checksums included in the response.
820 for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++) {
821 if ((*i)->send_p()) {
822 DBG(cerr << "Sending " << (*i)->name() << endl);
823
824#ifdef DAP4
825 m.reset_checksum();
826#endif
827
828 (*i)->serialize(eval, dds, m, ce_eval);
829
830#ifdef DAP4
831 m.put_checksum();
832#endif
833 }
834 }
835}
836
853void D4ResponseBuilder::send_data(ostream & data_stream, DDS & dds, ConstraintEvaluator & eval,
854 bool with_mime_headers)
855{
856 // Set up the alarm.
857 establish_timeout(data_stream);
859
860#if 0
861 eval.parse_constraint(d_dap4ce, dds); // Throws Error if the ce doesn't parse.
862
863 dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
864
865 if (dds.get_response_limit() != 0 && dds.get_request_size(true) > dds.get_response_limit()) {
866 string msg = "The Request for " + long_to_string(dds.get_request_size(true) / 1024)
867 + "KB is too large; requests for this user are limited to "
868 + long_to_string(dds.get_response_limit() / 1024) + "KB.";
869 throw Error(msg);
870 }
871#endif
872
873 // Split constraint into two halves
874 split_ce(eval);
875
876 // If there are functions, parse them and eval.
877 // Use that DDS and parse the non-function ce
878 // Serialize using the second ce and the second dds
879 if (!d_dap4_btp_func_expr.empty()) {
880 DBG(cerr << "Found function(s) in CE: " << d_dap4_btp_func_expr << endl);
881#if 0
882 // These are used for the cached or newly created DDS object
883 BaseTypeFactory factory;
884 DDS *fdds;
885
886 // Get the cache filename for this thing. Do not use the default
887 // name mangling; instead use what build_cache_file_name() does.
888 string cache_file_name = d_cache->get_cache_file_name(build_cache_file_name(d_dataset, d_dap4_btp_func_expr), false);
889 int fd;
890 try {
891 // If the object in the cache is not valid, remove it. The read_lock will
892 // then fail and the code will drop down to the create_and_lock() call.
893 // is_valid() tests for a non-zero object and for d_dateset newer than
894 // the cached object.
895 if (!is_valid(cache_file_name))
896 d_cache->purge_file(cache_file_name);
897
898 if (d_cache->get_read_lock(cache_file_name, fd)) {
899 DBG(cerr << "function ce - cached hit: " << cache_file_name << endl );
900 fdds = get_cached_dap2_data_ddx(cache_file_name, &factory);
901 }
902
903 // If here, the cache_file_name could not be locked for read access;
904 // try to build it. First make an empty file and get an exclusive lock on it.
905 if (d_cache->create_and_lock(cache_file_name, fd)) {
906 DBG(cerr << "function ce - caching " << cache_file_name << endl );
907
908 eval.parse_constraint(d_dap4_btp_func_expr, dds);
909 fdds = eval.eval_function_clauses(dds);
910
911 // TODO cache it using fd. Since this is advisory locking, this will work...
912 // Improve?
913 // Until Connect/Response support working with file descriptors, it's
914 // better to use the names.
915 cache_data_ddx(cache_file_name, *fdds);
916
917 // Change the exclusive lock on the new file to a shared lock. This keeps
918 // other processes from purging the new file and ensures that the reading
919 // process can use it.
920 d_cache->exclusive_to_shared_lock(fd);
921
922 // Now update the total cache size info and purge if needed. The new file's
923 // name is passed into the purge method because this process cannot detect its
924 // own lock on the file.
925 unsigned long long size = d_cache->update_cache_info(cache_file_name);
926 if (d_cache->cache_too_big(size))
927 d_cache->update_and_purge(cache_file_name);
928 }
929 else if (d_cache->get_read_lock(cache_file_name, fd)) {
930 DBG(cerr << "function ce - cached hit: " << cache_file_name << endl );
931 fdds = get_cached_dap2_data_ddx(cache_file_name, &factory);
932 }
933 else {
934 throw InternalErr(__FILE__, __LINE__, "Cache error during function invocation.");
935 }
936 }
937 catch (...) {
938 DBG(cerr << "caught exception, unlocking cache and re-throw." << endl );
939 // I think this call is not needed. jhrg 10/23/12
940 d_cache->unlock_cache();
941 throw;
942 }
943#endif
944#if 0
945 // ******** original code here ***********
946
947 // Check to see if the cached data ddx exists and is valid
948 if (cached_data_ddx_exists(cache_file_name)) {
949 fdds = get_cached_dap2_data_ddx(cache_file_name, &factory);
950#if 0
951 // Use the cache file and don't eval the function(s)
952 DBG(cerr << "Reading cache for " << d_dataset + "?" + d_dap4_btp_func_expr << endl);
953 icache_file.close(); // only opened to see if it's there; Connect/Response do their own thing
954
955 fdds = new DDS(&factory);
956 fdds->set_dap_version("4.0"); // TODO note about cid, ...
957 // FIXME name should be...
958 fdds->filename( d_dataset ) ;
960
961 Connect *url = new Connect( d_dataset ) ;
962 Response *r = new Response( fopen( cache_file_name.c_str(), "r" ), 0 ) ;
963 if( !r->get_stream() )
964 throw Error("The input source: " + cache_file_name + " could not be opened");
965
966 url->read_data( *fdds, r ) ;
967 fdds->set_factory( 0 ) ;
968
969 // mark everything as read.
970 DDS::Vars_iter i = fdds->var_begin() ;
971 DDS::Vars_iter e = fdds->var_end() ;
972 for( ; i != e; i++ ) {
973 BaseType *b = (*i) ;
974 b->set_read_p( true ) ;
975 }
976 // for_each(dds->var_begin(), dds->var_end(), mfunc(BaseType::set_read_p));
977
978 DAS *das = new DAS ;
980 fdds->transfer_attributes( das ) ;
981#endif
982 }
983 else {
984 eval.parse_constraint(d_dap4_btp_func_expr, dds);
985 fdds = eval.eval_function_clauses(dds);
986
987 cache_data_ddx(cache_file_name, *fdds);
988#if 0
989 // TODO cache the fdds here
990 ofstream ocache_file(cache_file_name.c_str());
991
992 DBG(cerr << "Caching " << d_dataset + "?" + d_dap4_btp_func_expr << endl);
993 cache_data_ddx(ocache_file, *fdds);
994 ocache_file.close();
995#endif
996 }
997#endif
998 string cache_token = "";
999 DDS *fdds = 0;
1000
1001 if (d_cache) {
1002 DBG(cerr << "Using the cache for the server function CE" << endl);
1003 fdds = read_cached_dataset(dds, eval, cache_token);
1004 }
1005 else {
1006 DBG(cerr << "Cache not found; (re)calculating" << endl);
1007 eval.parse_constraint(d_dap4_btp_func_expr, dds);
1008 fdds = eval.eval_function_clauses(dds);
1009 }
1010
1011 DBG(cerr << "Intermediate DDS: " << endl);
1012 DBG(fdds->print_constrained(cerr));
1013
1014 DBG(cerr << "Parsing remaining CE: " << d_dap4ce << endl);
1015
1016 // Server functions might mark variables to use their read()
1017 // methods. Clear that so the CE in d_dap4ce will control what is
1018 // sent. If that is empty (there was only a function call) all
1019 // of the variables in the intermediate DDS (i.e., the function
1020 // result) will be sent.
1021 fdds->mark_all(false);
1022
1023 eval.parse_constraint(d_dap4ce, *fdds);
1024
1025 fdds->tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
1026
1027 if (fdds->get_response_limit() != 0 && fdds->get_request_size(true) > fdds->get_response_limit()) {
1028 string msg = "The Request for " + long_to_string(dds.get_request_size(true) / 1024)
1029 + "KB is too large; requests for this user are limited to "
1030 + long_to_string(dds.get_response_limit() / 1024) + "KB.";
1031 throw Error(msg);
1032 }
1033
1034 if (with_mime_headers)
1036
1037 DBG(cerr << "About to call dataset_constraint" << endl);
1038 dataset_constraint(data_stream, *fdds, eval, false);
1039
1040 if (d_cache)
1041 d_cache->unlock_and_close(cache_token);
1042
1043 delete fdds;
1044 }
1045 else {
1046 DBG(cerr << "Simple constraint" << endl);
1047
1048 eval.parse_constraint(d_dap4ce, dds); // Throws Error if the ce doesn't parse.
1049
1050 dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
1051
1052 if (dds.get_response_limit() != 0 && dds.get_request_size(true) > dds.get_response_limit()) {
1053 string msg = "The Request for " + long_to_string(dds.get_request_size(true) / 1024)
1054 + "KB is too large; requests for this user are limited to "
1055 + long_to_string(dds.get_response_limit() / 1024) + "KB.";
1056 throw Error(msg);
1057 }
1058
1059 if (with_mime_headers)
1061
1062 dataset_constraint(data_stream, dds, eval);
1063 }
1064
1065#if 0
1066 // Start sending the response...
1067
1068 // Handle *functional* constraint expressions specially
1069 if (eval.function_clauses()) {
1070 DDS *fdds = eval.eval_function_clauses(dds);
1071 if (with_mime_headers)
1073
1074 serialize_dap2_data_dds(data_stream, *fdds, eval, false);
1075 delete fdds;
1076 }
1077 else {
1078 if (with_mime_headers)
1080
1081 serialize_dap2_data_dds(data_stream, dds, eval);
1082 }
1083#endif
1084
1085 data_stream << flush;
1086}
1087
1106void D4ResponseBuilder::send_ddx(ostream &out, DDS &dds, ConstraintEvaluator &eval, bool with_mime_headers)
1107{
1108 // If constrained, parse the constraint. Throws Error or InternalErr.
1109 if (!d_dap4ce.empty())
1110 eval.parse_constraint(d_dap4ce, dds);
1111
1112 if (eval.functional_expression())
1113 throw Error(
1114 "Function calls can only be used with data requests. To see the structure of the underlying data source, reissue the URL without the function.");
1115
1116 if (with_mime_headers)
1118
1119 dds.print_xml_writer(out, !d_dap4ce.empty(), "");
1120}
1121
1142void D4ResponseBuilder::send_data_ddx(ostream & data_stream, DDS & dds, ConstraintEvaluator & eval, const string &start,
1143 const string &boundary, bool with_mime_headers)
1144{
1145 // Set up the alarm.
1146 establish_timeout(data_stream);
1148
1149 eval.parse_constraint(d_dap4ce, dds); // Throws Error if the ce doesn't parse.
1150
1151 if (dds.get_response_limit() != 0 && dds.get_request_size(true) > dds.get_response_limit()) {
1152 string msg = "The Request for " + long_to_string(dds.get_request_size(true) / 1024)
1153 + "KB is too large; requests for this user are limited to "
1154 + long_to_string(dds.get_response_limit() / 1024) + "KB.";
1155 throw Error(msg);
1156 }
1157
1158 dds.tag_nested_sequences(); // Tag Sequences as Parent or Leaf node.
1159
1160 // Start sending the response...
1161
1162 // Handle *functional* constraint expressions specially
1163 if (eval.function_clauses()) {
1164 // We could unique_ptr<DDS> here to avoid memory leaks if
1165 // serialize_dap2_data_ddx() throws an exception.
1166 DDS *fdds = eval.eval_function_clauses(dds);
1167 try {
1168 if (with_mime_headers)
1169 set_mime_multipart(data_stream, boundary, start, dods_ddx, x_plain, last_modified_time(d_dataset));
1170 data_stream << flush;
1171 dataset_constraint_ddx(data_stream, *fdds, eval, boundary, start);
1172 }
1173 catch (...) {
1174 delete fdds;
1175 throw;
1176 }
1177 delete fdds;
1178 }
1179 else {
1180 if (with_mime_headers)
1181 set_mime_multipart(data_stream, boundary, start, dods_ddx, x_plain, last_modified_time(d_dataset));
1182 data_stream << flush;
1183 dataset_constraint_ddx(data_stream, dds, eval, boundary, start);
1184 }
1185
1186 data_stream << flush;
1187
1188 if (with_mime_headers)
1189 data_stream << CRLF << "--" << boundary << "--" << CRLF;
1190}
1191
1192#ifdef DAP4
1203void
1204D4ResponseBuilder::send_dmr(ostream &out, DDS &dds, ConstraintEvaluator &eval)
1205{
1206 // If constrained, parse the constraint. Throws Error or InternalErr.
1207 if (!d_dap4ce.empty())
1208 eval.parse_constraint(d_dap4ce, dds);
1209
1210 // TODO Change functions so this is no longer an error
1211 if (eval.functional_expression())
1212 throw Error(
1213 "Function calls can only be used with data requests. To see the structure of the underlying data source, reissue the URL without the function.");
1214
1215 dds.print_dmr(out, !d_dap4ce.empty());
1216}
1217#endif // DAP4
1218
1230
1231void D4ResponseBuilder::cache_data_ddx(const string &cache_file_name, DDS &dds)
1232{
1233 DBG(cerr << "Caching " << d_dataset + "?" + d_dap4_btp_func_expr << endl);
1234
1235 ofstream data_stream(cache_file_name.c_str());
1236 // Test for a valid file open
1237
1238 string start="dataddx_cache_start", boundary="dataddx_cache_boundary";
1239#if 1
1240 // Does this really need the full set of MIME headers? Not including these
1241 // might make it comparable with the dapreader module in the BES.
1242 set_mime_multipart(data_stream, boundary, start, dods_data_ddx, x_plain, last_modified_time(d_dataset));
1243 data_stream << flush;
1244#endif
1245
1246 // serialize_dap2_data_ddx() needs a ConstraintEvaluator because
1247 // it calls serialize().
1249
1250 // Setting the DDS version to 3.2 causes the print_xml() code
1251 // to write out a 'blob' element with a valid cid. The reader
1252 // code in Connect needs this (or thinks it does...)
1253 dds.set_dap_version("3.2");
1254
1255 dataset_constraint_ddx(data_stream, dds, eval, boundary, start);
1256 data_stream << flush;
1257
1258 data_stream << CRLF << "--" << boundary << "--" << CRLF;
1259 data_stream.close();
1260}
1261
1281{
1282 // Rip off the MIME headers from the response if they are present
1283 string mime = get_next_mime_header(data);
1284 while (!mime.empty()) {
1285#if 0
1286 string header, value;
1287 parse_mime_header(mime, header, value);
1288#endif
1289 mime = get_next_mime_header(data);
1290 }
1291
1292 // Parse the DDX; throw an exception on error.
1293 DDXParser ddx_parser(fdds->get_factory());
1294
1295 // Read the MPM boundary and then read the subsequent headers
1296 string boundary = read_multipart_boundary(data);
1297 DBG(cerr << "MPM Boundary: " << boundary << endl);
1298
1299 read_multipart_headers(data, "text/xml", dods_ddx);
1300
1301 // Parse the DDX, reading up to and including the next boundary.
1302 // Return the CID for the matching data part
1303 string data_cid;
1304 ddx_parser.intern_stream(data, fdds, data_cid, boundary);
1305
1306 // Munge the CID into something we can work with
1307 data_cid = cid_to_header_value(data_cid);
1308 DBG(cerr << "Data CID: " << data_cid << endl);
1309
1310 // Read the data part's MPM part headers (boundary was read by
1311 // DDXParse::intern)
1312 read_multipart_headers(data, "application/octet-stream", dap4_data, data_cid);
1313
1314 // Now read the data
1315
1316 XDRFileUnMarshaller um(data);
1317 for (DDS::Vars_iter i = fdds->var_begin(); i != fdds->var_end(); i++) {
1318 (*i)->deserialize(um, fdds);
1319 }
1320}
1321
1325DDS *
1326D4ResponseBuilder::get_cached_data_ddx(const string &cache_file_name, BaseTypeFactory *factory)
1327{
1328 DBG(cerr << "Reading cache for " << d_dataset + "?" + d_dap4_btp_func_expr << endl);
1329
1330 DDS *fdds = new DDS(factory);
1331
1332 fdds->filename( d_dataset ) ;
1333 fdds->set_dataset_name( "function_result_" + name_path( d_dataset ) ) ;
1334
1335#if 0
1336 Connect *url = new Connect( d_dataset ) ;
1337 Response *r = new Response( fopen( cache_file_name.c_str(), "r" ), 0 ) ;
1338 if( !r->get_stream() )
1339 throw Error("The input source: " + cache_file_name + " could not be opened");
1340
1341 url->read_data( *fdds, r ) ;
1342#endif
1343
1344 // fstream data(cache_file_name.c_str());
1345 FILE *data = fopen( cache_file_name.c_str(), "r" );
1346 read_data_from_cache(data, fdds);
1347 fclose(data);
1348
1349 fdds->set_factory( 0 ) ;
1350
1351 // mark everything as read.
1352 DDS::Vars_iter i = fdds->var_begin() ;
1353 DDS::Vars_iter e = fdds->var_end() ;
1354 for( ; i != e; i++ ) {
1355 BaseType *b = (*i) ;
1356 b->set_read_p( true ) ;
1357 }
1358
1359 // for_each(dds->var_begin(), dds->var_end(), mfunc(BaseType::set_read_p));
1360
1361#if 0
1362 // Ancillary attributes were read when the DDX was built and are part of the
1363 // cached BLOB.
1364 DAS *das = new DAS ;
1366 fdds->transfer_attributes( das ) ;
1367#endif
1368 return fdds;
1369}
1370
1371#ifdef DAP4
1385void
1386D4ResponseBuilder::send_dap4_data(ostream &out, DDS &dds, ConstraintEvaluator &eval)
1387{
1388 throw InternalErr(__FILE__, __LINE__, "ResponseBuilder::send_dap4_data: Not implemented");
1389
1390 // TODO
1391 // Print the chunk offset info so that clients can skip the DMR and go
1392 // directly to the data.
1393
1394 // Send constrained DMR
1395 dds.print_dmr(out, !d_dap4ce.empty());
1396
1397 // Grab a stream that encodes for DAP4
1398 D4StreamMarshaller m(out);
1399
1400 // TODO Write word order information
1401
1402 // Send all variables in the current projection (send_p()). In DAP4,
1403 // all of the top-level variables are serialized with their checksums.
1404 // Internal variables are not.
1405 //
1406 // TODO When Group support is added to libdap, this will need to be
1407 // generalized so that all variables in the top-levels of all the
1408 // groups will have checksums included in the response.
1409 //
1410 // TODO Switch to the DAP4 serialization method once it's written
1411 for (DDS::Vars_iter i = dds.var_begin(); i != dds.var_end(); i++) {
1412 if ((*i)->send_p()) {
1413 DBG(cerr << "Sending " << (*i)->name() << endl);
1414
1415 m.reset_checksum();
1416
1417 // FIXME Replace with DAP4 call
1418 (*i)->serialize(eval, dds, m, true);
1419
1420 m.put_checksum();
1421 }
1422 }
1423
1424}
1425#endif // DAP4
1426
1427static const char *descrip[] = { "unknown", "dods_das", "dods_dds", "dods_data", "dods_error", "web_error", "dap4-ddx",
1428 "dap4-data", "dap4-error", "dap4-data-ddx", "dods_ddx" };
1429static const char *encoding[] = { "unknown", "deflate", "x-plain", "gzip", "binary" };
1430
1446void D4ResponseBuilder::set_mime_text(ostream &strm, ObjectType type, EncodingType enc, const time_t last_modified,
1447 const string &protocol) const
1448{
1449 strm << "HTTP/1.0 200 OK" << CRLF;
1450
1451 strm << "XDODS-Server: " << DVR<< CRLF;
1452 strm << "XOPeNDAP-Server: " << DVR<< CRLF;
1453
1454 if (protocol == "")
1455 strm << "XDAP: " << d_default_protocol << CRLF;
1456 else
1457 strm << "XDAP: " << protocol << CRLF;
1458
1459 const time_t t = time(0);
1460 strm << "Date: " << rfc822_date(t).c_str() << CRLF;
1461
1462 strm << "Last-Modified: ";
1463 if (last_modified > 0)
1464 strm << rfc822_date(last_modified).c_str() << CRLF;
1465 else
1466 strm << rfc822_date(t).c_str() << CRLF;
1467
1468 if (type == dods_ddx)
1469 strm << "Content-Type: text/xml" << CRLF;
1470 else
1471 strm << "Content-Type: text/plain" << CRLF;
1472
1473 // Note that Content-Description is from RFC 2045 (MIME, pt 1), not 2616.
1474 // jhrg 12/23/05
1475 strm << "Content-Description: " << descrip[type] << CRLF;
1476 if (type == dods_error) // don't cache our error responses.
1477 strm << "Cache-Control: no-cache" << CRLF;
1478 // Don't write a Content-Encoding header for x-plain since that breaks
1479 // Netscape on NT. jhrg 3/23/97
1480 if (enc != x_plain)
1481 strm << "Content-Encoding: " << encoding[enc] << CRLF;
1482 strm << CRLF;
1483}
1484
1495void D4ResponseBuilder::set_mime_html(ostream &strm, ObjectType type, EncodingType enc, const time_t last_modified,
1496 const string &protocol) const
1497{
1498 strm << "HTTP/1.0 200 OK" << CRLF;
1499
1500 strm << "XDODS-Server: " << DVR<< CRLF;
1501 strm << "XOPeNDAP-Server: " << DVR<< CRLF;
1502
1503 if (protocol == "")
1504 strm << "XDAP: " << d_default_protocol << CRLF;
1505 else
1506 strm << "XDAP: " << protocol << CRLF;
1507
1508 const time_t t = time(0);
1509 strm << "Date: " << rfc822_date(t).c_str() << CRLF;
1510
1511 strm << "Last-Modified: ";
1512 if (last_modified > 0)
1513 strm << rfc822_date(last_modified).c_str() << CRLF;
1514 else
1515 strm << rfc822_date(t).c_str() << CRLF;
1516
1517 strm << "Content-type: text/html" << CRLF;
1518 // See note above about Content-Description header. jhrg 12/23/05
1519 strm << "Content-Description: " << descrip[type] << CRLF;
1520 if (type == dods_error) // don't cache our error responses.
1521 strm << "Cache-Control: no-cache" << CRLF;
1522 // Don't write a Content-Encoding header for x-plain since that breaks
1523 // Netscape on NT. jhrg 3/23/97
1524 if (enc != x_plain)
1525 strm << "Content-Encoding: " << encoding[enc] << CRLF;
1526 strm << CRLF;
1527}
1528
1542void D4ResponseBuilder::set_mime_binary(ostream &strm, ObjectType type, EncodingType enc, const time_t last_modified,
1543 const string &protocol) const
1544{
1545 strm << "HTTP/1.0 200 OK" << CRLF;
1546
1547 strm << "XDODS-Server: " << DVR<< CRLF;
1548 strm << "XOPeNDAP-Server: " << DVR<< CRLF;
1549
1550 if (protocol == "")
1551 strm << "XDAP: " << d_default_protocol << CRLF;
1552 else
1553 strm << "XDAP: " << protocol << CRLF;
1554
1555 const time_t t = time(0);
1556 strm << "Date: " << rfc822_date(t).c_str() << CRLF;
1557
1558 strm << "Last-Modified: ";
1559 if (last_modified > 0)
1560 strm << rfc822_date(last_modified).c_str() << CRLF;
1561 else
1562 strm << rfc822_date(t).c_str() << CRLF;
1563
1564 strm << "Content-Type: application/octet-stream" << CRLF;
1565 strm << "Content-Description: " << descrip[type] << CRLF;
1566 if (enc != x_plain)
1567 strm << "Content-Encoding: " << encoding[enc] << CRLF;
1568
1569 strm << CRLF;
1570}
1571
1573
1574void D4ResponseBuilder::set_mime_multipart(ostream &strm, const string &boundary, const string &start, ObjectType type, EncodingType enc,
1575 const time_t last_modified, const string &protocol, const string &url) const
1576{
1577 strm << "HTTP/1.1 200 OK" << CRLF;
1578
1579 const time_t t = time(0);
1580 strm << "Date: " << rfc822_date(t).c_str() << CRLF;
1581
1582 strm << "Last-Modified: ";
1583 if (last_modified > 0)
1584 strm << rfc822_date(last_modified).c_str() << CRLF;
1585 else
1586 strm << rfc822_date(t).c_str() << CRLF;
1587
1588 strm << "Content-Type: multipart/related; boundary=" << boundary << "; start=\"<" << start
1589 << ">\"; type=\"text/xml\"" << CRLF;
1590
1591 // data-ddx;"; removed as a result of the merge of the hyrax 1.8 release
1592 // branch.
1593 strm << "Content-Description: " << descrip[type] << ";";
1594 if (!url.empty())
1595 strm << " url=\"" << url << "\"" << CRLF;
1596 else
1597 strm << CRLF;
1598
1599 if (enc != x_plain)
1600 strm << "Content-Encoding: " << encoding[enc] << CRLF;
1601
1602 if (protocol == "")
1603 strm << "X-DAP: " << d_default_protocol << CRLF;
1604 else
1605 strm << "X-DAP: " << protocol << CRLF;
1606
1607 strm << "X-OPeNDAP-Server: " << DVR<< CRLF;
1608
1609 strm << CRLF;
1610}
1611
1612void D4ResponseBuilder::set_mime_ddx_boundary(ostream &strm, const string &boundary, const string &cid) const
1613{
1614 strm << "--" << boundary << CRLF;
1615 strm << "Content-Type: text/xml; charset=UTF-8" << CRLF;
1616 strm << "Content-Transfer-Encoding: binary" << CRLF;
1617 strm << "Content-Description: ddx" << CRLF;
1618 strm << "Content-Id: <" << cid << ">" << CRLF;
1619
1620 strm << CRLF;
1621}
1622
1623void D4ResponseBuilder::set_mime_data_boundary(ostream &strm, const string &boundary, const string &cid,
1624 const string &endian, unsigned long long len) const
1625{
1626 strm << "--" << boundary << CRLF;
1627 strm << "Content-Type: application/x-dap-" << endian << "-endian" << CRLF;
1628 strm << "Content-Transfer-Encoding: binary" << CRLF;
1629 strm << "Content-Description: data" << CRLF;
1630 strm << "Content-Id: <" << cid << ">" << CRLF;
1631 strm << "Content-Length: " << len << CRLF;
1632
1633 strm << CRLF;
1634}
1635
1642void D4ResponseBuilder::set_mime_error(ostream &strm, int code, const string &reason, const string &protocol) const
1643{
1644 strm << "HTTP/1.0 " << code << " " << reason.c_str() << CRLF;
1645
1646 strm << "XDODS-Server: " << DVR<< CRLF;
1647 strm << "X-OPeNDAP-Server: " << DVR<< CRLF;
1648
1649 if (protocol == "")
1650 strm << "X-DAP: " << d_default_protocol << CRLF;
1651 else
1652 strm << "X-DAP: " << protocol << CRLF;
1653
1654 const time_t t = time(0);
1655 strm << "Date: " << rfc822_date(t).c_str() << CRLF;
1656 strm << "Cache-Control: no-cache" << CRLF;
1657 strm << CRLF;
1658}
1659
1660} // namespace libdap
1661
#define CRLF
#define FUNCTION_CACHE_SIZE
#define FUNCTION_CACHE_PREFIX
#define FUNCTION_CACHE
static void read_ancillary_das(DAS &das, const string &pathname, const string &dir="", const string &file="")
Definition Ancillary.cc:185
The basic data type for the DODS DAP types.
Definition BaseType.h:118
virtual void set_read_p(bool state)
Sets the value of the read_p property.
Definition BaseType.cc:442
Holds information about the link from a DAP2 client to a dataset.
Definition Connect.h:127
virtual void read_data(DataDDS &data, Response *rs)
Read data which is preceded by MIME headers. This method works for both data dds and data ddx respons...
Definition Connect.cc:948
Evaluate a constraint expression.
bool find_function(const std::string &name, bool_func *f) const
Find a Boolean function with a given name in the function list.
void parse_constraint(const std::string &constraint, DDS &dds)
Parse the constraint expression given the current DDS.
bool function_clauses()
Does the current constraint expression contain function clauses.
DDS * eval_function_clauses(DDS &dds)
Evaluate a function-valued constraint expression that contains several function calls.
bool functional_expression()
Does the current constraint expression return a BaseType pointer? This method does not evaluate the c...
void set_mime_ddx_boundary(ostream &out, const string &boundary, const string &start) const
virtual void cache_data_ddx(const string &cache_file_name, DDS &dds)
Cache data.
virtual void set_ce(const string &ce)
int d_timeout
The BTP functions, extracted from the CE.
string d_default_protocol
Response timeout after N seconds.
virtual string ce() const
void set_mime_binary(ostream &out, ObjectType type=unknown_type, EncodingType enc=x_plain, const time_t last_modified=0, const string &protocol="") const
virtual bool is_valid(const string &cache_file_name)
virtual void dataset_constraint_ddx(ostream &out, DDS &dds, ConstraintEvaluator &eval, const string &boundary, const string &start, bool ce_eval=true)
virtual void remove_timeout() const
void set_mime_data_boundary(ostream &out, const string &boundary, const string &cid, const string &endian, unsigned long long len) const
virtual void send_data(ostream &data_stream, DDS &dds, ConstraintEvaluator &eval, bool with_mime_headers=true)
Transmit data.
void set_mime_error(ostream &out, int code=404, const string &reason="Dataset not found", const string &protocol="") const
virtual void establish_timeout(ostream &stream) const
virtual DDS * read_cached_dataset(DDS &dds, ConstraintEvaluator &eval, string &cache_token)
void set_mime_html(ostream &out, ObjectType type=unknown_type, EncodingType enc=x_plain, const time_t last_modified=0, const string &protocol="") const
virtual void split_ce(ConstraintEvaluator &eval, const string &expr="")
void set_mime_text(ostream &out, ObjectType type=unknown_type, EncodingType enc=x_plain, const time_t last_modified=0, const string &protocol="") const
virtual void send_data_ddx(ostream &data_stream, DDS &dds, ConstraintEvaluator &eval, const string &start, const string &boundary, bool with_mime_headers=true)
Transmit data.
virtual void send_ddx(ostream &out, DDS &dds, ConstraintEvaluator &eval, bool with_mime_headers=true)
virtual DDS * get_cached_data_ddx(const string &cache_file_name, BaseTypeFactory *factory)
virtual void dataset_constraint(ostream &out, DDS &dds, ConstraintEvaluator &eval, bool ce_eval=true)
virtual void set_dataset_name(const string &ds)
virtual void send_dds(ostream &out, DDS &dds, ConstraintEvaluator &eval, bool constrained=false, bool with_mime_headers=true)
Transmit a DDS.
DAPCache3 * d_cache
Version string for the library's default protocol version.
void set_mime_multipart(ostream &out, const string &boundary, const string &start, ObjectType type=unknown_type, EncodingType enc=x_plain, const time_t last_modified=0, const string &protocol="", const string &url="") const
virtual void read_data_from_cache(FILE *data, DDS *fdds)
Marshaller that knows how to marshal/serialize dap data objects to a C++ iostream using DAP4's receiv...
virtual void put_checksum()
Write the checksum Write the checksum for the data sent since the last call to reset_checksum() to th...
static DAPCache3 * get_instance()
Definition DAPCache3.cc:148
Hold attribute data for a DAP2 dataset.
Definition DAS.h:119
virtual void print(FILE *out, bool dereference=false)
Definition DAS.cc:304
void set_dataset_name(const string &n)
Definition DDS.cc:271
void mark_all(bool state)
Definition DDS.cc:1381
void print_dmr(ostream &out, bool constrained)
Print the DAP4 DMR object using a DDS.
Definition DDS.cc:1228
string filename() const
Definition DDS.cc:287
string get_dap_version() const
Definition DDS.h:256
virtual void transfer_attributes(DAS *das)
Definition DDS.cc:231
void print(FILE *out)
Print the entire DDS to the specified file.
Definition DDS.cc:812
int get_request_size(bool constrained)
Get the estimated response size in bytes.
Definition DDS.cc:438
BaseTypeFactory * set_factory(BaseTypeFactory *factory)
Definition DDS.h:239
void tag_nested_sequences()
Traverse DDS, set Sequence leaf nodes.
Definition DDS.cc:730
BaseTypeFactory * get_factory() const
Definition DDS.h:231
Vars_iter var_begin()
Definition DDS.h:336
void print_constrained(FILE *out)
Print a constrained DDS to the specified file.
Definition DDS.cc:1071
void set_timeout(int t)
Definition DDS.cc:714
Vars_iter var_end()
Return an iterator.
Definition DDS.h:341
void set_dap_version(const string &version_string="2.0")
Definition DDS.cc:328
std::vector< BaseType * >::iterator Vars_iter
Definition DDS.h:211
void print_xml_writer(ostream &out, bool constrained, const string &blob="")
Definition DDS.cc:1148
long get_response_limit()
Get the maximum response size, in bytes. Zero indicates no limit.
Definition DDS.h:279
void intern_stream(FILE *in, DDS *dds, string &cid, const string &boundary="")
Read the DDX from a stream instead of a file.
A class for error processing.
Definition Error.h:92
A class for software fault reporting.
Definition InternalErr.h:61
virtual FILE * get_stream() const
Definition Response.h:103
static EventHandler * register_handler(int signum, EventHandler *eh, bool ignore_by_default=false)
static SignalHandler * instance()
unmarshaller that knows how to unmarshall/deserialize dap objects using XDR from a file
Marshaller that knows how serialize dap data objects to a C++ iostream using XDR.
#define DAP_PROTOCOL_VERSION
Definition config.h:41
#define DVR
Definition config.h:80
#define DBG(x)
Definition debug.h:58
top level DAP object to house generic methods
Definition AISConnect.cc:30
@ dods_grid_c
Definition Type.h:111
@ dods_structure_c
Definition Type.h:106
string read_multipart_boundary(FILE *in, const string &boundary)
Definition mime_util.cc:893
string cid_to_header_value(const string &cid)
string long_to_string(long val, int base)
Definition util.cc:946
string www2id(const string &in, const string &escape, const string &except)
Definition escaping.cc:202
void parse_mime_header(const string &header, string &name, string &value)
Definition mime_util.cc:848
time_t last_modified_time(const string &name)
Definition mime_util.cc:94
string name_path(const string &path)
Returns the filename portion of a pathname.
Definition mime_util.cc:250
bool dir_exists(const string &dir)
Definition util.cc:912
void read_multipart_headers(FILE *in, const string &content_type, const ObjectType object_type, const string &cid)
Definition mime_util.cc:935
EncodingType
The type of encoding used on the current stream.
string rfc822_date(const time_t t)
Definition mime_util.cc:148
ObjectType
The type of object in the stream coming from the data server.
Definition ObjectType.h:57
@ dods_das
Definition ObjectType.h:59
@ dods_data_ddx
Definition ObjectType.h:63
@ dap4_data
Definition ObjectType.h:68
@ dods_error
Definition ObjectType.h:64
@ dods_data
Definition ObjectType.h:61
@ dods_dds
Definition ObjectType.h:60
@ dods_ddx
Definition ObjectType.h:62
string get_next_mime_header(FILE *in)
Definition mime_util.cc:777