libdap++  Updated for version 3.12.0
Connect.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) 2002,2003 OPeNDAP, Inc.
7 // Author: James Gallagher <jgallagher@opendap.org>
8 // Dan Holloway <dholloway@gso.uri.edu>
9 // Reza Nekovei <reza@intcomm.net>
10 //
11 // This library is free software; you can redistribute it and/or
12 // modify it under the terms of the GNU Lesser General Public
13 // License as published by the Free Software Foundation; either
14 // version 2.1 of the License, or (at your option) any later version.
15 //
16 // This library is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 // Lesser General Public License for more details.
20 //
21 // You should have received a copy of the GNU Lesser General Public
22 // License along with this library; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 //
25 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
26 
27 // (c) COPYRIGHT URI/MIT 1994-2002
28 // Please read the full copyright statement in the file COPYRIGHT_URI.
29 //
30 // Authors:
31 // jhrg,jimg James Gallagher <jgallagher@gso.uri.edu>
32 // dan Dan Holloway <dholloway@gso.uri.edu>
33 // reza Reza Nekovei <reza@intcomm.net>
34 
35 #include "config.h"
36 
37 //#define DODS_DEBUG
38 #define FILE_UN_MARSHALLER 1
39 
40 #include <cstring>
41 #include <fstream>
42 #include <algorithm>
43 
44 #include "debug.h"
45 #include "DataDDS.h"
46 #include "Connect.h"
47 #include "escaping.h"
48 //#include "RCReader.h"
49 #include "DDXParserSAX2.h"
50 #if FILE_UN_MARSHALLER
51 #include "XDRFileUnMarshaller.h"
52 #else
53 #include "fdiostream.h"
54 #include "XDRStreamUnMarshaller.h"
55 #endif
56 #include "mime_util.h"
57 
58 using std::cerr;
59 using std::endl;
60 using std::ifstream;
61 using std::ofstream;
62 using std::min;
63 
64 namespace libdap {
65 
68 void Connect::process_data(DataDDS &data, Response *rs)
69 {
70  DBG(cerr << "Entering Connect::process_data" << endl);
71 
72  data.set_version(rs->get_version());
73  data.set_protocol(rs->get_protocol());
74 
75  DBG(cerr << "Entering process_data: d_stream = " << rs << endl);
76  switch (rs->get_type()) {
77  case dods_error: {
78  Error e;
79  if (!e.parse(rs->get_stream()))
80  throw InternalErr(__FILE__, __LINE__, "Could not parse the Error object returned by the server!");
81  throw e;
82  }
83 
84  case web_error:
85  // Web errors (those reported in the return document's MIME header)
86  // are processed by the WWW library.
87  throw InternalErr(__FILE__, __LINE__,
88  "An error was reported by the remote httpd; this should have been processed by HTTPConnect..");
89 
90  case dap4_data_ddx: {
91  // Parse the DDX; throw an exception on error.
92  DDXParser ddx_parser(data.get_factory());
93 
94  // Read the MPM boundary and then read the subsequent headers
95  string boundary = read_multipart_boundary(rs->get_stream());
96  DBG(cerr << "MPM Boundary: " << boundary << endl);
97  read_multipart_headers(rs->get_stream(), "text/xml", dap4_ddx);
98 
99  // Parse the DDX, reading up to and including the next boundary.
100  // Return the CID for the matching data part
101  string data_cid;
102  ddx_parser.intern_stream(rs->get_stream(), &data, data_cid, boundary);
103 
104  // Munge the CID into something we can work with
105  data_cid = cid_to_header_value(data_cid);
106  DBG(cerr << "Data CID: " << data_cid << endl);
107 
108  // Read the data part's MPM part headers (boundary was read by
109  // DDXParse::intern)
110  read_multipart_headers(rs->get_stream(), "application/octet-stream", dap4_data, data_cid);
111 
112  // Now read the data
113 #if FILE_UN_MARSHALLER
114  XDRFileUnMarshaller um(rs->get_stream());
115 #else
116  fpistream in ( rs->get_stream() );
117  XDRStreamUnMarshaller um( in );
118 #endif
119  for (DDS::Vars_iter i = data.var_begin(); i != data.var_end(); i++) {
120  (*i)->deserialize(um, &data);
121  }
122  return;
123  }
124 
125  case dods_data:
126  default: {
127  // Parse the DDS; throw an exception on error.
128  data.parse(rs->get_stream());
129 #if FILE_UN_MARSHALLER
130  XDRFileUnMarshaller um(rs->get_stream());
131 #else
132  fpistream in ( rs->get_stream() );
133  XDRStreamUnMarshaller um( in );
134 #endif
135  // Load the DDS with data.
136  for (DDS::Vars_iter i = data.var_begin(); i != data.var_end(); i++) {
137  (*i)->deserialize(um, &data);
138  }
139  return;
140  }
141  }
142 }
143 
146 void
147 Connect::process_data(DDS &data, Response *rs)
148 {
149  DBG(cerr << "Entering Connect::process_data" << endl);
150 
151 #if 0
152  data.set_version(rs->get_version());
153  data.set_protocol(rs->get_protocol());
154 #endif
155  // TODO is this the correct info?
156  data.set_dap_version(rs->get_protocol());
157 
158  DBG(cerr << "Entering process_data: d_stream = " << rs << endl);
159  switch (rs->get_type()) {
160  case dods_error: {
161  Error e;
162  if (!e.parse(rs->get_stream()))
163  throw InternalErr(__FILE__, __LINE__,
164  "Could not parse the Error object returned by the server!");
165  throw e;
166  }
167 
168  case web_error:
169  // Web errors (those reported in the return document's MIME header)
170  // are processed by the WWW library.
171  throw InternalErr(__FILE__, __LINE__, "An error was reported by the remote httpd; this should have been processed by HTTPConnect..");
172 
173  case dap4_data_ddx: {
174  // Parse the DDX; throw an exception on error.
175  DDXParser ddx_parser(data.get_factory());
176 
177  // Read the MPM boundary and then read the subsequent headers
178  string boundary = read_multipart_boundary(rs->get_stream());
179  DBG(cerr << "MPM Boundary: " << boundary << endl);
180  read_multipart_headers(rs->get_stream(), "text/xml", dap4_ddx);
181 
182  // Parse the DDX, reading up to and including the next boundary.
183  // Return the CID for the matching data part
184  string data_cid;
185  ddx_parser.intern_stream(rs->get_stream(), &data, data_cid, boundary);
186 
187  // Munge the CID into something we can work with
188  data_cid = cid_to_header_value(data_cid);
189  DBG(cerr << "Data CID: " << data_cid << endl);
190 
191  // Read the data part's MPM part headers (boundary was read by
192  // DDXParse::intern)
193  read_multipart_headers(rs->get_stream(),
194  "application/octet-stream", dap4_data, data_cid);
195 
196  // Now read the data
197 #if FILE_UN_MARSHALLER
198  XDRFileUnMarshaller um( rs->get_stream() ) ;
199 #else
200  fpistream in ( rs->get_stream() );
201  XDRStreamUnMarshaller um( in ) ;
202 #endif
203 #if 0
204  try {
205 #endif
206  for (DDS::Vars_iter i = data.var_begin(); i != data.var_end();
207  i++) {
208  (*i)->deserialize(um, &data);
209  }
210 #if 0
211  }
212  catch (Error &e) {
213  throw ;
214  }
215 #endif
216  return;
217  }
218 
219  case dods_data:
220  default: {
221  // Parse the DDS; throw an exception on error.
222  data.parse(rs->get_stream());
223 #if FILE_UN_MARSHALLER
224  XDRFileUnMarshaller um( rs->get_stream() ) ;
225 #else
226  fpistream in ( rs->get_stream() );
227  XDRStreamUnMarshaller um( in ) ;
228 #endif
229  // Load the DDS with data.
230 #if 0
231  try {
232 #endif
233  for (DDS::Vars_iter i = data.var_begin(); i != data.var_end();
234  i++) {
235  (*i)->deserialize(um, &data);
236  }
237 #if 0
238  }
239  catch (Error &e) {
240  throw ;
241  }
242 #endif
243  return;
244  }
245  }
246 }
247 
248 // Barely a parser... This is used when reading from local sources of DODS
249 // Data objects. It simulates the important actions of the libwww MIME header
250 // parser. Those actions fill in certain fields in the Connect object. jhrg
251 // 5/20/97
252 //
253 // Make sure that this parser reads from data_source without disturbing the
254 // information in data_source that follows the MIME header. Since the DDS
255 // (which follows the MIME header) is parsed by a flex/bison scanner/parser,
256 // make sure to use I/O calls that will mesh with ANSI C I/O calls. In the
257 // old GNU libg++, the C++ calls were synchronized with the C calls, but that
258 // may no longer be the case. 5/31/99 jhrg
259 
269 void Connect::parse_mime(Response *rs)
270 {
271  rs->set_version("dods/0.0"); // initial value; for backward compatibility.
272  rs->set_protocol("2.0");
273 
274  FILE *data_source = rs->get_stream();
275  string mime = get_next_mime_header(data_source);
276  while (!mime.empty()) {
277  string header, value;
278  parse_mime_header(mime, header, value);
279 
280  // Note that this is an ordered list
281  if (header == "content-description:") {
282  DBG(cout << header << ": " << value << endl);
283  rs->set_type(get_description_type(value));
284  }
285  // Use the value of xdods-server only if no other value has been read
286  else if (header == "xdods-server:" && rs->get_version() == "dods/0.0") {
287  DBG(cout << header << ": " << value << endl);
288  rs->set_version(value);
289  }
290  // This trumps 'xdods-server' and 'server'
291  else if (header == "xopendap-server:") {
292  DBG(cout << header << ": " << value << endl);
293  rs->set_version(value);
294  }
295  else if (header == "xdap:") {
296  DBG(cout << header << ": " << value << endl);
297  rs->set_protocol(value);
298  }
299  // Only look for 'server' if no other header supplies this info.
300  else if (rs->get_version() == "dods/0.0" && header == "server:") {
301  DBG(cout << header << ": " << value << endl);
302  rs->set_version(value);
303  }
304 
305  mime = get_next_mime_header(data_source);
306  }
307 }
308 
309 // public mfuncs
310 
318 Connect::Connect(const string &n, string uname, string password) throw (Error, InternalErr) :
319  d_http(0), d_version("unknown"), d_protocol("2.0")
320 {
321  string name = prune_spaces(n);
322 
323  // Figure out if the URL starts with 'http', if so, make sure that we
324  // talk to an instance of HTTPConnect.
325  if (name.find("http") == 0) {
326  DBG(cerr << "Connect: The identifier is an http URL" << endl);
327  d_http = new HTTPConnect(RCReader::instance());
328 
329  // Find and store any CE given with the URL.
330  string::size_type dotpos = name.find('?');
331  if (dotpos != name.npos) {
332  _URL = name.substr(0, dotpos);
333  string expr = name.substr(dotpos + 1);
334 
335  dotpos = expr.find('&');
336  if (dotpos != expr.npos) {
337  _proj = expr.substr(0, dotpos);
338  _sel = expr.substr(dotpos); // XXX includes '&'
339  }
340  else {
341  _proj = expr;
342  _sel = "";
343  }
344  }
345  else {
346  _URL = name;
347  _proj = "";
348  _sel = "";
349  }
350 
351  _local = false;
352  }
353  else {
354  DBG(cerr << "Connect: The identifier is a local data source." << endl);
355 
356  d_http = 0;
357  _URL = "";
358  _local = true; // local in this case means non-DAP
359  }
360 
361  set_credentials(uname, password);
362 }
363 
365 {
366  DBG2(cerr << "Entering the Connect dtor" << endl);
367 
368  if (d_http)
369  delete d_http;
370  d_http = 0;
371 
372  DBG2(cerr << "Leaving the Connect dtor" << endl);
373 }
374 
383 {
384  string version_url = _URL + ".ver";
385  if (_proj.length() + _sel.length())
386  version_url = version_url + "?" + id2www_ce(_proj + _sel);
387 
388  Response *rs = 0;
389  try {
390  rs = d_http->fetch_url(version_url);
391  }
392  catch (Error &e) {
393  delete rs;
394  rs = 0;
395  throw;
396  }
397 
398  d_version = rs->get_version();
399  d_protocol = rs->get_protocol();
400 
401  delete rs;
402  rs = 0;
403 
404  return d_version;
405 }
406 
419 {
420  string version_url = _URL + ".ver";
421  if (_proj.length() + _sel.length())
422  version_url = version_url + "?" + id2www_ce(_proj + _sel);
423 
424  Response *rs = 0;
425  try {
426  rs = d_http->fetch_url(version_url);
427  }
428  catch (Error &e) {
429  delete rs;
430  rs = 0;
431  throw;
432  }
433 
434  d_version = rs->get_version();
435  d_protocol = rs->get_protocol();
436 
437  delete rs;
438  rs = 0;
439 
440  return d_protocol;
441 }
442 
451 {
452  string das_url = _URL + ".das";
453  if (_proj.length() + _sel.length())
454  das_url = das_url + "?" + id2www_ce(_proj + _sel);
455 
456  Response *rs = 0;
457  try {
458  rs = d_http->fetch_url(das_url);
459  }
460  catch (Error &e) {
461  delete rs;
462  rs = 0;
463  throw;
464  }
465 
466  d_version = rs->get_version();
467  d_protocol = rs->get_protocol();
468 
469  switch (rs->get_type()) {
470  case dods_error: {
471  Error e;
472  if (!e.parse(rs->get_stream())) {
473  delete rs;
474  rs = 0;
475  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
476  }
477  delete rs;
478  rs = 0;
479  throw e;
480  }
481 
482  case web_error:
483  // We should never get here; a web error should be picked up read_url
484  // (called by fetch_url) and result in a thrown Error object.
485  break;
486 
487  case dods_das:
488  default:
489  // DAS::parse throws an exception on error.
490  try {
491  das.parse(rs->get_stream()); // read and parse the das from a file
492  }
493  catch (Error &e) {
494  delete rs;
495  rs = 0;
496  throw;
497  }
498 
499  break;
500  }
501 
502  delete rs;
503  rs = 0;
504 }
505 
517 {
518  string use_url = _URL + "?" + _proj + _sel;
519  Response *rs = 0;
520  try {
521  rs = d_http->fetch_url(use_url);
522  }
523  catch (Error &e) {
524  delete rs;
525  rs = 0;
526  throw;
527  }
528 
529  d_version = rs->get_version();
530  d_protocol = rs->get_protocol();
531 
532  switch (rs->get_type()) {
533  case dods_error: {
534  Error e;
535  if (!e.parse(rs->get_stream())) {
536  delete rs;
537  rs = 0;
538  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
539  }
540  delete rs;
541  rs = 0;
542  throw e;
543  }
544 
545  case web_error:
546  // We should never get here; a web error should be picked up read_url
547  // (called by fetch_url) and result in a thrown Error object.
548  break;
549 
550  case dods_das:
551  default:
552  // DAS::parse throws an exception on error.
553  try {
554  das.parse(rs->get_stream()); // read and parse the das from a file
555  }
556  catch (Error &e) {
557  delete rs;
558  rs = 0;
559  throw;
560  }
561 
562  break;
563  }
564 
565  delete rs;
566  rs = 0;
567 }
568 
582 void Connect::request_dds(DDS &dds, string expr)
583 {
584  string proj, sel;
585  string::size_type dotpos = expr.find('&');
586  if (dotpos != expr.npos) {
587  proj = expr.substr(0, dotpos);
588  sel = expr.substr(dotpos);
589  }
590  else {
591  proj = expr;
592  sel = "";
593  }
594 
595  string dds_url = _URL + ".dds" + "?" + id2www_ce(_proj + proj + _sel + sel);
596 
597  Response *rs = 0;
598  try {
599  rs = d_http->fetch_url(dds_url);
600  }
601  catch (Error &e) {
602  delete rs;
603  rs = 0;
604  throw;
605  }
606 
607  d_version = rs->get_version();
608  d_protocol = rs->get_protocol();
609 
610  switch (rs->get_type()) {
611  case dods_error: {
612  Error e;
613  if (!e.parse(rs->get_stream())) {
614  delete rs;
615  rs = 0;
616  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
617  }
618  delete rs;
619  rs = 0;
620  throw e;
621  }
622 
623  case web_error:
624  // We should never get here; a web error should be picked up read_url
625  // (called by fetch_url) and result in a thrown Error object.
626  break;
627 
628  case dods_dds:
629  default:
630  // DDS::prase throws an exception on error.
631  try {
632  dds.parse(rs->get_stream()); // read and parse the dds from a file
633  }
634  catch (Error &e) {
635  delete rs;
636  rs = 0;
637  throw;
638  }
639  break;
640  }
641 
642  delete rs;
643  rs = 0;
644 }
645 
663 {
664  string use_url = _URL + "?" + _proj + _sel;
665  Response *rs = 0;
666  try {
667  rs = d_http->fetch_url(use_url);
668  }
669  catch (Error &e) {
670  delete rs;
671  rs = 0;
672  throw;
673  }
674 
675  d_version = rs->get_version();
676  d_protocol = rs->get_protocol();
677 
678  switch (rs->get_type()) {
679  case dods_error: {
680  Error e;
681  if (!e.parse(rs->get_stream())) {
682  delete rs;
683  rs = 0;
684  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
685  }
686  delete rs;
687  rs = 0;
688  throw e;
689  }
690 
691  case web_error:
692  // We should never get here; a web error should be picked up read_url
693  // (called by fetch_url) and result in a thrown Error object.
694  break;
695 
696  case dods_dds:
697  default:
698  // DDS::prase throws an exception on error.
699  try {
700  dds.parse(rs->get_stream()); // read and parse the dds from a file
701  }
702  catch (Error &e) {
703  delete rs;
704  rs = 0;
705  throw;
706  }
707  break;
708  }
709 
710  delete rs;
711  rs = 0;
712 }
713 
725 void Connect::request_ddx(DDS &dds, string expr)
726 {
727  string proj, sel;
728  string::size_type dotpos = expr.find('&');
729  if (dotpos != expr.npos) {
730  proj = expr.substr(0, dotpos);
731  sel = expr.substr(dotpos);
732  }
733  else {
734  proj = expr;
735  sel = "";
736  }
737 
738  string ddx_url = _URL + ".ddx" + "?" + id2www_ce(_proj + proj + _sel + sel);
739 
740  Response *rs = 0;
741  try {
742  rs = d_http->fetch_url(ddx_url);
743  }
744  catch (Error &e) {
745  delete rs;
746  rs = 0;
747  throw;
748  }
749 
750  d_version = rs->get_version();
751  d_protocol = rs->get_protocol();
752 
753  switch (rs->get_type()) {
754  case dods_error: {
755  Error e;
756  if (!e.parse(rs->get_stream())) {
757  delete rs;
758  rs = 0;
759  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
760  }
761  delete rs;
762  rs = 0;
763  throw e;
764  }
765 
766  case web_error:
767  // We should never get here; a web error should be picked up read_url
768  // (called by fetch_url) and result in a thrown Error object.
769  break;
770 
771  case dap4_ddx:
772  case dods_ddx:
773  try {
774  string blob;
775 
776  DDXParser ddxp(dds.get_factory());
777  ddxp.intern_stream(rs->get_stream(), &dds, blob);
778  }
779  catch (Error &e) {
780  delete rs;
781  rs = 0;
782  throw;
783  }
784  break;
785 
786  default:
787  delete rs;
788  rs = 0;
789  throw Error(
790  "The site did not return a valid response (it lacked the\n\
791 expected content description header value of 'dap4-ddx' and\n\
792 instead returned '"
793  + long_to_string(rs->get_type())
794  + "').\n\
795 This may indicate that the server at the site is not correctly\n\
796 configured, or that the URL has changed.");
797  }
798 
799  delete rs;
800  rs = 0;
801 }
802 
806 {
807  string use_url = _URL + "?" + _proj + _sel;
808 
809  Response *rs = 0;
810  try {
811  rs = d_http->fetch_url(use_url);
812  }
813  catch (Error &e) {
814  delete rs;
815  rs = 0;
816  throw;
817  }
818 
819  d_version = rs->get_version();
820  d_protocol = rs->get_protocol();
821 
822  switch (rs->get_type()) {
823  case dods_error: {
824  Error e;
825  if (!e.parse(rs->get_stream())) {
826  delete rs;
827  rs = 0;
828  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
829  }
830  delete rs;
831  rs = 0;
832  throw e;
833  }
834 
835  case web_error:
836  // We should never get here; a web error should be picked up read_url
837  // (called by fetch_url) and result in a thrown Error object.
838  break;
839 
840  case dap4_ddx:
841  case dods_ddx:
842  try {
843  string blob;
844 
845  DDXParser ddxp(dds.get_factory());
846  ddxp.intern_stream(rs->get_stream(), &dds, blob);
847  }
848  catch (Error &e) {
849  delete rs;
850  rs = 0;
851  throw;
852  }
853  break;
854 
855  default:
856  delete rs;
857  rs = 0;
858  throw Error(
859  "The site did not return a valid response (it lacked the\n\
860 expected content description header value of 'dap4-ddx' and\n\
861 instead returned '"
862  + long_to_string(rs->get_type())
863  + "').\n\
864 This may indicate that the server at the site is not correctly\n\
865 configured, or that the URL has changed.");
866  }
867 
868  delete rs;
869  rs = 0;
870 }
871 
887 void Connect::request_data(DataDDS &data, string expr)
888 {
889  string proj, sel;
890  string::size_type dotpos = expr.find('&');
891  if (dotpos != expr.npos) {
892  proj = expr.substr(0, dotpos);
893  sel = expr.substr(dotpos);
894  }
895  else {
896  proj = expr;
897  sel = "";
898  }
899 
900  string data_url = _URL + ".dods?" + id2www_ce(_proj + proj + _sel + sel);
901 
902  Response *rs = 0;
903  // We need to catch Error exceptions to ensure calling close_output.
904  try {
905  rs = d_http->fetch_url(data_url);
906 
907  d_version = rs->get_version();
908  d_protocol = rs->get_protocol();
909 
910  process_data(data, rs);
911  delete rs;
912  rs = 0;
913  }
914  catch (Error &e) {
915  delete rs;
916  rs = 0;
917  throw;
918  }
919 }
920 
939 {
940  string use_url = _URL + "?" + _proj + _sel;
941  Response *rs = 0;
942  // We need to catch Error exceptions to ensure calling close_output.
943  try {
944  rs = d_http->fetch_url(use_url);
945 
946  d_version = rs->get_version();
947  d_protocol = rs->get_protocol();
948 
949  process_data(data, rs);
950  delete rs;
951  rs = 0;
952  }
953  catch (Error &e) {
954  delete rs;
955  rs = 0;
956  throw;
957  }
958 }
959 
960 void Connect::request_data_ddx(DataDDS &data, string expr)
961 {
962  string proj, sel;
963  string::size_type dotpos = expr.find('&');
964  if (dotpos != expr.npos) {
965  proj = expr.substr(0, dotpos);
966  sel = expr.substr(dotpos);
967  }
968  else {
969  proj = expr;
970  sel = "";
971  }
972 
973  string data_url = _URL + ".dap?" + id2www_ce(_proj + proj + _sel + sel);
974 
975  Response *rs = 0;
976  // We need to catch Error exceptions to ensure calling close_output.
977  try {
978  rs = d_http->fetch_url(data_url);
979 
980  d_version = rs->get_version();
981  d_protocol = rs->get_protocol();
982 
983  process_data(data, rs);
984  delete rs;
985  rs = 0;
986  }
987  catch (Error &e) {
988  delete rs;
989  rs = 0;
990  throw;
991  }
992 }
993 
995 {
996  string use_url = _URL + "?" + _proj + _sel;
997  Response *rs = 0;
998  // We need to catch Error exceptions to ensure calling close_output.
999  try {
1000  rs = d_http->fetch_url(use_url);
1001 
1002  d_version = rs->get_version();
1003  d_protocol = rs->get_protocol();
1004 
1005  process_data(data, rs);
1006  delete rs;
1007  rs = 0;
1008  }
1009  catch (Error &e) {
1010  delete rs;
1011  rs = 0;
1012  throw;
1013  }
1014 }
1015 
1030 {
1031  if (!rs)
1032  throw InternalErr(__FILE__, __LINE__, "Response object is null.");
1033 
1034  // Read from data_source and parse the MIME headers specific to DAP2/4.
1035  parse_mime(rs);
1036 
1037  read_data_no_mime(data, rs);
1038 }
1039 void
1041 {
1042  if (!rs)
1043  throw InternalErr(__FILE__, __LINE__, "Response object is null.");
1044 
1045  // Read from data_source and parse the MIME headers specific to DAP2/4.
1046  parse_mime(rs);
1047 
1048  read_data_no_mime(data, rs);
1049 }
1050 
1051 // This function looks at the input stream and makes its best guess at what
1052 // lies in store for downstream processing code. Definitely heuristic.
1053 // Assumptions:
1054 // #1 The current file position is past any MIME headers (if they were present).
1055 // #2 We must reset the FILE* position to the start of the DDS or DDX headers
1056 static void divine_type_information(Response *rs)
1057 {
1058  // Consume whitespace
1059  char c = getc(rs->get_stream());
1060  while (isspace(c)) {
1061  c = getc(rs->get_stream());
1062  }
1063 
1064  // The heuristic here is that a DataDDX is a multipart MIME document and
1065  // The first non space character found after the headers is the start of
1066  // the first part which looks like '--<boundary>' while a DataDDS starts
1067  // with a DDS (;Dataset {' ...). I take into account that our parsers have
1068  // accepted both 'Dataset' and 'dataset' for a long time.
1069  switch (c) {
1070  case '-':
1071  rs->set_type(dap4_data_ddx);
1072  break;
1073  case 'D':
1074  case 'd':
1075  rs->set_type(dods_data);
1076  break;
1077  default:
1078  throw InternalErr(__FILE__, __LINE__, "Could not determine type of response object in stream.");
1079  }
1080 
1081  ungetc(c, rs->get_stream());
1082 }
1083 
1097 {
1098  if (rs->get_type() == unknown_type)
1099  divine_type_information(rs);
1100 
1101  switch (rs->get_type()) {
1102  case dods_data:
1103  d_version = rs->get_version();
1104  d_protocol = rs->get_protocol();
1105  process_data(data, rs);
1106  break;
1107  case dap4_data_ddx:
1108  process_data(data, rs);
1109  d_version = rs->get_version();
1110  d_protocol = data.get_protocol();
1111  break;
1112  default:
1113  throw InternalErr(__FILE__, __LINE__, "Should have been a DataDDS or DataDDX.");
1114  }
1115 }
1117 {
1118  if (rs->get_type() == unknown_type)
1119  divine_type_information(rs);
1120 
1121  switch (rs->get_type()) {
1122  case dods_data:
1123  d_version = rs->get_version();
1124  d_protocol = rs->get_protocol();
1125  process_data(data, rs);
1126  break;
1127  case dap4_data_ddx:
1128  process_data(data, rs);
1129  d_version = rs->get_version();
1130  // TODO should check to see if this hack is a correct replacement
1131  // for get_protocol from DataDDS
1132  d_protocol = data.get_dap_version();
1133  break;
1134  default:
1135  throw InternalErr(__FILE__, __LINE__, "Should have been a DataDDS or DataDDX.");
1136  }
1137 }
1138 
1139 bool
1141 {
1142  return _local;
1143 }
1144 
1161 string Connect::URL(bool ce)
1162 {
1163  if (_local)
1164  throw InternalErr(__FILE__, __LINE__, "URL(): This call is only valid for a DAP data source.");
1165 
1166  if (ce)
1167  return _URL + "?" + _proj + _sel;
1168  else
1169  return _URL;
1170 }
1171 
1180 string Connect::CE()
1181 {
1182  if (_local)
1183  throw InternalErr(__FILE__, __LINE__, "CE(): This call is only valid for a DAP data source.");
1184 
1185  return _proj + _sel;
1186 }
1187 
1193 void Connect::set_credentials(string u, string p)
1194 {
1195  if (d_http)
1196  d_http->set_credentials(u, p);
1197 }
1198 
1203 {
1204  if (d_http)
1205  d_http->set_accept_deflate(deflate);
1206 }
1207 
1213 void Connect::set_xdap_protocol(int major, int minor)
1214 {
1215  if (d_http)
1216  d_http->set_xdap_protocol(major, minor);
1217 }
1218 
1223 {
1224  if (d_http)
1225  d_http->set_cache_enabled(cache);
1226 }
1227 
1229 {
1230  bool status;
1231  DBG(cerr << "Entering is_cache_enabled (" << hex << d_http << dec
1232  << ")... ");
1233  if (d_http)
1234  status = d_http->is_cache_enabled();
1235  else
1236  status = false;
1237  DBGN(cerr << "exiting" << endl);
1238  return status;
1239 }
1240 
1241 } // namespace libdap
virtual string CE()
Get the Connect's constraint expression.
Definition: Connect.cc:1180
virtual void request_das_url(DAS &das)
Get the DAS from a server.
Definition: Connect.cc:516
void intern_stream(FILE *in, DDS *dds, string &cid, const string &boundary="")
Read the DDX from a stream instead of a file.
virtual void request_ddx(DDS &dds, string expr="")
Get the DDX from a server.
Definition: Connect.cc:725
virtual string URL(bool CE=true)
Get the object's URL.
Definition: Connect.cc:1161
string id2www_ce(string in, const string &allowable)
Definition: escaping.cc:176
string prune_spaces(const string &name)
Definition: util.cc:315
void set_credentials(const string &u, const string &p)
Definition: HTTPConnect.cc:993
void set_xdap_protocol(int major, int minor)
Definition: Connect.cc:1213
void read_multipart_headers(FILE *in, const string &content_type, const ObjectType object_type, const string &cid)
Definition: mime_util.cc:992
#define DBGN(x)
Definition: debug.h:59
virtual void set_type(ObjectType o)
Definition: Response.h:143
virtual void request_dds_url(DDS &dds)
Get the DDS from a server.
Definition: Connect.cc:662
bool parse(FILE *fp)
Parse an Error object.
Definition: Error.cc:159
void set_cache_enabled(bool enabled)
Definition: HTTPConnect.h:159
string cid_to_header_value(const string &cid)
Definition: mime_util.cc:1064
virtual ObjectType get_type() const
Definition: Response.h:119
HTTPResponse * fetch_url(const string &url)
Definition: HTTPConnect.cc:569
#define DBG2(x)
Definition: debug.h:73
bool is_local()
Definition: Connect.cc:1140
A class for software fault reporting.
Definition: InternalErr.h:64
void parse(string fname)
Parse a DDS from a file with the given d_name.
Definition: DDS.cc:897
void parse_mime_header(const string &header, string &name, string &value)
Definition: mime_util.cc:899
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:1029
virtual string get_protocol() const
Definition: Response.h:127
#define DBG(x)
Definition: debug.h:58
string get_dap_version() const
Definition: DDS.h:268
ObjectType get_description_type(const string &value)
Definition: mime_util.cc:340
void set_cache_enabled(bool enabled)
Definition: Connect.cc:1222
bool is_cache_enabled()
Definition: Connect.cc:1228
std::vector< BaseType * >::iterator Vars_iter
Definition: DDS.h:219
virtual void request_dds(DDS &dds, string expr="")
Get the DDS from a server.
Definition: Connect.cc:582
string get_next_mime_header(FILE *in)
Definition: mime_util.cc:837
void set_accept_deflate(bool deflate)
Definition: Connect.cc:1202
virtual void request_data(DataDDS &data, string expr="")
Get the DAS from a server.
Definition: Connect.cc:887
string read_multipart_boundary(FILE *in, const string &boundary)
Definition: mime_util.cc:944
virtual void read_data_no_mime(DataDDS &data, Response *rs)
Read data from a file which does not have response MIME headers. This method is a companion to read_d...
Definition: Connect.cc:1096
virtual void request_das(DAS &das)
Get the DAS from a server.
Definition: Connect.cc:450
void set_accept_deflate(bool defalte)
Definition: HTTPConnect.cc:919
string long_to_string(long val, int base)
Definition: util.cc:773
BaseTypeFactory * get_factory() const
Definition: DDS.h:239
void set_xdap_protocol(int major, int minor)
Definition: HTTPConnect.cc:956
virtual void parse(string fname)
Reads a DAS from the named file.
Definition: DAS.cc:249
virtual string request_version()
Definition: Connect.cc:382
virtual void request_data_ddx_url(DataDDS &data)
Definition: Connect.cc:994
virtual void request_data_ddx(DataDDS &data, string expr="")
Definition: Connect.cc:960
void set_credentials(string u, string p)
Set the credentials for responding to challenges while dereferencing URLs.
Definition: Connect.cc:1193
virtual string request_protocol()
Definition: Connect.cc:418
string get_protocol() const
Definition: DataDDS.h:129
Hold attribute data for a DAP2 dataset.
Definition: DAS.h:121
virtual void request_data_url(DataDDS &data)
Get the DAS from a server.
Definition: Connect.cc:938
A class for error processing.
Definition: Error.h:90
virtual string get_version() const
Definition: Response.h:123
Holds a DAP2 DDS.
Definition: DataDDS.h:77
virtual void request_ddx_url(DDS &dds)
The 'url' version of request_ddx.
Definition: Connect.cc:805
virtual ~Connect()
Definition: Connect.cc:364
static RCReader * instance()
Definition: RCReader.cc:486
virtual FILE * get_stream() const
Definition: Response.h:115