libdap  Updated for version 3.17.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  case dods_data_ddx: {
93  // Parse the DDX; throw an exception on error.
94  DDXParser ddx_parser(data.get_factory());
95 
96  // Read the MPM boundary and then read the subsequent headers
97  string boundary = read_multipart_boundary(rs->get_stream());
98  DBG(cerr << "MPM Boundary: " << boundary << endl);
99  read_multipart_headers(rs->get_stream(), "text/xml", dods_ddx);
100 
101  // Parse the DDX, reading up to and including the next boundary.
102  // Return the CID for the matching data part
103  string data_cid;
104  ddx_parser.intern_stream(rs->get_stream(), &data, data_cid, boundary);
105 
106  // Munge the CID into something we can work with
107  data_cid = cid_to_header_value(data_cid);
108  DBG(cerr << "Data CID: " << data_cid << endl);
109 
110  // Read the data part's MPM part headers (boundary was read by
111  // DDXParse::intern)
112  read_multipart_headers(rs->get_stream(), "application/octet-stream", dap4_data, data_cid);
113 
114  // Now read the data
115 #if FILE_UN_MARSHALLER
116  XDRFileUnMarshaller um(rs->get_stream());
117 #else
118  fpistream in ( rs->get_stream() );
119  XDRStreamUnMarshaller um( in );
120 #endif
121  for (DDS::Vars_iter i = data.var_begin(); i != data.var_end(); i++) {
122  (*i)->deserialize(um, &data);
123  }
124  return;
125  }
126 
127  case dods_data:
128  default: {
129  // Parse the DDS; throw an exception on error.
130  data.parse(rs->get_stream());
131 #if FILE_UN_MARSHALLER
132  XDRFileUnMarshaller um(rs->get_stream());
133 #else
134  fpistream in ( rs->get_stream() );
135  XDRStreamUnMarshaller um( in );
136 #endif
137  // Load the DDS with data.
138  for (DDS::Vars_iter i = data.var_begin(); i != data.var_end(); i++) {
139  (*i)->deserialize(um, &data);
140  }
141  return;
142  }
143  }
144 }
145 
148 void Connect::process_data(DDS &data, Response *rs)
149 {
150  DBG(cerr << "Entering Connect::process_data" << endl);
151 
152  // TODO is this the correct info?
153  data.set_dap_version(rs->get_protocol());
154 
155  DBG(cerr << "Entering process_data: d_stream = " << rs << endl);
156  switch (rs->get_type()) {
157  case dods_error: {
158  Error e;
159  if (!e.parse(rs->get_stream()))
160  throw InternalErr(__FILE__, __LINE__, "Could not parse the Error object returned by the server!");
161  throw e;
162  }
163 
164  case web_error:
165  // Web errors (those reported in the return document's MIME header)
166  // are processed by the WWW library.
167  throw InternalErr(__FILE__, __LINE__,
168  "An error was reported by the remote httpd; this should have been processed by HTTPConnect.");
169 
170  // FIXME: The following case is never used. There is no such response. jhrg 10/20/15
171  case dods_data_ddx: {
172  // Parse the DDX; throw an exception on error.
173  DDXParser ddx_parser(data.get_factory());
174 
175  // Read the MPM boundary and then read the subsequent headers
176  string boundary = read_multipart_boundary(rs->get_stream());
177  DBG(cerr << "MPM Boundary: " << boundary << endl);
178  read_multipart_headers(rs->get_stream(), "text/xml", dods_ddx);
179 
180  // Parse the DDX, reading up to and including the next boundary.
181  // Return the CID for the matching data part
182  string data_cid;
183  ddx_parser.intern_stream(rs->get_stream(), &data, data_cid, boundary);
184 
185  // Munge the CID into something we can work with
186  data_cid = cid_to_header_value(data_cid);
187  DBG(cerr << "Data CID: " << data_cid << endl);
188 
189  // Read the data part's MPM part headers (boundary was read by
190  // DDXParse::intern)
191  read_multipart_headers(rs->get_stream(), "application/octet-stream", dap4_data, data_cid);
192 
193  // Now read the data
194  XDRFileUnMarshaller um(rs->get_stream());
195  for (DDS::Vars_iter i = data.var_begin(); i != data.var_end(); i++) {
196  (*i)->deserialize(um, &data);
197  }
198  return;
199  }
200 
201  case dods_data:
202  default: {
203  // Parse the DDS; throw an exception on error.
204  data.parse(rs->get_stream());
205 
206  XDRFileUnMarshaller um(rs->get_stream());
207 
208  // Load the DDS with data.
209  for (DDS::Vars_iter i = data.var_begin(); i != data.var_end(); i++) {
210  (*i)->deserialize(um, &data);
211  }
212  return;
213  }
214  }
215 }
216 
217 // Barely a parser... This is used when reading from local sources of DODS
218 // Data objects. It simulates the important actions of the libwww MIME header
219 // parser. Those actions fill in certain fields in the Connect object. jhrg
220 // 5/20/97
221 //
222 // Make sure that this parser reads from data_source without disturbing the
223 // information in data_source that follows the MIME header. Since the DDS
224 // (which follows the MIME header) is parsed by a flex/bison scanner/parser,
225 // make sure to use I/O calls that will mesh with ANSI C I/O calls. In the
226 // old GNU libg++, the C++ calls were synchronized with the C calls, but that
227 // may no longer be the case. 5/31/99 jhrg
228 
238 void Connect::parse_mime(Response *rs)
239 {
240  rs->set_version("dods/0.0"); // initial value; for backward compatibility.
241  rs->set_protocol("2.0");
242 
243  FILE *data_source = rs->get_stream();
244  string mime = get_next_mime_header(data_source);
245  while (!mime.empty()) {
246  string header, value;
247  parse_mime_header(mime, header, value);
248 
249  // Note that this is an ordered list
250  if (header == "content-description:") {
251  DBG(cout << header << ": " << value << endl);
252  rs->set_type(get_description_type(value));
253  }
254  // Use the value of xdods-server only if no other value has been read
255  else if (header == "xdods-server:" && rs->get_version() == "dods/0.0") {
256  DBG(cout << header << ": " << value << endl);
257  rs->set_version(value);
258  }
259  // This trumps 'xdods-server' and 'server'
260  else if (header == "xopendap-server:") {
261  DBG(cout << header << ": " << value << endl);
262  rs->set_version(value);
263  }
264  else if (header == "xdap:") {
265  DBG(cout << header << ": " << value << endl);
266  rs->set_protocol(value);
267  }
268  // Only look for 'server' if no other header supplies this info.
269  else if (rs->get_version() == "dods/0.0" && header == "server:") {
270  DBG(cout << header << ": " << value << endl);
271  rs->set_version(value);
272  }
273 
274  mime = get_next_mime_header(data_source);
275  }
276 }
277 
278 // public mfuncs
279 
287 Connect::Connect(const string &n, string uname, string password) :
288  d_http(0), d_version("unknown"), d_protocol("2.0")
289 {
290  string name = prune_spaces(n);
291 
292  // Figure out if the URL starts with 'http', if so, make sure that we
293  // talk to an instance of HTTPConnect.
294  if (name.find("http") == 0) {
295  DBG(cerr << "Connect: The identifier is an http URL" << endl);
296  d_http = new HTTPConnect(RCReader::instance());
297 
298  // Find and store any CE given with the URL.
299  string::size_type dotpos = name.find('?');
300  if (dotpos != name.npos) {
301  _URL = name.substr(0, dotpos);
302  string expr = name.substr(dotpos + 1);
303 
304  dotpos = expr.find('&');
305  if (dotpos != expr.npos) {
306  _proj = expr.substr(0, dotpos);
307  _sel = expr.substr(dotpos); // XXX includes '&'
308  }
309  else {
310  _proj = expr;
311  _sel = "";
312  }
313  }
314  else {
315  _URL = name;
316  _proj = "";
317  _sel = "";
318  }
319 
320  _local = false;
321  }
322  else {
323  DBG(cerr << "Connect: The identifier is a local data source." << endl);
324 
325  d_http = 0;
326  _URL = "";
327  _local = true; // local in this case means non-DAP
328  }
329 
330  set_credentials(uname, password);
331 }
332 
333 Connect::~Connect()
334 {
335  DBG2(cerr << "Entering the Connect dtor" << endl);
336 
337  if (d_http)
338  delete d_http;
339  d_http = 0;
340 
341  DBG2(cerr << "Leaving the Connect dtor" << endl);
342 }
343 
352 {
353  string version_url = _URL + ".ver";
354  if (_proj.length() + _sel.length())
355  version_url = version_url + "?" + id2www_ce(_proj + _sel);
356 
357  Response *rs = 0;
358  try {
359  rs = d_http->fetch_url(version_url);
360  }
361  catch (Error &e) {
362  delete rs;
363  rs = 0;
364  throw;
365  }
366 
367  d_version = rs->get_version();
368  d_protocol = rs->get_protocol();
369 
370  delete rs;
371  rs = 0;
372 
373  return d_version;
374 }
375 
388 {
389  string version_url = _URL + ".ver";
390  if (_proj.length() + _sel.length())
391  version_url = version_url + "?" + id2www_ce(_proj + _sel);
392 
393  Response *rs = 0;
394  try {
395  rs = d_http->fetch_url(version_url);
396  }
397  catch (Error &e) {
398  delete rs;
399  rs = 0;
400  throw;
401  }
402 
403  d_version = rs->get_version();
404  d_protocol = rs->get_protocol();
405 
406  delete rs;
407  rs = 0;
408 
409  return d_protocol;
410 }
411 
420 {
421  string das_url = _URL + ".das";
422  if (_proj.length() + _sel.length())
423  das_url = das_url + "?" + id2www_ce(_proj + _sel);
424 
425  Response *rs = 0;
426  try {
427  rs = d_http->fetch_url(das_url);
428  }
429  catch (Error &e) {
430  delete rs;
431  rs = 0;
432  throw;
433  }
434 
435  d_version = rs->get_version();
436  d_protocol = rs->get_protocol();
437 
438  switch (rs->get_type()) {
439  case dods_error: {
440  Error e;
441  if (!e.parse(rs->get_stream())) {
442  delete rs;
443  rs = 0;
444  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
445  }
446  delete rs;
447  rs = 0;
448  throw e;
449  }
450 
451  case web_error:
452  // We should never get here; a web error should be picked up read_url
453  // (called by fetch_url) and result in a thrown Error object.
454  break;
455 
456  case dods_das:
457  default:
458  // DAS::parse throws an exception on error.
459  try {
460  das.parse(rs->get_stream()); // read and parse the das from a file
461  }
462  catch (Error &e) {
463  delete rs;
464  rs = 0;
465  throw;
466  }
467 
468  break;
469  }
470 
471  delete rs;
472  rs = 0;
473 }
474 
486 {
487  string use_url = _URL + "?" + _proj + _sel;
488  Response *rs = 0;
489  try {
490  rs = d_http->fetch_url(use_url);
491  }
492  catch (Error &e) {
493  delete rs;
494  rs = 0;
495  throw;
496  }
497 
498  d_version = rs->get_version();
499  d_protocol = rs->get_protocol();
500 
501  switch (rs->get_type()) {
502  case dods_error: {
503  Error e;
504  if (!e.parse(rs->get_stream())) {
505  delete rs;
506  rs = 0;
507  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
508  }
509  delete rs;
510  rs = 0;
511  throw e;
512  }
513 
514  case web_error:
515  // We should never get here; a web error should be picked up read_url
516  // (called by fetch_url) and result in a thrown Error object.
517  break;
518 
519  case dods_das:
520  default:
521  // DAS::parse throws an exception on error.
522  try {
523  das.parse(rs->get_stream()); // read and parse the das from a file
524  }
525  catch (Error &e) {
526  delete rs;
527  rs = 0;
528  throw;
529  }
530 
531  break;
532  }
533 
534  delete rs;
535  rs = 0;
536 }
537 
551 void Connect::request_dds(DDS &dds, string expr)
552 {
553  string proj, sel;
554  string::size_type dotpos = expr.find('&');
555  if (dotpos != expr.npos) {
556  proj = expr.substr(0, dotpos);
557  sel = expr.substr(dotpos);
558  }
559  else {
560  proj = expr;
561  sel = "";
562  }
563 
564  string dds_url = _URL + ".dds" + "?" + id2www_ce(_proj + proj + _sel + sel);
565 
566  Response *rs = 0;
567  try {
568  rs = d_http->fetch_url(dds_url);
569  }
570  catch (Error &e) {
571  delete rs;
572  rs = 0;
573  throw;
574  }
575 
576  d_version = rs->get_version();
577  d_protocol = rs->get_protocol();
578 
579  switch (rs->get_type()) {
580  case dods_error: {
581  Error e;
582  if (!e.parse(rs->get_stream())) {
583  delete rs;
584  rs = 0;
585  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
586  }
587  delete rs;
588  rs = 0;
589  throw e;
590  }
591 
592  case web_error:
593  // We should never get here; a web error should be picked up read_url
594  // (called by fetch_url) and result in a thrown Error object.
595  break;
596 
597  case dods_dds:
598  default:
599  // DDS::prase throws an exception on error.
600  try {
601  dds.parse(rs->get_stream()); // read and parse the dds from a file
602  }
603  catch (Error &e) {
604  delete rs;
605  rs = 0;
606  throw;
607  }
608  break;
609  }
610 
611  delete rs;
612  rs = 0;
613 }
614 
632 {
633  string use_url = _URL + "?" + _proj + _sel;
634  Response *rs = 0;
635  try {
636  rs = d_http->fetch_url(use_url);
637  }
638  catch (Error &e) {
639  delete rs;
640  rs = 0;
641  throw;
642  }
643 
644  d_version = rs->get_version();
645  d_protocol = rs->get_protocol();
646 
647  switch (rs->get_type()) {
648  case dods_error: {
649  Error e;
650  if (!e.parse(rs->get_stream())) {
651  delete rs;
652  rs = 0;
653  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
654  }
655  delete rs;
656  rs = 0;
657  throw e;
658  }
659 
660  case web_error:
661  // We should never get here; a web error should be picked up read_url
662  // (called by fetch_url) and result in a thrown Error object.
663  break;
664 
665  case dods_dds:
666  default:
667  // DDS::prase throws an exception on error.
668  try {
669  dds.parse(rs->get_stream()); // read and parse the dds from a file
670  }
671  catch (Error &e) {
672  delete rs;
673  rs = 0;
674  throw;
675  }
676  break;
677  }
678 
679  delete rs;
680  rs = 0;
681 }
682 
694 void Connect::request_ddx(DDS &dds, string expr)
695 {
696  string proj, sel;
697  string::size_type dotpos = expr.find('&');
698  if (dotpos != expr.npos) {
699  proj = expr.substr(0, dotpos);
700  sel = expr.substr(dotpos);
701  }
702  else {
703  proj = expr;
704  sel = "";
705  }
706 
707  string ddx_url = _URL + ".ddx" + "?" + id2www_ce(_proj + proj + _sel + sel);
708 
709  Response *rs = 0;
710  try {
711  rs = d_http->fetch_url(ddx_url);
712  }
713  catch (Error &e) {
714  delete rs;
715  throw;
716  }
717 
718  d_version = rs->get_version();
719  d_protocol = rs->get_protocol();
720 
721  switch (rs->get_type()) {
722  case dods_error: {
723  Error e;
724  if (!e.parse(rs->get_stream())) {
725  delete rs;
726  rs = 0;
727  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
728  }
729  delete rs;
730  throw e;
731  }
732 
733  case web_error:
734  // We should never get here; a web error should be picked up read_url
735  // (called by fetch_url) and result in a thrown Error object.
736  break;
737 
738  case dods_ddx:
739  try {
740  string blob;
741 
742  DDXParser ddxp(dds.get_factory());
743  ddxp.intern_stream(rs->get_stream(), &dds, blob);
744  }
745  catch (Error &e) {
746  delete rs;
747  throw;
748  }
749  break;
750 
751  default:
752  ObjectType ot = rs->get_type();
753  delete rs;
754  throw Error("Invalid response type when requesting a DDX response. Response type: " + long_to_string(ot));
755  }
756 
757  delete rs;
758 }
759 
763 {
764  string use_url = _URL + "?" + _proj + _sel;
765 
766  Response *rs = 0;
767  try {
768  rs = d_http->fetch_url(use_url);
769  }
770  catch (Error &e) {
771  delete rs;
772  throw;
773  }
774 
775  d_version = rs->get_version();
776  d_protocol = rs->get_protocol();
777 
778  switch (rs->get_type()) {
779  case dods_error: {
780  Error e;
781  if (!e.parse(rs->get_stream())) {
782  delete rs;
783  throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
784  }
785  delete rs;
786  throw e;
787  }
788 
789  case web_error:
790  // We should never get here; a web error should be picked up read_url
791  // (called by fetch_url) and result in a thrown Error object.
792  delete rs;
793  throw InternalErr(__FILE__, __LINE__, "Web error.");
794 
795  case dods_ddx:
796  try {
797  string blob;
798 
799  DDXParser ddxp(dds.get_factory());
800  ddxp.intern_stream(rs->get_stream(), &dds, blob);
801  }
802  catch (Error &e) {
803  delete rs;
804  throw;
805  }
806  break;
807 
808  default: {
809  ObjectType ot = rs->get_type();
810  delete rs;
811 
812  throw Error("Invalid response type when requesting a DDX response. Response type: " + long_to_string(ot));
813  }
814  }
815 
816  delete rs;
817 }
818 
834 void Connect::request_data(DataDDS &data, string expr)
835 {
836  string proj, sel;
837  string::size_type dotpos = expr.find('&');
838  if (dotpos != expr.npos) {
839  proj = expr.substr(0, dotpos);
840  sel = expr.substr(dotpos);
841  }
842  else {
843  proj = expr;
844  sel = "";
845  }
846 
847  string data_url = _URL + ".dods?" + id2www_ce(_proj + proj + _sel + sel);
848 
849  Response *rs = 0;
850  // We need to catch Error exceptions to ensure calling close_output.
851  try {
852  rs = d_http->fetch_url(data_url);
853 
854  d_version = rs->get_version();
855  d_protocol = rs->get_protocol();
856 
857  process_data(data, rs);
858  delete rs;
859  rs = 0;
860  }
861  catch (Error &e) {
862  delete rs;
863  rs = 0;
864  throw;
865  }
866 }
867 
886 {
887  string use_url = _URL + "?" + _proj + _sel;
888  Response *rs = 0;
889  // We need to catch Error exceptions to ensure calling close_output.
890  try {
891  rs = d_http->fetch_url(use_url);
892 
893  d_version = rs->get_version();
894  d_protocol = rs->get_protocol();
895 
896  process_data(data, rs);
897  delete rs;
898  rs = 0;
899  }
900  catch (Error &e) {
901  delete rs;
902  rs = 0;
903  throw;
904  }
905 }
906 
907 // FIXME Unused?
908 void Connect::request_data_ddx(DataDDS &data, string expr)
909 {
910  string proj, sel;
911  string::size_type dotpos = expr.find('&');
912  if (dotpos != expr.npos) {
913  proj = expr.substr(0, dotpos);
914  sel = expr.substr(dotpos);
915  }
916  else {
917  proj = expr;
918  sel = "";
919  }
920 
921  string data_url = _URL + ".dap?" + id2www_ce(_proj + proj + _sel + sel);
922 
923  Response *rs = 0;
924  // We need to catch Error exceptions to ensure calling close_output.
925  try {
926  rs = d_http->fetch_url(data_url);
927 
928  d_version = rs->get_version();
929  d_protocol = rs->get_protocol();
930 
931  process_data(data, rs);
932  delete rs;
933  rs = 0;
934  }
935  catch (Error &e) {
936  delete rs;
937  rs = 0;
938  throw;
939  }
940 }
941 
942 // FIXME Unused?
943 void Connect::request_data_ddx_url(DataDDS &data)
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;
956  rs = 0;
957  }
958  catch (Error &e) {
959  delete rs;
960  rs = 0;
961  throw;
962  }
963 }
964 
979 {
980  if (!rs)
981  throw InternalErr(__FILE__, __LINE__, "Response object is null.");
982 
983  // Read from data_source and parse the MIME headers specific to DAP2/4.
984  parse_mime(rs);
985 
986  read_data_no_mime(data, rs);
987 }
988 void
989 Connect::read_data(DDS &data, Response *rs)
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 
1000 // This function looks at the input stream and makes its best guess at what
1001 // lies in store for downstream processing code. Definitely heuristic.
1002 // Assumptions:
1003 // #1 The current file position is past any MIME headers (if they were present).
1004 // #2 We must reset the FILE* position to the start of the DDS or DDX headers
1005 static void divine_type_information(Response *rs)
1006 {
1007  // Consume whitespace
1008  int c = getc(rs->get_stream());
1009  while (!feof(rs->get_stream()) && !ferror(rs->get_stream()) && isspace(c)) {
1010  c = getc(rs->get_stream());
1011  }
1012 
1013 
1014  if (ferror(rs->get_stream()))
1015  throw Error("Error reading response type information: " + string(strerror(errno)));
1016  if (feof(rs->get_stream()))
1017  throw Error("Error reading response type information: Found EOF");
1018 
1019  // The heuristic here is that a DataDDX is a multipart MIME document and
1020  // The first non space character found after the headers is the start of
1021  // the first part which looks like '--<boundary>' while a DataDDS starts
1022  // with a DDS (;Dataset {' ...). I take into account that our parsers have
1023  // accepted both 'Dataset' and 'dataset' for a long time.
1024  switch (c) {
1025  case '-':
1026  rs->set_type(dods_data_ddx);
1027  break;
1028  case 'D':
1029  case 'd':
1030  rs->set_type(dods_data);
1031  break;
1032  default:
1033  throw InternalErr(__FILE__, __LINE__, "Could not determine type of response object in stream.");
1034  }
1035 
1036  ungetc(c, rs->get_stream());
1037 }
1038 
1052 {
1053  if (rs->get_type() == unknown_type)
1054  divine_type_information(rs);
1055 
1056  switch (rs->get_type()) {
1057  case dods_data:
1058  d_version = rs->get_version();
1059  d_protocol = rs->get_protocol();
1060  process_data(data, rs);
1061  break;
1062  case dods_data_ddx:
1063  process_data(data, rs);
1064  d_version = rs->get_version();
1065  d_protocol = data.get_protocol();
1066  break;
1067  default:
1068  throw InternalErr(__FILE__, __LINE__, "Should have been a DataDDS or DataDDX.");
1069  }
1070 }
1071 void Connect::read_data_no_mime(DDS &data, Response *rs)
1072 {
1073  if (rs->get_type() == unknown_type)
1074  divine_type_information(rs);
1075 
1076  switch (rs->get_type()) {
1077  case dods_data:
1078  d_version = rs->get_version();
1079  d_protocol = rs->get_protocol();
1080  process_data(data, rs);
1081  break;
1082  case dods_data_ddx:
1083  process_data(data, rs);
1084  d_version = rs->get_version();
1085  // TODO should check to see if this hack is a correct replacement
1086  // for get_protocol from DataDDS
1087  d_protocol = data.get_dap_version();
1088  break;
1089  default:
1090  throw InternalErr(__FILE__, __LINE__, "Should have been a DataDDS or DataDDX.");
1091  }
1092 }
1093 
1094 bool
1095 Connect::is_local()
1096 {
1097  return _local;
1098 }
1099 
1116 string Connect::URL(bool ce)
1117 {
1118  if (_local)
1119  throw InternalErr(__FILE__, __LINE__, "URL(): This call is only valid for a DAP data source.");
1120 
1121  if (ce)
1122  return _URL + "?" + _proj + _sel;
1123  else
1124  return _URL;
1125 }
1126 
1135 string Connect::CE()
1136 {
1137  if (_local)
1138  throw InternalErr(__FILE__, __LINE__, "CE(): This call is only valid for a DAP data source.");
1139 
1140  return _proj + _sel;
1141 }
1142 
1148 void Connect::set_credentials(string u, string p)
1149 {
1150  if (d_http)
1151  d_http->set_credentials(u, p);
1152 }
1153 
1158 {
1159  if (d_http)
1160  d_http->set_accept_deflate(deflate);
1161 }
1162 
1168 void Connect::set_xdap_protocol(int major, int minor)
1169 {
1170  if (d_http)
1171  d_http->set_xdap_protocol(major, minor);
1172 }
1173 
1178 {
1179  if (d_http)
1180  d_http->set_cache_enabled(cache);
1181 }
1182 
1183 bool Connect::is_cache_enabled()
1184 {
1185  bool status;
1186  DBG(cerr << "Entering is_cache_enabled (" << hex << d_http << dec
1187  << ")... ");
1188  if (d_http)
1189  status = d_http->is_cache_enabled();
1190  else
1191  status = false;
1192  DBGN(cerr << "exiting" << endl);
1193  return status;
1194 }
1195 
1196 } // namespace libdap
virtual string CE()
Get the Connect&#39;s constraint expression.
Definition: Connect.cc:1135
virtual void request_das_url(DAS &das)
Get the DAS from a server.
Definition: Connect.cc:485
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:694
virtual string URL(bool CE=true)
Get the object&#39;s URL.
Definition: Connect.cc:1116
string id2www_ce(string in, const string &allowable)
Definition: escaping.cc:178
string prune_spaces(const string &name)
Definition: util.cc:455
void set_credentials(const string &u, const string &p)
void set_xdap_protocol(int major, int minor)
Definition: Connect.cc:1168
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:631
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:978
ObjectType get_description_type(const string &value)
Definition: mime_util.cc:339
void set_cache_enabled(bool enabled)
Definition: Connect.cc:1177
virtual void request_dds(DDS &dds, string expr="")
Get the DDS from a server.
Definition: Connect.cc:551
void set_accept_deflate(bool deflate)
Definition: Connect.cc:1157
virtual void request_data(DataDDS &data, string expr="")
Get the DAS from a server.
Definition: Connect.cc:834
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:1051
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:419
void set_accept_deflate(bool defalte)
Definition: HTTPConnect.cc:998
BaseTypeFactory * get_factory() const
Definition: DDS.h:237
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:351
void set_credentials(string u, string p)
Set the credentials for responding to challenges while dereferencing URLs.
Definition: Connect.cc:1148
virtual string request_protocol()
Definition: Connect.cc:387
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:885
A class for error processing.
Definition: Error.h:90
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:762