libdap  Updated for version 3.17.0
Connect.cc
00001 // -*- mode: c++; c-basic-offset:4 -*-
00002 
00003 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
00004 // Access Protocol.
00005 
00006 // Copyright (c) 2002,2003 OPeNDAP, Inc.
00007 // Author: James Gallagher <jgallagher@opendap.org>
00008 //         Dan Holloway <dholloway@gso.uri.edu>
00009 //         Reza Nekovei <reza@intcomm.net>
00010 //
00011 // This library is free software; you can redistribute it and/or
00012 // modify it under the terms of the GNU Lesser General Public
00013 // License as published by the Free Software Foundation; either
00014 // version 2.1 of the License, or (at your option) any later version.
00015 //
00016 // This library is distributed in the hope that it will be useful,
00017 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019 // Lesser General Public License for more details.
00020 //
00021 // You should have received a copy of the GNU Lesser General Public
00022 // License along with this library; if not, write to the Free Software
00023 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00024 //
00025 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
00026 
00027 // (c) COPYRIGHT URI/MIT 1994-2002
00028 // Please read the full copyright statement in the file COPYRIGHT_URI.
00029 //
00030 // Authors:
00031 //      jhrg,jimg       James Gallagher <jgallagher@gso.uri.edu>
00032 //      dan             Dan Holloway <dholloway@gso.uri.edu>
00033 //      reza            Reza Nekovei <reza@intcomm.net>
00034 
00035 #include "config.h"
00036 
00037 //#define DODS_DEBUG
00038 #define FILE_UN_MARSHALLER 1
00039 
00040 #include <cstring>
00041 #include <cerrno>
00042 
00043 #include <fstream>
00044 #include <algorithm>
00045 
00046 #include "debug.h"
00047 #include "DataDDS.h"
00048 #include "Connect.h"
00049 #include "escaping.h"
00050 //#include "RCReader.h"
00051 #include "DDXParserSAX2.h"
00052 #if FILE_UN_MARSHALLER
00053 #include "XDRFileUnMarshaller.h"
00054 #else
00055 #include "fdiostream.h"
00056 #include "XDRStreamUnMarshaller.h"
00057 #endif
00058 #include "mime_util.h"
00059 
00060 using std::cerr;
00061 using std::endl;
00062 using std::ifstream;
00063 using std::ofstream;
00064 using std::min;
00065 
00066 namespace libdap {
00067 
00070 void Connect::process_data(DataDDS &data, Response *rs)
00071 {
00072     DBG(cerr << "Entering Connect::process_data" << endl);
00073 
00074     data.set_version(rs->get_version());
00075     data.set_protocol(rs->get_protocol());
00076 
00077     DBG(cerr << "Entering process_data: d_stream = " << rs << endl);
00078     switch (rs->get_type()) {
00079         case dods_error: {
00080             Error e;
00081             if (!e.parse(rs->get_stream()))
00082                 throw InternalErr(__FILE__, __LINE__, "Could not parse the Error object returned by the server!");
00083             throw e;
00084         }
00085 
00086         case web_error:
00087             // Web errors (those reported in the return document's MIME header)
00088             // are processed by the WWW library.
00089             throw InternalErr(__FILE__, __LINE__,
00090                     "An error was reported by the remote httpd; this should have been processed by HTTPConnect..");
00091 
00092         case dods_data_ddx: {
00093             // Parse the DDX; throw an exception on error.
00094             DDXParser ddx_parser(data.get_factory());
00095 
00096             // Read the MPM boundary and then read the subsequent headers
00097             string boundary = read_multipart_boundary(rs->get_stream());
00098             DBG(cerr << "MPM Boundary: " << boundary << endl);
00099             read_multipart_headers(rs->get_stream(), "text/xml", dods_ddx);
00100 
00101             // Parse the DDX, reading up to and including the next boundary.
00102             // Return the CID for the matching data part
00103             string data_cid;
00104             ddx_parser.intern_stream(rs->get_stream(), &data, data_cid, boundary);
00105 
00106             // Munge the CID into something we can work with
00107             data_cid = cid_to_header_value(data_cid);
00108             DBG(cerr << "Data CID: " << data_cid << endl);
00109 
00110             // Read the data part's MPM part headers (boundary was read by
00111             // DDXParse::intern)
00112             read_multipart_headers(rs->get_stream(), "application/octet-stream", dap4_data, data_cid);
00113 
00114             // Now read the data
00115 #if FILE_UN_MARSHALLER
00116             XDRFileUnMarshaller um(rs->get_stream());
00117 #else
00118         fpistream in ( rs->get_stream() );
00119             XDRStreamUnMarshaller um( in );
00120 #endif
00121             for (DDS::Vars_iter i = data.var_begin(); i != data.var_end(); i++) {
00122                 (*i)->deserialize(um, &data);
00123             }
00124             return;
00125         }
00126 
00127         case dods_data:
00128         default: {
00129             // Parse the DDS; throw an exception on error.
00130             data.parse(rs->get_stream());
00131 #if FILE_UN_MARSHALLER
00132             XDRFileUnMarshaller um(rs->get_stream());
00133 #else
00134             fpistream in ( rs->get_stream() );
00135             XDRStreamUnMarshaller um( in );
00136 #endif
00137             // Load the DDS with data.
00138             for (DDS::Vars_iter i = data.var_begin(); i != data.var_end(); i++) {
00139                 (*i)->deserialize(um, &data);
00140             }
00141             return;
00142         }
00143     }
00144 }
00145 
00148 void Connect::process_data(DDS &data, Response *rs)
00149 {
00150     DBG(cerr << "Entering Connect::process_data" << endl);
00151 
00152     // TODO is this the correct info?
00153     data.set_dap_version(rs->get_protocol());
00154 
00155     DBG(cerr << "Entering process_data: d_stream = " << rs << endl);
00156     switch (rs->get_type()) {
00157     case dods_error: {
00158         Error e;
00159         if (!e.parse(rs->get_stream()))
00160             throw InternalErr(__FILE__, __LINE__, "Could not parse the Error object returned by the server!");
00161         throw e;
00162     }
00163 
00164     case web_error:
00165         // Web errors (those reported in the return document's MIME header)
00166         // are processed by the WWW library.
00167         throw InternalErr(__FILE__, __LINE__,
00168             "An error was reported by the remote httpd; this should have been processed by HTTPConnect.");
00169 
00170         // FIXME: The following case is never used. There is no such response. jhrg 10/20/15
00171     case dods_data_ddx: {
00172         // Parse the DDX; throw an exception on error.
00173         DDXParser ddx_parser(data.get_factory());
00174 
00175         // Read the MPM boundary and then read the subsequent headers
00176         string boundary = read_multipart_boundary(rs->get_stream());
00177         DBG(cerr << "MPM Boundary: " << boundary << endl);
00178         read_multipart_headers(rs->get_stream(), "text/xml", dods_ddx);
00179 
00180         // Parse the DDX, reading up to and including the next boundary.
00181         // Return the CID for the matching data part
00182         string data_cid;
00183         ddx_parser.intern_stream(rs->get_stream(), &data, data_cid, boundary);
00184 
00185         // Munge the CID into something we can work with
00186         data_cid = cid_to_header_value(data_cid);
00187         DBG(cerr << "Data CID: " << data_cid << endl);
00188 
00189         // Read the data part's MPM part headers (boundary was read by
00190         // DDXParse::intern)
00191         read_multipart_headers(rs->get_stream(), "application/octet-stream", dap4_data, data_cid);
00192 
00193         // Now read the data
00194         XDRFileUnMarshaller um(rs->get_stream());
00195         for (DDS::Vars_iter i = data.var_begin(); i != data.var_end(); i++) {
00196             (*i)->deserialize(um, &data);
00197         }
00198         return;
00199     }
00200 
00201     case dods_data:
00202     default: {
00203         // Parse the DDS; throw an exception on error.
00204         data.parse(rs->get_stream());
00205 
00206         XDRFileUnMarshaller um(rs->get_stream());
00207 
00208         // Load the DDS with data.
00209         for (DDS::Vars_iter i = data.var_begin(); i != data.var_end(); i++) {
00210             (*i)->deserialize(um, &data);
00211         }
00212         return;
00213     }
00214     }
00215 }
00216 
00217 // Barely a parser... This is used when reading from local sources of DODS
00218 // Data objects. It simulates the important actions of the libwww MIME header
00219 // parser. Those actions fill in certain fields in the Connect object. jhrg
00220 // 5/20/97
00221 //
00222 // Make sure that this parser reads from data_source without disturbing the
00223 // information in data_source that follows the MIME header. Since the DDS
00224 // (which follows the MIME header) is parsed by a flex/bison scanner/parser,
00225 // make sure to use I/O calls that will mesh with ANSI C I/O calls. In the
00226 // old GNU libg++, the C++ calls were synchronized with the C calls, but that
00227 // may no longer be the case. 5/31/99 jhrg
00228 
00238 void Connect::parse_mime(Response *rs)
00239 {
00240     rs->set_version("dods/0.0"); // initial value; for backward compatibility.
00241     rs->set_protocol("2.0");
00242 
00243     FILE *data_source = rs->get_stream();
00244     string mime = get_next_mime_header(data_source);
00245     while (!mime.empty()) {
00246         string header, value;
00247         parse_mime_header(mime, header, value);
00248 
00249         // Note that this is an ordered list
00250         if (header == "content-description:") {
00251             DBG(cout << header << ": " << value << endl);
00252             rs->set_type(get_description_type(value));
00253         }
00254         // Use the value of xdods-server only if no other value has been read
00255         else if (header == "xdods-server:" && rs->get_version() == "dods/0.0") {
00256             DBG(cout << header << ": " << value << endl);
00257             rs->set_version(value);
00258         }
00259         // This trumps 'xdods-server' and 'server'
00260         else if (header == "xopendap-server:") {
00261             DBG(cout << header << ": " << value << endl);
00262             rs->set_version(value);
00263         }
00264         else if (header == "xdap:") {
00265             DBG(cout << header << ": " << value << endl);
00266             rs->set_protocol(value);
00267         }
00268         // Only look for 'server' if no other header supplies this info.
00269         else if (rs->get_version() == "dods/0.0" && header == "server:") {
00270             DBG(cout << header << ": " << value << endl);
00271             rs->set_version(value);
00272         }
00273 
00274         mime = get_next_mime_header(data_source);
00275     }
00276 }
00277 
00278 // public mfuncs
00279 
00287 Connect::Connect(const string &n, string uname, string password) :
00288         d_http(0), d_version("unknown"), d_protocol("2.0")
00289 {
00290     string name = prune_spaces(n);
00291 
00292     // Figure out if the URL starts with 'http', if so, make sure that we
00293     // talk to an instance of HTTPConnect.
00294     if (name.find("http") == 0) {
00295         DBG(cerr << "Connect: The identifier is an http URL" << endl);
00296         d_http = new HTTPConnect(RCReader::instance());
00297 
00298         // Find and store any CE given with the URL.
00299         string::size_type dotpos = name.find('?');
00300         if (dotpos != name.npos) {
00301             _URL = name.substr(0, dotpos);
00302             string expr = name.substr(dotpos + 1);
00303 
00304             dotpos = expr.find('&');
00305             if (dotpos != expr.npos) {
00306                 _proj = expr.substr(0, dotpos);
00307                 _sel = expr.substr(dotpos); // XXX includes '&'
00308             }
00309             else {
00310                 _proj = expr;
00311                 _sel = "";
00312             }
00313         }
00314         else {
00315             _URL = name;
00316             _proj = "";
00317             _sel = "";
00318         }
00319 
00320         _local = false;
00321     }
00322     else {
00323         DBG(cerr << "Connect: The identifier is a local data source." << endl);
00324 
00325         d_http = 0;
00326         _URL = "";
00327         _local = true; // local in this case means non-DAP
00328     }
00329 
00330     set_credentials(uname, password);
00331 }
00332 
00333 Connect::~Connect()
00334 {
00335     DBG2(cerr << "Entering the Connect dtor" << endl);
00336 
00337     if (d_http)
00338         delete d_http;
00339     d_http = 0;
00340 
00341     DBG2(cerr << "Leaving the Connect dtor" << endl);
00342 }
00343 
00351 string Connect::request_version()
00352 {
00353     string version_url = _URL + ".ver";
00354     if (_proj.length() + _sel.length())
00355         version_url = version_url + "?" + id2www_ce(_proj + _sel);
00356 
00357     Response *rs = 0;
00358     try {
00359         rs = d_http->fetch_url(version_url);
00360     }
00361     catch (Error &e) {
00362         delete rs;
00363         rs = 0;
00364         throw;
00365     }
00366 
00367     d_version = rs->get_version();
00368     d_protocol = rs->get_protocol();
00369 
00370     delete rs;
00371     rs = 0;
00372 
00373     return d_version;
00374 }
00375 
00387 string Connect::request_protocol()
00388 {
00389     string version_url = _URL + ".ver";
00390     if (_proj.length() + _sel.length())
00391         version_url = version_url + "?" + id2www_ce(_proj + _sel);
00392 
00393     Response *rs = 0;
00394     try {
00395         rs = d_http->fetch_url(version_url);
00396     }
00397     catch (Error &e) {
00398         delete rs;
00399         rs = 0;
00400         throw;
00401     }
00402 
00403     d_version = rs->get_version();
00404     d_protocol = rs->get_protocol();
00405 
00406     delete rs;
00407     rs = 0;
00408 
00409     return d_protocol;
00410 }
00411 
00419 void Connect::request_das(DAS &das)
00420 {
00421     string das_url = _URL + ".das";
00422     if (_proj.length() + _sel.length())
00423         das_url = das_url + "?" + id2www_ce(_proj + _sel);
00424 
00425     Response *rs = 0;
00426     try {
00427         rs = d_http->fetch_url(das_url);
00428     }
00429     catch (Error &e) {
00430         delete rs;
00431         rs = 0;
00432         throw;
00433     }
00434 
00435     d_version = rs->get_version();
00436     d_protocol = rs->get_protocol();
00437 
00438     switch (rs->get_type()) {
00439         case dods_error: {
00440             Error e;
00441             if (!e.parse(rs->get_stream())) {
00442                 delete rs;
00443                 rs = 0;
00444                 throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
00445             }
00446             delete rs;
00447             rs = 0;
00448             throw e;
00449         }
00450 
00451         case web_error:
00452             // We should never get here; a web error should be picked up read_url
00453             // (called by fetch_url) and result in a thrown Error object.
00454             break;
00455 
00456         case dods_das:
00457         default:
00458             // DAS::parse throws an exception on error.
00459             try {
00460                 das.parse(rs->get_stream()); // read and parse the das from a file
00461             }
00462             catch (Error &e) {
00463                 delete rs;
00464                 rs = 0;
00465                 throw;
00466             }
00467 
00468             break;
00469     }
00470 
00471     delete rs;
00472     rs = 0;
00473 }
00474 
00485 void Connect::request_das_url(DAS &das)
00486 {
00487     string use_url = _URL + "?" + _proj + _sel;
00488     Response *rs = 0;
00489     try {
00490         rs = d_http->fetch_url(use_url);
00491     }
00492     catch (Error &e) {
00493         delete rs;
00494         rs = 0;
00495         throw;
00496     }
00497 
00498     d_version = rs->get_version();
00499     d_protocol = rs->get_protocol();
00500 
00501     switch (rs->get_type()) {
00502         case dods_error: {
00503             Error e;
00504             if (!e.parse(rs->get_stream())) {
00505                 delete rs;
00506                 rs = 0;
00507                 throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
00508             }
00509             delete rs;
00510             rs = 0;
00511             throw e;
00512         }
00513 
00514         case web_error:
00515             // We should never get here; a web error should be picked up read_url
00516             // (called by fetch_url) and result in a thrown Error object.
00517             break;
00518 
00519         case dods_das:
00520         default:
00521             // DAS::parse throws an exception on error.
00522             try {
00523                 das.parse(rs->get_stream()); // read and parse the das from a file
00524             }
00525             catch (Error &e) {
00526                 delete rs;
00527                 rs = 0;
00528                 throw;
00529             }
00530 
00531             break;
00532     }
00533 
00534     delete rs;
00535     rs = 0;
00536 }
00537 
00551 void Connect::request_dds(DDS &dds, string expr)
00552 {
00553     string proj, sel;
00554     string::size_type dotpos = expr.find('&');
00555     if (dotpos != expr.npos) {
00556         proj = expr.substr(0, dotpos);
00557         sel = expr.substr(dotpos);
00558     }
00559     else {
00560         proj = expr;
00561         sel = "";
00562     }
00563 
00564     string dds_url = _URL + ".dds" + "?" + id2www_ce(_proj + proj + _sel + sel);
00565 
00566     Response *rs = 0;
00567     try {
00568         rs = d_http->fetch_url(dds_url);
00569     }
00570     catch (Error &e) {
00571         delete rs;
00572         rs = 0;
00573         throw;
00574     }
00575 
00576     d_version = rs->get_version();
00577     d_protocol = rs->get_protocol();
00578 
00579     switch (rs->get_type()) {
00580         case dods_error: {
00581             Error e;
00582             if (!e.parse(rs->get_stream())) {
00583                 delete rs;
00584                 rs = 0;
00585                 throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
00586             }
00587             delete rs;
00588             rs = 0;
00589             throw e;
00590         }
00591 
00592         case web_error:
00593             // We should never get here; a web error should be picked up read_url
00594             // (called by fetch_url) and result in a thrown Error object.
00595             break;
00596 
00597         case dods_dds:
00598         default:
00599             // DDS::prase throws an exception on error.
00600             try {
00601                 dds.parse(rs->get_stream()); // read and parse the dds from a file
00602             }
00603             catch (Error &e) {
00604                 delete rs;
00605                 rs = 0;
00606                 throw;
00607             }
00608             break;
00609     }
00610 
00611     delete rs;
00612     rs = 0;
00613 }
00614 
00631 void Connect::request_dds_url(DDS &dds)
00632 {
00633     string use_url = _URL + "?" + _proj + _sel;
00634     Response *rs = 0;
00635     try {
00636         rs = d_http->fetch_url(use_url);
00637     }
00638     catch (Error &e) {
00639         delete rs;
00640         rs = 0;
00641         throw;
00642     }
00643 
00644     d_version = rs->get_version();
00645     d_protocol = rs->get_protocol();
00646 
00647     switch (rs->get_type()) {
00648         case dods_error: {
00649             Error e;
00650             if (!e.parse(rs->get_stream())) {
00651                 delete rs;
00652                 rs = 0;
00653                 throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
00654             }
00655             delete rs;
00656             rs = 0;
00657             throw e;
00658         }
00659 
00660         case web_error:
00661             // We should never get here; a web error should be picked up read_url
00662             // (called by fetch_url) and result in a thrown Error object.
00663             break;
00664 
00665         case dods_dds:
00666         default:
00667             // DDS::prase throws an exception on error.
00668             try {
00669                 dds.parse(rs->get_stream()); // read and parse the dds from a file
00670             }
00671             catch (Error &e) {
00672                 delete rs;
00673                 rs = 0;
00674                 throw;
00675             }
00676             break;
00677     }
00678 
00679     delete rs;
00680     rs = 0;
00681 }
00682 
00694 void Connect::request_ddx(DDS &dds, string expr)
00695 {
00696     string proj, sel;
00697     string::size_type dotpos = expr.find('&');
00698     if (dotpos != expr.npos) {
00699         proj = expr.substr(0, dotpos);
00700         sel = expr.substr(dotpos);
00701     }
00702     else {
00703         proj = expr;
00704         sel = "";
00705     }
00706 
00707     string ddx_url = _URL + ".ddx" + "?" + id2www_ce(_proj + proj + _sel + sel);
00708 
00709     Response *rs = 0;
00710     try {
00711         rs = d_http->fetch_url(ddx_url);
00712     }
00713     catch (Error &e) {
00714         delete rs;
00715         throw;
00716     }
00717 
00718     d_version = rs->get_version();
00719     d_protocol = rs->get_protocol();
00720 
00721     switch (rs->get_type()) {
00722         case dods_error: {
00723             Error e;
00724             if (!e.parse(rs->get_stream())) {
00725                 delete rs;
00726                 rs = 0;
00727                 throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
00728             }
00729             delete rs;
00730             throw e;
00731         }
00732 
00733         case web_error:
00734             // We should never get here; a web error should be picked up read_url
00735             // (called by fetch_url) and result in a thrown Error object.
00736             break;
00737 
00738         case dods_ddx:
00739             try {
00740                 string blob;
00741 
00742                 DDXParser ddxp(dds.get_factory());
00743                 ddxp.intern_stream(rs->get_stream(), &dds, blob);
00744             }
00745             catch (Error &e) {
00746                 delete rs;
00747                 throw;
00748             }
00749             break;
00750 
00751         default:
00752             ObjectType ot = rs->get_type();
00753             delete rs;
00754             throw Error("Invalid response type when requesting a DDX response. Response type: " + long_to_string(ot));
00755     }
00756 
00757     delete rs;
00758 }
00759 
00762 void Connect::request_ddx_url(DDS &dds)
00763 {
00764     string use_url = _URL + "?" + _proj + _sel;
00765 
00766     Response *rs = 0;
00767     try {
00768         rs = d_http->fetch_url(use_url);
00769     }
00770     catch (Error &e) {
00771         delete rs;
00772         throw;
00773     }
00774 
00775     d_version = rs->get_version();
00776     d_protocol = rs->get_protocol();
00777 
00778     switch (rs->get_type()) {
00779         case dods_error: {
00780             Error e;
00781             if (!e.parse(rs->get_stream())) {
00782                 delete rs;
00783                 throw InternalErr(__FILE__, __LINE__, "Could not parse error returned from server.");
00784             }
00785             delete rs;
00786             throw e;
00787         }
00788 
00789         case web_error:
00790             // We should never get here; a web error should be picked up read_url
00791             // (called by fetch_url) and result in a thrown Error object.
00792             delete rs;
00793             throw InternalErr(__FILE__, __LINE__, "Web error.");
00794 
00795         case dods_ddx:
00796             try {
00797                 string blob;
00798 
00799                 DDXParser ddxp(dds.get_factory());
00800                 ddxp.intern_stream(rs->get_stream(), &dds, blob);
00801             }
00802             catch (Error &e) {
00803                 delete rs;
00804                 throw;
00805             }
00806             break;
00807 
00808         default: {
00809             ObjectType ot = rs->get_type();
00810             delete rs;
00811 
00812             throw Error("Invalid response type when requesting a DDX response. Response type: " + long_to_string(ot));
00813         }
00814     }
00815 
00816     delete rs;
00817 }
00818 
00834 void Connect::request_data(DataDDS &data, string expr)
00835 {
00836     string proj, sel;
00837     string::size_type dotpos = expr.find('&');
00838     if (dotpos != expr.npos) {
00839         proj = expr.substr(0, dotpos);
00840         sel = expr.substr(dotpos);
00841     }
00842     else {
00843         proj = expr;
00844         sel = "";
00845     }
00846 
00847     string data_url = _URL + ".dods?" + id2www_ce(_proj + proj + _sel + sel);
00848 
00849     Response *rs = 0;
00850     // We need to catch Error exceptions to ensure calling close_output.
00851     try {
00852         rs = d_http->fetch_url(data_url);
00853 
00854         d_version = rs->get_version();
00855         d_protocol = rs->get_protocol();
00856 
00857         process_data(data, rs);
00858         delete rs;
00859         rs = 0;
00860     }
00861     catch (Error &e) {
00862         delete rs;
00863         rs = 0;
00864         throw;
00865     }
00866 }
00867 
00885 void Connect::request_data_url(DataDDS &data)
00886 {
00887     string use_url = _URL + "?" + _proj + _sel;
00888     Response *rs = 0;
00889     // We need to catch Error exceptions to ensure calling close_output.
00890     try {
00891         rs = d_http->fetch_url(use_url);
00892 
00893         d_version = rs->get_version();
00894         d_protocol = rs->get_protocol();
00895 
00896         process_data(data, rs);
00897         delete rs;
00898         rs = 0;
00899     }
00900     catch (Error &e) {
00901         delete rs;
00902         rs = 0;
00903         throw;
00904     }
00905 }
00906 
00907 // FIXME Unused?
00908 void Connect::request_data_ddx(DataDDS &data, string expr)
00909 {
00910     string proj, sel;
00911     string::size_type dotpos = expr.find('&');
00912     if (dotpos != expr.npos) {
00913         proj = expr.substr(0, dotpos);
00914         sel = expr.substr(dotpos);
00915     }
00916     else {
00917         proj = expr;
00918         sel = "";
00919     }
00920 
00921     string data_url = _URL + ".dap?" + id2www_ce(_proj + proj + _sel + sel);
00922 
00923     Response *rs = 0;
00924     // We need to catch Error exceptions to ensure calling close_output.
00925     try {
00926         rs = d_http->fetch_url(data_url);
00927 
00928         d_version = rs->get_version();
00929         d_protocol = rs->get_protocol();
00930 
00931         process_data(data, rs);
00932         delete rs;
00933         rs = 0;
00934     }
00935     catch (Error &e) {
00936         delete rs;
00937         rs = 0;
00938         throw;
00939     }
00940 }
00941 
00942 // FIXME Unused?
00943 void Connect::request_data_ddx_url(DataDDS &data)
00944 {
00945     string use_url = _URL + "?" + _proj + _sel;
00946     Response *rs = 0;
00947     // We need to catch Error exceptions to ensure calling close_output.
00948     try {
00949         rs = d_http->fetch_url(use_url);
00950 
00951         d_version = rs->get_version();
00952         d_protocol = rs->get_protocol();
00953 
00954         process_data(data, rs);
00955         delete rs;
00956         rs = 0;
00957     }
00958     catch (Error &e) {
00959         delete rs;
00960         rs = 0;
00961         throw;
00962     }
00963 }
00964 
00978 void Connect::read_data(DataDDS &data, Response *rs)
00979 {
00980     if (!rs)
00981         throw InternalErr(__FILE__, __LINE__, "Response object is null.");
00982 
00983     // Read from data_source and parse the MIME headers specific to DAP2/4.
00984     parse_mime(rs);
00985 
00986     read_data_no_mime(data, rs);
00987 }
00988 void
00989 Connect::read_data(DDS &data, Response *rs)
00990 {
00991     if (!rs)
00992         throw InternalErr(__FILE__, __LINE__, "Response object is null.");
00993 
00994     // Read from data_source and parse the MIME headers specific to DAP2/4.
00995     parse_mime(rs);
00996 
00997     read_data_no_mime(data, rs);
00998 }
00999 
01000 // This function looks at the input stream and makes its best guess at what
01001 // lies in store for downstream processing code. Definitely heuristic.
01002 // Assumptions:
01003 // #1 The current file position is past any MIME headers (if they were present).
01004 // #2 We must reset the FILE* position to the start of the DDS or DDX headers
01005 static void divine_type_information(Response *rs)
01006 {
01007     // Consume whitespace
01008     int c = getc(rs->get_stream());
01009     while (!feof(rs->get_stream()) && !ferror(rs->get_stream()) && isspace(c)) {
01010         c = getc(rs->get_stream());
01011     }
01012 
01013 
01014     if (ferror(rs->get_stream()))
01015         throw Error("Error reading response type information: " + string(strerror(errno)));
01016     if (feof(rs->get_stream()))
01017         throw Error("Error reading response type information: Found EOF");
01018 
01019     // The heuristic here is that a DataDDX is a multipart MIME document and
01020     // The first non space character found after the headers is the start of
01021     // the first part which looks like '--<boundary>' while a DataDDS starts
01022     // with a DDS (;Dataset {' ...). I take into account that our parsers have
01023     // accepted both 'Dataset' and 'dataset' for a long time.
01024     switch (c) {
01025         case '-':
01026             rs->set_type(dods_data_ddx);
01027             break;
01028         case 'D':
01029         case 'd':
01030             rs->set_type(dods_data);
01031             break;
01032         default:
01033             throw InternalErr(__FILE__, __LINE__, "Could not determine type of response object in stream.");
01034     }
01035 
01036     ungetc(c, rs->get_stream());
01037 }
01038 
01051 void Connect::read_data_no_mime(DataDDS &data, Response *rs)
01052 {
01053     if (rs->get_type() == unknown_type)
01054         divine_type_information(rs);
01055 
01056     switch (rs->get_type()) {
01057         case dods_data:
01058             d_version = rs->get_version();
01059             d_protocol = rs->get_protocol();
01060             process_data(data, rs);
01061             break;
01062         case dods_data_ddx:
01063             process_data(data, rs);
01064             d_version = rs->get_version();
01065             d_protocol = data.get_protocol();
01066             break;
01067         default:
01068             throw InternalErr(__FILE__, __LINE__, "Should have been a DataDDS or DataDDX.");
01069     }
01070 }
01071 void Connect::read_data_no_mime(DDS &data, Response *rs)
01072 {
01073     if (rs->get_type() == unknown_type)
01074         divine_type_information(rs);
01075 
01076     switch (rs->get_type()) {
01077     case dods_data:
01078         d_version = rs->get_version();
01079         d_protocol = rs->get_protocol();
01080         process_data(data, rs);
01081         break;
01082     case dods_data_ddx:
01083         process_data(data, rs);
01084         d_version = rs->get_version();
01085         // TODO should check to see if this hack is a correct replacement
01086         // for get_protocol from DataDDS
01087         d_protocol = data.get_dap_version();
01088         break;
01089     default:
01090         throw InternalErr(__FILE__, __LINE__, "Should have been a DataDDS or DataDDX.");
01091     }
01092 }
01093 
01094 bool
01095 Connect::is_local()
01096 {
01097     return _local;
01098 }
01099 
01116 string Connect::URL(bool ce)
01117 {
01118     if (_local)
01119         throw InternalErr(__FILE__, __LINE__, "URL(): This call is only valid for a DAP data source.");
01120 
01121     if (ce)
01122         return _URL + "?" + _proj + _sel;
01123     else
01124         return _URL;
01125 }
01126 
01135 string Connect::CE()
01136 {
01137     if (_local)
01138         throw InternalErr(__FILE__, __LINE__, "CE(): This call is only valid for a DAP data source.");
01139 
01140     return _proj + _sel;
01141 }
01142 
01148 void Connect::set_credentials(string u, string p)
01149 {
01150     if (d_http)
01151         d_http->set_credentials(u, p);
01152 }
01153 
01157 void Connect::set_accept_deflate(bool deflate)
01158 {
01159     if (d_http)
01160         d_http->set_accept_deflate(deflate);
01161 }
01162 
01168 void Connect::set_xdap_protocol(int major, int minor)
01169 {
01170     if (d_http)
01171         d_http->set_xdap_protocol(major, minor);
01172 }
01173 
01177 void Connect::set_cache_enabled(bool cache)
01178 {
01179     if (d_http)
01180         d_http->set_cache_enabled(cache);
01181 }
01182 
01183 bool Connect::is_cache_enabled()
01184 {
01185     bool status;
01186     DBG(cerr << "Entering is_cache_enabled (" << hex << d_http << dec
01187             << ")... ");
01188     if (d_http)
01189         status = d_http->is_cache_enabled();
01190     else
01191         status = false;
01192     DBGN(cerr << "exiting" << endl);
01193     return status;
01194 }
01195 
01196 } // namespace libdap