libdap  Updated for version 3.20.6
libdap4 is an implementation of OPeNDAP's DAP protocol.
Connect.cc
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 <cerrno>
42 
43 #include <fstream>
44 #include <algorithm>
45 
46 #include "debug.h"
47 #include "DataDDS.h"
48 #include "Connect.h"
49 #include "escaping.h"
50 //#include "RCReader.h"
51 #include "DDXParserSAX2.h"
52 #if FILE_UN_MARSHALLER
53 #include "XDRFileUnMarshaller.h"
54 #else
55 #include "fdiostream.h"
56 #include "XDRStreamUnMarshaller.h"
57 #endif
58 #include "mime_util.h"
59 
60 using std::cerr;
61 using std::endl;
62 using std::ifstream;
63 using std::ofstream;
64 using std::min;
65 
66 namespace libdap {
67 
70 void Connect::process_data(DataDDS &data, Response *rs)
71 {
72  DBG(cerr << "Entering Connect::process_data" << endl);
73 
74  data.set_version(rs->get_version());
75  data.set_protocol(rs->get_protocol());
76 
77  DBG(cerr << "Entering process_data: d_stream = " << rs << endl);
78  switch (rs->get_type()) {
79  case dods_error: {
80  Error e;
81  if (!e.parse(rs->get_stream()))
82  throw InternalErr(__FILE__, __LINE__, "Could not parse the Error object returned by the server!");
83  throw e;
84  }
85 
86  case web_error:
87  // Web errors (those reported in the return document's MIME header)
88  // are processed by the WWW library.
89  throw InternalErr(__FILE__, __LINE__,
90  "An error was reported by the remote httpd; this should have been processed by HTTPConnect..");
91 
92 #if 0
93  // This code triggers a security warning from Coverity; since it is not used,
94  // I have removed it. jhrg 5/5/16
95  case dods_data_ddx: {
96  // Parse the DDX; throw an exception on error.
97  DDXParser ddx_parser(data.get_factory());
98 
99  // Read the MPM boundary and then read the subsequent headers
100  string boundary = read_multipart_boundary(rs->get_stream());
101  DBG(cerr << "MPM Boundary: " << boundary << endl);
102  read_multipart_headers(rs->get_stream(), "text/xml", dods_ddx);
103 
104  // Parse the DDX, reading up to and including the next boundary.
105  // Return the CID for the matching data part
106  string data_cid;
107  ddx_parser.intern_stream(rs->get_stream(), &data, data_cid, boundary);
108 
109  // Munge the CID into something we can work with
110  data_cid = cid_to_header_value(data_cid);
111  DBG(cerr << "Data CID: " << data_cid << endl);
112 
113  // Read the data part's MPM part headers (boundary was read by
114  // DDXParse::intern)
115  read_multipart_headers(rs->get_stream(), "application/octet-stream", dap4_data, data_cid);
116 
117  // Now read the data
118 #if FILE_UN_MARSHALLER
119  XDRFileUnMarshaller um(rs->get_stream());
120 #else
121  fpistream in ( rs->get_stream() );
122  XDRStreamUnMarshaller um( in );
123 #endif
124  for (DDS::Vars_iter i = data.var_begin(); i != data.var_end(); i++) {
125  (*i)->deserialize(um, &data);
126  }
127  return;
128  }
129 #endif
130 
131  case dods_data:
132  default: {
133  // Parse the DDS; throw an exception on error.
134  data.parse(rs->get_stream());
135 #if FILE_UN_MARSHALLER
136  XDRFileUnMarshaller um(rs->get_stream());
137 #else
138  fpistream in ( rs->get_stream() );
139  XDRStreamUnMarshaller um( in );
140 #endif
141  // Load the DDS with data.
142  for (DDS::Vars_iter i = data.var_begin(); i != data.var_end(); i++) {
143  (*i)->deserialize(um, &data);
144  }
145  return;
146  }
147  }
148 }
149 
152 void Connect::process_data(DDS &data, Response *rs)
153 {
154  DBG(cerr << "Entering Connect::process_data" << endl);
155 
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__, "Could not parse the Error object returned by the server!");
164  throw e;
165  }
166 
167  case web_error:
168  // Web errors (those reported in the return document's MIME header)
169  // are processed by the WWW library.
170  throw InternalErr(__FILE__, __LINE__,
171  "An error was reported by the remote web server; this should have been processed by HTTPConnect.");
172 
173 #if 0
174  // FIXME: The following case is never used. There is no such response. jhrg 10/20/15
175  // This code triggers a security warning from Coverity; since it is not used,
176  // I have removed it. jhrg 5/5/16
177  case dods_data_ddx: {
178  // Parse the DDX; throw an exception on error.
179  DDXParser ddx_parser(data.get_factory());
180 
181  // Read the MPM boundary and then read the subsequent headers
182  string boundary = read_multipart_boundary(rs->get_stream());
183  DBG(cerr << "MPM Boundary: " << boundary << endl);
184  read_multipart_headers(rs->get_stream(), "text/xml", dods_ddx);
185 
186  // Parse the DDX, reading up to and including the next boundary.
187  // Return the CID for the matching data part
188  string data_cid;
189  ddx_parser.intern_stream(rs->get_stream(), &data, data_cid, boundary);
190 
191  // Munge the CID into something we can work with
192  data_cid = cid_to_header_value(data_cid);
193  DBG(cerr << "Data CID: " << data_cid << endl);
194 
195  // Read the data part's MPM part headers (boundary was read by
196  // DDXParse::intern)
197  read_multipart_headers(rs->get_stream(), "application/octet-stream", dap4_data, data_cid);
198 
199  // Now read the data
200  XDRFileUnMarshaller um(rs->get_stream());
201  for (DDS::Vars_iter i = data.var_begin(); i != data.var_end(); i++) {
202  (*i)->deserialize(um, &data);
203  }
204  return;
205  }
206 #endif
207 
208  case dods_data:
209  default: {
210  // Parse the DDS; throw an exception on error.
211  data.parse(rs->get_stream());
212 
213  XDRFileUnMarshaller um(rs->get_stream());
214 
215  // Load the DDS with data.
216  for (DDS::Vars_iter i = data.var_begin(); i != data.var_end(); i++) {
217  (*i)->deserialize(um, &data);
218  }
219 
220  return;
221  }
222  }
223 }
224 
225 // Barely a parser... This is used when reading from local sources of DODS
226 // Data objects. It simulates the important actions of the libwww MIME header
227 // parser. Those actions fill in certain fields in the Connect object. jhrg
228 // 5/20/97
229 //
230 // Make sure that this parser reads from data_source without disturbing the
231 // information in data_source that follows the MIME header. Since the DDS
232 // (which follows the MIME header) is parsed by a flex/bison scanner/parser,
233 // make sure to use I/O calls that will mesh with ANSI C I/O calls. In the
234 // old GNU libg++, the C++ calls were synchronized with the C calls, but that
235 // may no longer be the case. 5/31/99 jhrg
236 
249 void Connect::parse_mime(Response *rs)
250 {
251  rs->set_version("dods/0.0"); // initial value; for backward compatibility.
252  rs->set_protocol("2.0");
253 
254  FILE *data_source = rs->get_stream();
255  string mime = get_next_mime_header(data_source);
256  while (!mime.empty()) {
257  string header, value;
258  parse_mime_header(mime, header, value);
259 
260  // Note that this is an ordered list
261  if (header == "content-description:") {
262  DBG(cout << header << ": " << value << endl);
263  rs->set_type(get_description_type(value));
264  }
265  // Use the value of xdods-server only if no other value has been read
266  else if (header == "xdods-server:" && rs->get_version() == "dods/0.0") {
267  DBG(cout << header << ": " << value << endl);
268  rs->set_version(value);
269  }
270  // This trumps 'xdods-server' and 'server'
271  else if (header == "xopendap-server:") {
272  DBG(cout << header << ": " << value << endl);
273  rs->set_version(value);
274  }
275  else if (header == "xdap:") {
276  DBG(cout << header << ": " << value << endl);
277  rs->set_protocol(value);
278  }
279  // Only look for 'server' if no other header supplies this info.
280  else if (rs->get_version() == "dods/0.0" && header == "server:") {
281  DBG(cout << header << ": " << value << endl);
282  rs->set_version(value);
283  }
284 
285  mime = get_next_mime_header(data_source);
286  }
287 }
288 
289 // public mfuncs
290 
298 Connect::Connect(const string &n, string uname, string password) :
299  d_http(0), d_version("unknown"), d_protocol("2.0")
300 {
301  string name = prune_spaces(n);
302 
303  // Figure out if the URL starts with 'http', if so, make sure that we
304  // talk to an instance of HTTPConnect.
305  if (name.find("http") == 0) {
306  DBG(cerr << "Connect: The identifier is an http URL" << endl);
307  d_http = new HTTPConnect(RCReader::instance());
308 
309  // Find and store any CE given with the URL.
310  string::size_type dotpos = name.find('?');
311  if (dotpos != name.npos) {
312  _URL = name.substr(0, dotpos);
313  string expr = name.substr(dotpos + 1);
314 
315  dotpos = expr.find('&');
316  if (dotpos != expr.npos) {
317  _proj = expr.substr(0, dotpos);
318  _sel = expr.substr(dotpos); // XXX includes '&'
319  }
320  else {
321  _proj = expr;
322  _sel = "";
323  }
324  }
325  else {
326  _URL = name;
327  _proj = "";
328  _sel = "";
329  }
330 
331  _local = false;
332  }
333  else {
334  DBG(cerr << "Connect: The identifier is a local data source." << endl);
335 
336  d_http = 0;
337  _URL = "";
338  _local = true; // local in this case means non-DAP
339  }
340 
341  set_credentials(uname, password);
342 }
343 
344 Connect::~Connect()
345 {
346  DBG2(cerr << "Entering the Connect dtor" << endl);
347 
348  if (d_http)
349  delete d_http;
350  d_http = 0;
351 
352  DBG2(cerr << "Leaving the Connect dtor" << endl);
353 }
354 
363 {
364  string version_url = _URL + ".ver";
365  if (_proj.length() + _sel.length())
366  version_url = version_url + "?" + id2www_ce(_proj + _sel);
367 
368  Response *rs = 0;
369  try {
370  rs = d_http->fetch_url(version_url);
371  }
372  catch (Error &e) {
373  delete rs;
374  rs = 0;
375  throw;
376  }
377 
378  d_version = rs->get_version();
379  d_protocol = rs->get_protocol();
380 
381  delete rs;
382  rs = 0;
383 
384  return d_version;
385 }
386 
399 {
400  string version_url = _URL + ".ver";
401  if (_proj.length() + _sel.length())
402  version_url = version_url + "?" + id2www_ce(_proj + _sel);
403 
404  Response *rs = 0;
405  try {
406  rs = d_http->fetch_url(version_url);
407  }
408  catch (Error &e) {
409  delete rs;
410  rs = 0;
411  throw;
412  }
413 
414  d_version = rs->get_version();
415  d_protocol = rs->get_protocol();
416 
417  delete rs;
418  rs = 0;
419 
420  return d_protocol;
421 }
422 
431 {
432  string das_url = _URL + ".das";
433  if (_proj.length() + _sel.length())
434  das_url = das_url + "?" + id2www_ce(_proj + _sel);
435 
436  Response *rs = 0;
437  try {
438  rs = d_http->fetch_url(das_url);
439  }
440  catch (Error &e) {
441  delete rs;
442  rs = 0;
443  throw;
444  }
445 
446  d_version = rs->get_version();
447  d_protocol = rs->get_protocol();
448 
449  switch (rs->get_type()) {
450  case dods_error: {
451  Error e;
452  if (!e.parse(rs->get_stream())) {
453  delete rs;
454  rs = 0;
455  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
456  }
457  delete rs;
458  rs = 0;
459  throw e;
460  }
461 
462  case web_error:
463  // We should never get here; a web error should be picked up read_url
464  // (called by fetch_url) and result in a thrown Error object.
465  break;
466 
467  case dods_das:
468  default:
469  // DAS::parse throws an exception on error.
470  try {
471  das.parse(rs->get_stream()); // read and parse the das from a file
472  }
473  catch (Error &e) {
474  delete rs;
475  rs = 0;
476  throw;
477  }
478 
479  break;
480  }
481 
482  delete rs;
483  rs = 0;
484 }
485 
497 {
498  string use_url = _URL + "?" + _proj + _sel;
499  Response *rs = 0;
500  try {
501  rs = d_http->fetch_url(use_url);
502  }
503  catch (Error &e) {
504  delete rs;
505  rs = 0;
506  throw;
507  }
508 
509  d_version = rs->get_version();
510  d_protocol = rs->get_protocol();
511 
512  switch (rs->get_type()) {
513  case dods_error: {
514  Error e;
515  if (!e.parse(rs->get_stream())) {
516  delete rs;
517  rs = 0;
518  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
519  }
520  delete rs;
521  rs = 0;
522  throw e;
523  }
524 
525  case web_error:
526  // We should never get here; a web error should be picked up read_url
527  // (called by fetch_url) and result in a thrown Error object.
528  break;
529 
530  case dods_das:
531  default:
532  // DAS::parse throws an exception on error.
533  try {
534  das.parse(rs->get_stream()); // read and parse the das from a file
535  }
536  catch (Error &e) {
537  delete rs;
538  rs = 0;
539  throw;
540  }
541 
542  break;
543  }
544 
545  delete rs;
546  rs = 0;
547 }
548 
562 void Connect::request_dds(DDS &dds, string expr)
563 {
564  string proj, sel;
565  string::size_type dotpos = expr.find('&');
566  if (dotpos != expr.npos) {
567  proj = expr.substr(0, dotpos);
568  sel = expr.substr(dotpos);
569  }
570  else {
571  proj = expr;
572  sel = "";
573  }
574 
575  string dds_url = _URL + ".dds" + "?" + id2www_ce(_proj + proj + _sel + sel);
576 
577  Response *rs = 0;
578  try {
579  rs = d_http->fetch_url(dds_url);
580  }
581  catch (Error &e) {
582  delete rs;
583  rs = 0;
584  throw;
585  }
586 
587  d_version = rs->get_version();
588  d_protocol = rs->get_protocol();
589 
590  switch (rs->get_type()) {
591  case dods_error: {
592  Error e;
593  if (!e.parse(rs->get_stream())) {
594  delete rs;
595  rs = 0;
596  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
597  }
598  delete rs;
599  rs = 0;
600  throw e;
601  }
602 
603  case web_error:
604  // We should never get here; a web error should be picked up read_url
605  // (called by fetch_url) and result in a thrown Error object.
606  break;
607 
608  case dods_dds:
609  default:
610  // DDS::prase throws an exception on error.
611  try {
612  dds.parse(rs->get_stream()); // read and parse the dds from a file
613  }
614  catch (Error &e) {
615  delete rs;
616  rs = 0;
617  throw;
618  }
619  break;
620  }
621 
622  delete rs;
623  rs = 0;
624 }
625 
643 {
644  string use_url = _URL + "?" + _proj + _sel;
645  Response *rs = 0;
646  try {
647  rs = d_http->fetch_url(use_url);
648  }
649  catch (Error &e) {
650  delete rs;
651  rs = 0;
652  throw;
653  }
654 
655  d_version = rs->get_version();
656  d_protocol = rs->get_protocol();
657 
658  switch (rs->get_type()) {
659  case dods_error: {
660  Error e;
661  if (!e.parse(rs->get_stream())) {
662  delete rs;
663  rs = 0;
664  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
665  }
666  delete rs;
667  rs = 0;
668  throw e;
669  }
670 
671  case web_error:
672  // We should never get here; a web error should be picked up read_url
673  // (called by fetch_url) and result in a thrown Error object.
674  break;
675 
676  case dods_dds:
677  default:
678  // DDS::prase throws an exception on error.
679  try {
680  dds.parse(rs->get_stream()); // read and parse the dds from a file
681  }
682  catch (Error &e) {
683  delete rs;
684  rs = 0;
685  throw;
686  }
687  break;
688  }
689 
690  delete rs;
691  rs = 0;
692 }
693 
705 void Connect::request_ddx(DDS &dds, string expr)
706 {
707  string proj, sel;
708  string::size_type dotpos = expr.find('&');
709  if (dotpos != expr.npos) {
710  proj = expr.substr(0, dotpos);
711  sel = expr.substr(dotpos);
712  }
713  else {
714  proj = expr;
715  sel = "";
716  }
717 
718  string ddx_url = _URL + ".ddx" + "?" + id2www_ce(_proj + proj + _sel + sel);
719 
720  Response *rs = 0;
721  try {
722  rs = d_http->fetch_url(ddx_url);
723  }
724  catch (Error &e) {
725  delete rs;
726  throw;
727  }
728 
729  d_version = rs->get_version();
730  d_protocol = rs->get_protocol();
731 
732  switch (rs->get_type()) {
733  case dods_error: {
734  Error e;
735  if (!e.parse(rs->get_stream())) {
736  delete rs;
737  rs = 0;
738  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
739  }
740  delete rs;
741  throw e;
742  }
743 
744  case web_error:
745  // We should never get here; a web error should be picked up read_url
746  // (called by fetch_url) and result in a thrown Error object.
747  break;
748 
749  case dods_ddx:
750  try {
751  string blob;
752 
753  DDXParser ddxp(dds.get_factory());
754  ddxp.intern_stream(rs->get_stream(), &dds, blob);
755  }
756  catch (Error &e) {
757  delete rs;
758  throw;
759  }
760  break;
761 
762  default:
763  ObjectType ot = rs->get_type();
764  delete rs;
765  throw Error("Invalid response type when requesting a DDX response. Response type: " + long_to_string(ot));
766  }
767 
768  delete rs;
769 }
770 
774 {
775  string use_url = _URL + "?" + _proj + _sel;
776 
777  Response *rs = 0;
778  try {
779  rs = d_http->fetch_url(use_url);
780  }
781  catch (Error &e) {
782  delete rs;
783  throw;
784  }
785 
786  d_version = rs->get_version();
787  d_protocol = rs->get_protocol();
788 
789  switch (rs->get_type()) {
790  case dods_error: {
791  Error e;
792  if (!e.parse(rs->get_stream())) {
793  delete rs;
794  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
795  }
796  delete rs;
797  throw e;
798  }
799 
800  case web_error:
801  // We should never get here; a web error should be picked up read_url
802  // (called by fetch_url) and result in a thrown Error object.
803  delete rs;
804  throw InternalErr(__FILE__, __LINE__, "Web error.");
805 
806  case dods_ddx:
807  try {
808  string blob;
809 
810  DDXParser ddxp(dds.get_factory());
811  ddxp.intern_stream(rs->get_stream(), &dds, blob);
812  }
813  catch (Error &e) {
814  delete rs;
815  throw;
816  }
817  break;
818 
819  default: {
820  ObjectType ot = rs->get_type();
821  delete rs;
822 
823  throw Error("Invalid response type when requesting a DDX response. Response type: " + long_to_string(ot));
824  }
825  }
826 
827  delete rs;
828 }
829 
845 void Connect::request_data(DataDDS &data, string expr)
846 {
847  string proj, sel;
848  string::size_type dotpos = expr.find('&');
849  if (dotpos != expr.npos) {
850  proj = expr.substr(0, dotpos);
851  sel = expr.substr(dotpos);
852  }
853  else {
854  proj = expr;
855  sel = "";
856  }
857 
858  string data_url = _URL + ".dods?" + id2www_ce(_proj + proj + _sel + sel);
859 
860  Response *rs = 0;
861  // We need to catch Error exceptions to ensure calling close_output.
862  try {
863  rs = d_http->fetch_url(data_url);
864 
865  d_version = rs->get_version();
866  d_protocol = rs->get_protocol();
867 
868  process_data(data, rs);
869  delete rs;
870  rs = 0;
871  }
872  catch (Error &e) {
873  delete rs;
874  rs = 0;
875  throw;
876  }
877 }
878 
897 {
898  string use_url = _URL + "?" + _proj + _sel;
899  Response *rs = 0;
900  // We need to catch Error exceptions to ensure calling close_output.
901  try {
902  rs = d_http->fetch_url(use_url);
903 
904  d_version = rs->get_version();
905  d_protocol = rs->get_protocol();
906 
907  process_data(data, rs);
908  delete rs;
909  rs = 0;
910  }
911  catch (Error &e) {
912  delete rs;
913  rs = 0;
914  throw;
915  }
916 }
917 
918 // FIXME Unused?
919 void Connect::request_data_ddx(DataDDS &data, string expr)
920 {
921  string proj, sel;
922  string::size_type dotpos = expr.find('&');
923  if (dotpos != expr.npos) {
924  proj = expr.substr(0, dotpos);
925  sel = expr.substr(dotpos);
926  }
927  else {
928  proj = expr;
929  sel = "";
930  }
931 
932  string data_url = _URL + ".dap?" + id2www_ce(_proj + proj + _sel + sel);
933 
934  Response *rs = 0;
935  // We need to catch Error exceptions to ensure calling close_output.
936  try {
937  rs = d_http->fetch_url(data_url);
938 
939  d_version = rs->get_version();
940  d_protocol = rs->get_protocol();
941 
942  process_data(data, rs);
943  delete rs;
944  rs = 0;
945  }
946  catch (Error &e) {
947  delete rs;
948  rs = 0;
949  throw;
950  }
951 }
952 
953 // FIXME Unused?
954 void Connect::request_data_ddx_url(DataDDS &data)
955 {
956  string use_url = _URL + "?" + _proj + _sel;
957  Response *rs = 0;
958  // We need to catch Error exceptions to ensure calling close_output.
959  try {
960  rs = d_http->fetch_url(use_url);
961 
962  d_version = rs->get_version();
963  d_protocol = rs->get_protocol();
964 
965  process_data(data, rs);
966  delete rs;
967  rs = 0;
968  }
969  catch (Error &e) {
970  delete rs;
971  rs = 0;
972  throw;
973  }
974 }
975 
990 {
991  if (!rs)
992  throw InternalErr(__FILE__, __LINE__, "Response object is null.");
993 
994  // Read from data_source and parse the MIME headers specific to DAP2/4.
995  parse_mime(rs);
996 
997  read_data_no_mime(data, rs);
998 }
999 void
1000 Connect::read_data(DDS &data, Response *rs)
1001 {
1002  if (!rs)
1003  throw InternalErr(__FILE__, __LINE__, "Response object is null.");
1004 
1005  // Read from data_source and parse the MIME headers specific to DAP2/4.
1006  parse_mime(rs);
1007 
1008  read_data_no_mime(data, rs);
1009 }
1010 
1011 // This function looks at the input stream and makes its best guess at what
1012 // lies in store for downstream processing code. Definitely heuristic.
1013 // Assumptions:
1014 // #1 The current file position is past any MIME headers (if they were present).
1015 // #2 We must reset the FILE* position to the start of the DDS or DDX headers
1016 static void divine_type_information(Response *rs)
1017 {
1018  // Consume whitespace
1019  int c = getc(rs->get_stream());
1020  while (!feof(rs->get_stream()) && !ferror(rs->get_stream()) && isspace(c)) {
1021  c = getc(rs->get_stream());
1022  }
1023 
1024 
1025  if (ferror(rs->get_stream()))
1026  throw Error("Error reading response type information: " + string(strerror(errno)));
1027  if (feof(rs->get_stream()))
1028  throw Error("Error reading response type information: Found EOF");
1029 
1030  // The heuristic here is that a DataDDX is a multipart MIME document and
1031  // The first non space character found after the headers is the start of
1032  // the first part which looks like '--<boundary>' while a DataDDS starts
1033  // with a DDS (;Dataset {' ...). I take into account that our parsers have
1034  // accepted both 'Dataset' and 'dataset' for a long time.
1035  switch (c) {
1036  case '-':
1037  rs->set_type(dods_data_ddx);
1038  break;
1039  case 'D':
1040  case 'd':
1041  rs->set_type(dods_data);
1042  break;
1043  default:
1044  throw InternalErr(__FILE__, __LINE__, "Could not determine type of response object in stream.");
1045  }
1046 
1047  ungetc(c, rs->get_stream());
1048 }
1049 
1063 {
1064  if (rs->get_type() == unknown_type)
1065  divine_type_information(rs);
1066 
1067  switch (rs->get_type()) {
1068  case dods_data:
1069  d_version = rs->get_version();
1070  d_protocol = rs->get_protocol();
1071  process_data(data, rs);
1072  break;
1073  case dods_data_ddx:
1074  process_data(data, rs);
1075  d_version = rs->get_version();
1076  d_protocol = data.get_protocol();
1077  break;
1078  default:
1079  throw InternalErr(__FILE__, __LINE__, "Should have been a DataDDS or DataDDX.");
1080  }
1081 }
1082 void Connect::read_data_no_mime(DDS &data, Response *rs)
1083 {
1084  if (rs->get_type() == unknown_type)
1085  divine_type_information(rs);
1086 
1087  switch (rs->get_type()) {
1088  case dods_data:
1089  d_version = rs->get_version();
1090  d_protocol = rs->get_protocol();
1091  process_data(data, rs);
1092  break;
1093  case dods_data_ddx:
1094  process_data(data, rs);
1095  d_version = rs->get_version();
1096  // TODO should check to see if this hack is a correct replacement
1097  // for get_protocol from DataDDS
1098  d_protocol = data.get_dap_version();
1099  break;
1100  default:
1101  throw InternalErr(__FILE__, __LINE__, "Should have been a DataDDS or DataDDX.");
1102  }
1103 }
1104 
1105 bool
1106 Connect::is_local()
1107 {
1108  return _local;
1109 }
1110 
1127 string Connect::URL(bool ce)
1128 {
1129  if (_local)
1130  throw InternalErr(__FILE__, __LINE__, "URL(): This call is only valid for a DAP data source.");
1131 
1132  if (ce)
1133  return _URL + "?" + _proj + _sel;
1134  else
1135  return _URL;
1136 }
1137 
1146 string Connect::CE()
1147 {
1148  if (_local)
1149  throw InternalErr(__FILE__, __LINE__, "CE(): This call is only valid for a DAP data source.");
1150 
1151  return _proj + _sel;
1152 }
1153 
1159 void Connect::set_credentials(string u, string p)
1160 {
1161  if (d_http)
1162  d_http->set_credentials(u, p);
1163 }
1164 
1169 {
1170  if (d_http)
1171  d_http->set_accept_deflate(deflate);
1172 }
1173 
1179 void Connect::set_xdap_protocol(int major, int minor)
1180 {
1181  if (d_http)
1182  d_http->set_xdap_protocol(major, minor);
1183 }
1184 
1189 {
1190  if (d_http)
1191  d_http->set_cache_enabled(cache);
1192 }
1193 
1194 bool Connect::is_cache_enabled()
1195 {
1196  bool status;
1197  DBG(cerr << "Entering is_cache_enabled (" << hex << d_http << dec
1198  << ")... ");
1199  if (d_http)
1200  status = d_http->is_cache_enabled();
1201  else
1202  status = false;
1203  DBGN(cerr << "exiting" << endl);
1204  return status;
1205 }
1206 
1207 } // namespace libdap
virtual string CE()
Get the Connect&#39;s constraint expression.
Definition: Connect.cc:1146
virtual void request_das_url(DAS &das)
Get the DAS from a server.
Definition: Connect.cc:496
string get_next_mime_header(FILE *in)
Definition: mime_util.cc:838
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:705
virtual string URL(bool CE=true)
Get the object&#39;s URL.
Definition: Connect.cc:1127
string id2www_ce(string in, const string &allowable)
Definition: escaping.cc:178
string prune_spaces(const string &name)
Definition: util.cc:459
void set_credentials(const string &u, const string &p)
void set_xdap_protocol(int major, int minor)
Definition: Connect.cc:1179
void read_multipart_headers(FILE *in, const string &content_type, const ObjectType object_type, const string &cid)
Definition: mime_util.cc:1005
virtual void request_dds_url(DDS &dds)
Get the DDS from a server.
Definition: Connect.cc:642
bool parse(FILE *fp)
Parse an Error object.
Definition: Error.cc:158
void set_cache_enabled(bool enabled)
Definition: HTTPConnect.h:151
string read_multipart_boundary(FILE *in, const string &boundary)
Definition: mime_util.cc:959
ObjectType
The type of object in the stream coming from the data server.
Definition: ObjectType.h:58
HTTPResponse * fetch_url(const string &url)
Definition: HTTPConnect.cc:627
top level DAP object to house generic methods
Definition: AISConnect.cc:30
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:942
void parse_mime_header(const string &header, string &name, string &value)
Definition: mime_util.cc:912
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:989
ObjectType get_description_type(const string &value)
Definition: mime_util.cc:339
void set_cache_enabled(bool enabled)
Definition: Connect.cc:1188
virtual void request_dds(DDS &dds, string expr="")
Get the DDS from a server.
Definition: Connect.cc:562
void set_accept_deflate(bool deflate)
Definition: Connect.cc:1168
virtual void request_data(DataDDS &data, string expr="")
Get the DAS from a server.
Definition: Connect.cc:845
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:1062
string cid_to_header_value(const string &cid)
Definition: mime_util.cc:1077
virtual void request_das(DAS &das)
Get the DAS from a server.
Definition: Connect.cc:430
void set_accept_deflate(bool defalte)
void set_xdap_protocol(int major, int minor)
virtual void parse(string fname)
Reads a DAS from the named file.
Definition: DAS.cc:232
virtual string request_version()
Definition: Connect.cc:362
void set_credentials(string u, string p)
Set the credentials for responding to challenges while dereferencing URLs.
Definition: Connect.cc:1159
virtual string request_protocol()
Definition: Connect.cc:398
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:896
A class for error processing.
Definition: Error.h:92
BaseTypeFactory * get_factory() const
Definition: DDS.h:242
Holds a DAP2 DDS.
Definition: DataDDS.h:77
virtual void request_ddx_url(DDS &dds)
The &#39;url&#39; version of request_ddx.
Definition: Connect.cc:773