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