libdap  Updated for version 3.18.2
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 
246 void Connect::parse_mime(Response *rs)
247 {
248  rs->set_version("dods/0.0"); // initial value; for backward compatibility.
249  rs->set_protocol("2.0");
250 
251  FILE *data_source = rs->get_stream();
252  string mime = get_next_mime_header(data_source);
253  while (!mime.empty()) {
254  string header, value;
255  parse_mime_header(mime, header, value);
256 
257  // Note that this is an ordered list
258  if (header == "content-description:") {
259  DBG(cout << header << ": " << value << endl);
260  rs->set_type(get_description_type(value));
261  }
262  // Use the value of xdods-server only if no other value has been read
263  else if (header == "xdods-server:" && rs->get_version() == "dods/0.0") {
264  DBG(cout << header << ": " << value << endl);
265  rs->set_version(value);
266  }
267  // This trumps 'xdods-server' and 'server'
268  else if (header == "xopendap-server:") {
269  DBG(cout << header << ": " << value << endl);
270  rs->set_version(value);
271  }
272  else if (header == "xdap:") {
273  DBG(cout << header << ": " << value << endl);
274  rs->set_protocol(value);
275  }
276  // Only look for 'server' if no other header supplies this info.
277  else if (rs->get_version() == "dods/0.0" && header == "server:") {
278  DBG(cout << header << ": " << value << endl);
279  rs->set_version(value);
280  }
281 
282  mime = get_next_mime_header(data_source);
283  }
284 }
285 
286 // public mfuncs
287 
295 Connect::Connect(const string &n, string uname, string password) :
296  d_http(0), d_version("unknown"), d_protocol("2.0")
297 {
298  string name = prune_spaces(n);
299 
300  // Figure out if the URL starts with 'http', if so, make sure that we
301  // talk to an instance of HTTPConnect.
302  if (name.find("http") == 0) {
303  DBG(cerr << "Connect: The identifier is an http URL" << endl);
304  d_http = new HTTPConnect(RCReader::instance());
305 
306  // Find and store any CE given with the URL.
307  string::size_type dotpos = name.find('?');
308  if (dotpos != name.npos) {
309  _URL = name.substr(0, dotpos);
310  string expr = name.substr(dotpos + 1);
311 
312  dotpos = expr.find('&');
313  if (dotpos != expr.npos) {
314  _proj = expr.substr(0, dotpos);
315  _sel = expr.substr(dotpos); // XXX includes '&'
316  }
317  else {
318  _proj = expr;
319  _sel = "";
320  }
321  }
322  else {
323  _URL = name;
324  _proj = "";
325  _sel = "";
326  }
327 
328  _local = false;
329  }
330  else {
331  DBG(cerr << "Connect: The identifier is a local data source." << endl);
332 
333  d_http = 0;
334  _URL = "";
335  _local = true; // local in this case means non-DAP
336  }
337 
338  set_credentials(uname, password);
339 }
340 
341 Connect::~Connect()
342 {
343  DBG2(cerr << "Entering the Connect dtor" << endl);
344 
345  if (d_http)
346  delete d_http;
347  d_http = 0;
348 
349  DBG2(cerr << "Leaving the Connect dtor" << endl);
350 }
351 
360 {
361  string version_url = _URL + ".ver";
362  if (_proj.length() + _sel.length())
363  version_url = version_url + "?" + id2www_ce(_proj + _sel);
364 
365  Response *rs = 0;
366  try {
367  rs = d_http->fetch_url(version_url);
368  }
369  catch (Error &e) {
370  delete rs;
371  rs = 0;
372  throw;
373  }
374 
375  d_version = rs->get_version();
376  d_protocol = rs->get_protocol();
377 
378  delete rs;
379  rs = 0;
380 
381  return d_version;
382 }
383 
396 {
397  string version_url = _URL + ".ver";
398  if (_proj.length() + _sel.length())
399  version_url = version_url + "?" + id2www_ce(_proj + _sel);
400 
401  Response *rs = 0;
402  try {
403  rs = d_http->fetch_url(version_url);
404  }
405  catch (Error &e) {
406  delete rs;
407  rs = 0;
408  throw;
409  }
410 
411  d_version = rs->get_version();
412  d_protocol = rs->get_protocol();
413 
414  delete rs;
415  rs = 0;
416 
417  return d_protocol;
418 }
419 
428 {
429  string das_url = _URL + ".das";
430  if (_proj.length() + _sel.length())
431  das_url = das_url + "?" + id2www_ce(_proj + _sel);
432 
433  Response *rs = 0;
434  try {
435  rs = d_http->fetch_url(das_url);
436  }
437  catch (Error &e) {
438  delete rs;
439  rs = 0;
440  throw;
441  }
442 
443  d_version = rs->get_version();
444  d_protocol = rs->get_protocol();
445 
446  switch (rs->get_type()) {
447  case dods_error: {
448  Error e;
449  if (!e.parse(rs->get_stream())) {
450  delete rs;
451  rs = 0;
452  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
453  }
454  delete rs;
455  rs = 0;
456  throw e;
457  }
458 
459  case web_error:
460  // We should never get here; a web error should be picked up read_url
461  // (called by fetch_url) and result in a thrown Error object.
462  break;
463 
464  case dods_das:
465  default:
466  // DAS::parse throws an exception on error.
467  try {
468  das.parse(rs->get_stream()); // read and parse the das from a file
469  }
470  catch (Error &e) {
471  delete rs;
472  rs = 0;
473  throw;
474  }
475 
476  break;
477  }
478 
479  delete rs;
480  rs = 0;
481 }
482 
494 {
495  string use_url = _URL + "?" + _proj + _sel;
496  Response *rs = 0;
497  try {
498  rs = d_http->fetch_url(use_url);
499  }
500  catch (Error &e) {
501  delete rs;
502  rs = 0;
503  throw;
504  }
505 
506  d_version = rs->get_version();
507  d_protocol = rs->get_protocol();
508 
509  switch (rs->get_type()) {
510  case dods_error: {
511  Error e;
512  if (!e.parse(rs->get_stream())) {
513  delete rs;
514  rs = 0;
515  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
516  }
517  delete rs;
518  rs = 0;
519  throw e;
520  }
521 
522  case web_error:
523  // We should never get here; a web error should be picked up read_url
524  // (called by fetch_url) and result in a thrown Error object.
525  break;
526 
527  case dods_das:
528  default:
529  // DAS::parse throws an exception on error.
530  try {
531  das.parse(rs->get_stream()); // read and parse the das from a file
532  }
533  catch (Error &e) {
534  delete rs;
535  rs = 0;
536  throw;
537  }
538 
539  break;
540  }
541 
542  delete rs;
543  rs = 0;
544 }
545 
559 void Connect::request_dds(DDS &dds, string expr)
560 {
561  string proj, sel;
562  string::size_type dotpos = expr.find('&');
563  if (dotpos != expr.npos) {
564  proj = expr.substr(0, dotpos);
565  sel = expr.substr(dotpos);
566  }
567  else {
568  proj = expr;
569  sel = "";
570  }
571 
572  string dds_url = _URL + ".dds" + "?" + id2www_ce(_proj + proj + _sel + sel);
573 
574  Response *rs = 0;
575  try {
576  rs = d_http->fetch_url(dds_url);
577  }
578  catch (Error &e) {
579  delete rs;
580  rs = 0;
581  throw;
582  }
583 
584  d_version = rs->get_version();
585  d_protocol = rs->get_protocol();
586 
587  switch (rs->get_type()) {
588  case dods_error: {
589  Error e;
590  if (!e.parse(rs->get_stream())) {
591  delete rs;
592  rs = 0;
593  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
594  }
595  delete rs;
596  rs = 0;
597  throw e;
598  }
599 
600  case web_error:
601  // We should never get here; a web error should be picked up read_url
602  // (called by fetch_url) and result in a thrown Error object.
603  break;
604 
605  case dods_dds:
606  default:
607  // DDS::prase throws an exception on error.
608  try {
609  dds.parse(rs->get_stream()); // read and parse the dds from a file
610  }
611  catch (Error &e) {
612  delete rs;
613  rs = 0;
614  throw;
615  }
616  break;
617  }
618 
619  delete rs;
620  rs = 0;
621 }
622 
640 {
641  string use_url = _URL + "?" + _proj + _sel;
642  Response *rs = 0;
643  try {
644  rs = d_http->fetch_url(use_url);
645  }
646  catch (Error &e) {
647  delete rs;
648  rs = 0;
649  throw;
650  }
651 
652  d_version = rs->get_version();
653  d_protocol = rs->get_protocol();
654 
655  switch (rs->get_type()) {
656  case dods_error: {
657  Error e;
658  if (!e.parse(rs->get_stream())) {
659  delete rs;
660  rs = 0;
661  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
662  }
663  delete rs;
664  rs = 0;
665  throw e;
666  }
667 
668  case web_error:
669  // We should never get here; a web error should be picked up read_url
670  // (called by fetch_url) and result in a thrown Error object.
671  break;
672 
673  case dods_dds:
674  default:
675  // DDS::prase throws an exception on error.
676  try {
677  dds.parse(rs->get_stream()); // read and parse the dds from a file
678  }
679  catch (Error &e) {
680  delete rs;
681  rs = 0;
682  throw;
683  }
684  break;
685  }
686 
687  delete rs;
688  rs = 0;
689 }
690 
702 void Connect::request_ddx(DDS &dds, string expr)
703 {
704  string proj, sel;
705  string::size_type dotpos = expr.find('&');
706  if (dotpos != expr.npos) {
707  proj = expr.substr(0, dotpos);
708  sel = expr.substr(dotpos);
709  }
710  else {
711  proj = expr;
712  sel = "";
713  }
714 
715  string ddx_url = _URL + ".ddx" + "?" + id2www_ce(_proj + proj + _sel + sel);
716 
717  Response *rs = 0;
718  try {
719  rs = d_http->fetch_url(ddx_url);
720  }
721  catch (Error &e) {
722  delete rs;
723  throw;
724  }
725 
726  d_version = rs->get_version();
727  d_protocol = rs->get_protocol();
728 
729  switch (rs->get_type()) {
730  case dods_error: {
731  Error e;
732  if (!e.parse(rs->get_stream())) {
733  delete rs;
734  rs = 0;
735  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
736  }
737  delete rs;
738  throw e;
739  }
740 
741  case web_error:
742  // We should never get here; a web error should be picked up read_url
743  // (called by fetch_url) and result in a thrown Error object.
744  break;
745 
746  case dods_ddx:
747  try {
748  string blob;
749 
750  DDXParser ddxp(dds.get_factory());
751  ddxp.intern_stream(rs->get_stream(), &dds, blob);
752  }
753  catch (Error &e) {
754  delete rs;
755  throw;
756  }
757  break;
758 
759  default:
760  ObjectType ot = rs->get_type();
761  delete rs;
762  throw Error("Invalid response type when requesting a DDX response. Response type: " + long_to_string(ot));
763  }
764 
765  delete rs;
766 }
767 
771 {
772  string use_url = _URL + "?" + _proj + _sel;
773 
774  Response *rs = 0;
775  try {
776  rs = d_http->fetch_url(use_url);
777  }
778  catch (Error &e) {
779  delete rs;
780  throw;
781  }
782 
783  d_version = rs->get_version();
784  d_protocol = rs->get_protocol();
785 
786  switch (rs->get_type()) {
787  case dods_error: {
788  Error e;
789  if (!e.parse(rs->get_stream())) {
790  delete rs;
791  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
792  }
793  delete rs;
794  throw e;
795  }
796 
797  case web_error:
798  // We should never get here; a web error should be picked up read_url
799  // (called by fetch_url) and result in a thrown Error object.
800  delete rs;
801  throw InternalErr(__FILE__, __LINE__, "Web error.");
802 
803  case dods_ddx:
804  try {
805  string blob;
806 
807  DDXParser ddxp(dds.get_factory());
808  ddxp.intern_stream(rs->get_stream(), &dds, blob);
809  }
810  catch (Error &e) {
811  delete rs;
812  throw;
813  }
814  break;
815 
816  default: {
817  ObjectType ot = rs->get_type();
818  delete rs;
819 
820  throw Error("Invalid response type when requesting a DDX response. Response type: " + long_to_string(ot));
821  }
822  }
823 
824  delete rs;
825 }
826 
842 void Connect::request_data(DataDDS &data, string expr)
843 {
844  string proj, sel;
845  string::size_type dotpos = expr.find('&');
846  if (dotpos != expr.npos) {
847  proj = expr.substr(0, dotpos);
848  sel = expr.substr(dotpos);
849  }
850  else {
851  proj = expr;
852  sel = "";
853  }
854 
855  string data_url = _URL + ".dods?" + id2www_ce(_proj + proj + _sel + sel);
856 
857  Response *rs = 0;
858  // We need to catch Error exceptions to ensure calling close_output.
859  try {
860  rs = d_http->fetch_url(data_url);
861 
862  d_version = rs->get_version();
863  d_protocol = rs->get_protocol();
864 
865  process_data(data, rs);
866  delete rs;
867  rs = 0;
868  }
869  catch (Error &e) {
870  delete rs;
871  rs = 0;
872  throw;
873  }
874 }
875 
894 {
895  string use_url = _URL + "?" + _proj + _sel;
896  Response *rs = 0;
897  // We need to catch Error exceptions to ensure calling close_output.
898  try {
899  rs = d_http->fetch_url(use_url);
900 
901  d_version = rs->get_version();
902  d_protocol = rs->get_protocol();
903 
904  process_data(data, rs);
905  delete rs;
906  rs = 0;
907  }
908  catch (Error &e) {
909  delete rs;
910  rs = 0;
911  throw;
912  }
913 }
914 
915 // FIXME Unused?
916 void Connect::request_data_ddx(DataDDS &data, string expr)
917 {
918  string proj, sel;
919  string::size_type dotpos = expr.find('&');
920  if (dotpos != expr.npos) {
921  proj = expr.substr(0, dotpos);
922  sel = expr.substr(dotpos);
923  }
924  else {
925  proj = expr;
926  sel = "";
927  }
928 
929  string data_url = _URL + ".dap?" + id2www_ce(_proj + proj + _sel + sel);
930 
931  Response *rs = 0;
932  // We need to catch Error exceptions to ensure calling close_output.
933  try {
934  rs = d_http->fetch_url(data_url);
935 
936  d_version = rs->get_version();
937  d_protocol = rs->get_protocol();
938 
939  process_data(data, rs);
940  delete rs;
941  rs = 0;
942  }
943  catch (Error &e) {
944  delete rs;
945  rs = 0;
946  throw;
947  }
948 }
949 
950 // FIXME Unused?
951 void Connect::request_data_ddx_url(DataDDS &data)
952 {
953  string use_url = _URL + "?" + _proj + _sel;
954  Response *rs = 0;
955  // We need to catch Error exceptions to ensure calling close_output.
956  try {
957  rs = d_http->fetch_url(use_url);
958 
959  d_version = rs->get_version();
960  d_protocol = rs->get_protocol();
961 
962  process_data(data, rs);
963  delete rs;
964  rs = 0;
965  }
966  catch (Error &e) {
967  delete rs;
968  rs = 0;
969  throw;
970  }
971 }
972 
987 {
988  if (!rs)
989  throw InternalErr(__FILE__, __LINE__, "Response object is null.");
990 
991  // Read from data_source and parse the MIME headers specific to DAP2/4.
992  parse_mime(rs);
993 
994  read_data_no_mime(data, rs);
995 }
996 void
997 Connect::read_data(DDS &data, Response *rs)
998 {
999  if (!rs)
1000  throw InternalErr(__FILE__, __LINE__, "Response object is null.");
1001 
1002  // Read from data_source and parse the MIME headers specific to DAP2/4.
1003  parse_mime(rs);
1004 
1005  read_data_no_mime(data, rs);
1006 }
1007 
1008 // This function looks at the input stream and makes its best guess at what
1009 // lies in store for downstream processing code. Definitely heuristic.
1010 // Assumptions:
1011 // #1 The current file position is past any MIME headers (if they were present).
1012 // #2 We must reset the FILE* position to the start of the DDS or DDX headers
1013 static void divine_type_information(Response *rs)
1014 {
1015  // Consume whitespace
1016  int c = getc(rs->get_stream());
1017  while (!feof(rs->get_stream()) && !ferror(rs->get_stream()) && isspace(c)) {
1018  c = getc(rs->get_stream());
1019  }
1020 
1021 
1022  if (ferror(rs->get_stream()))
1023  throw Error("Error reading response type information: " + string(strerror(errno)));
1024  if (feof(rs->get_stream()))
1025  throw Error("Error reading response type information: Found EOF");
1026 
1027  // The heuristic here is that a DataDDX is a multipart MIME document and
1028  // The first non space character found after the headers is the start of
1029  // the first part which looks like '--<boundary>' while a DataDDS starts
1030  // with a DDS (;Dataset {' ...). I take into account that our parsers have
1031  // accepted both 'Dataset' and 'dataset' for a long time.
1032  switch (c) {
1033  case '-':
1034  rs->set_type(dods_data_ddx);
1035  break;
1036  case 'D':
1037  case 'd':
1038  rs->set_type(dods_data);
1039  break;
1040  default:
1041  throw InternalErr(__FILE__, __LINE__, "Could not determine type of response object in stream.");
1042  }
1043 
1044  ungetc(c, rs->get_stream());
1045 }
1046 
1060 {
1061  if (rs->get_type() == unknown_type)
1062  divine_type_information(rs);
1063 
1064  switch (rs->get_type()) {
1065  case dods_data:
1066  d_version = rs->get_version();
1067  d_protocol = rs->get_protocol();
1068  process_data(data, rs);
1069  break;
1070  case dods_data_ddx:
1071  process_data(data, rs);
1072  d_version = rs->get_version();
1073  d_protocol = data.get_protocol();
1074  break;
1075  default:
1076  throw InternalErr(__FILE__, __LINE__, "Should have been a DataDDS or DataDDX.");
1077  }
1078 }
1079 void Connect::read_data_no_mime(DDS &data, Response *rs)
1080 {
1081  if (rs->get_type() == unknown_type)
1082  divine_type_information(rs);
1083 
1084  switch (rs->get_type()) {
1085  case dods_data:
1086  d_version = rs->get_version();
1087  d_protocol = rs->get_protocol();
1088  process_data(data, rs);
1089  break;
1090  case dods_data_ddx:
1091  process_data(data, rs);
1092  d_version = rs->get_version();
1093  // TODO should check to see if this hack is a correct replacement
1094  // for get_protocol from DataDDS
1095  d_protocol = data.get_dap_version();
1096  break;
1097  default:
1098  throw InternalErr(__FILE__, __LINE__, "Should have been a DataDDS or DataDDX.");
1099  }
1100 }
1101 
1102 bool
1103 Connect::is_local()
1104 {
1105  return _local;
1106 }
1107 
1124 string Connect::URL(bool ce)
1125 {
1126  if (_local)
1127  throw InternalErr(__FILE__, __LINE__, "URL(): This call is only valid for a DAP data source.");
1128 
1129  if (ce)
1130  return _URL + "?" + _proj + _sel;
1131  else
1132  return _URL;
1133 }
1134 
1143 string Connect::CE()
1144 {
1145  if (_local)
1146  throw InternalErr(__FILE__, __LINE__, "CE(): This call is only valid for a DAP data source.");
1147 
1148  return _proj + _sel;
1149 }
1150 
1156 void Connect::set_credentials(string u, string p)
1157 {
1158  if (d_http)
1159  d_http->set_credentials(u, p);
1160 }
1161 
1166 {
1167  if (d_http)
1168  d_http->set_accept_deflate(deflate);
1169 }
1170 
1176 void Connect::set_xdap_protocol(int major, int minor)
1177 {
1178  if (d_http)
1179  d_http->set_xdap_protocol(major, minor);
1180 }
1181 
1186 {
1187  if (d_http)
1188  d_http->set_cache_enabled(cache);
1189 }
1190 
1191 bool Connect::is_cache_enabled()
1192 {
1193  bool status;
1194  DBG(cerr << "Entering is_cache_enabled (" << hex << d_http << dec
1195  << ")... ");
1196  if (d_http)
1197  status = d_http->is_cache_enabled();
1198  else
1199  status = false;
1200  DBGN(cerr << "exiting" << endl);
1201  return status;
1202 }
1203 
1204 } // namespace libdap
virtual string CE()
Get the Connect&#39;s constraint expression.
Definition: Connect.cc:1143
virtual void request_das_url(DAS &das)
Get the DAS from a server.
Definition: Connect.cc:493
string get_next_mime_header(FILE *in)
Definition: mime_util.cc:836
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:702
virtual string URL(bool CE=true)
Get the object&#39;s URL.
Definition: Connect.cc:1124
string id2www_ce(string in, const string &allowable)
Definition: escaping.cc:178
string prune_spaces(const string &name)
Definition: util.cc:458
void set_credentials(const string &u, const string &p)
void set_xdap_protocol(int major, int minor)
Definition: Connect.cc:1176
void read_multipart_headers(FILE *in, const string &content_type, const ObjectType object_type, const string &cid)
Definition: mime_util.cc:991
virtual void request_dds_url(DDS &dds)
Get the DDS from a server.
Definition: Connect.cc:639
bool parse(FILE *fp)
Parse an Error object.
Definition: Error.cc:156
void set_cache_enabled(bool enabled)
Definition: HTTPConnect.h:150
string read_multipart_boundary(FILE *in, const string &boundary)
Definition: mime_util.cc:945
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:617
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:948
void parse_mime_header(const string &header, string &name, string &value)
Definition: mime_util.cc:898
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:986
ObjectType get_description_type(const string &value)
Definition: mime_util.cc:339
void set_cache_enabled(bool enabled)
Definition: Connect.cc:1185
virtual void request_dds(DDS &dds, string expr="")
Get the DDS from a server.
Definition: Connect.cc:559
void set_accept_deflate(bool deflate)
Definition: Connect.cc:1165
virtual void request_data(DataDDS &data, string expr="")
Get the DAS from a server.
Definition: Connect.cc:842
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:1059
string cid_to_header_value(const string &cid)
Definition: mime_util.cc:1061
virtual void request_das(DAS &das)
Get the DAS from a server.
Definition: Connect.cc:427
void set_accept_deflate(bool defalte)
Definition: HTTPConnect.cc:998
void set_xdap_protocol(int major, int minor)
virtual void parse(string fname)
Reads a DAS from the named file.
Definition: DAS.cc:231
virtual string request_version()
Definition: Connect.cc:359
void set_credentials(string u, string p)
Set the credentials for responding to challenges while dereferencing URLs.
Definition: Connect.cc:1156
virtual string request_protocol()
Definition: Connect.cc:395
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:893
A class for error processing.
Definition: Error.h:90
BaseTypeFactory * get_factory() const
Definition: DDS.h:237
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:770