libdap++  Updated for version 3.8.2
Connect.cc
Go to the documentation of this file.
00001 
00002 // -*- mode: c++; c-basic-offset:4 -*-
00003 
00004 // This file is part of libdap, A C++ implementation of the OPeNDAP Data
00005 // Access Protocol.
00006 
00007 // Copyright (c) 2002,2003 OPeNDAP, Inc.
00008 // Author: James Gallagher <jgallagher@opendap.org>
00009 //         Dan Holloway <dholloway@gso.uri.edu>
00010 //         Reza Nekovei <reza@intcomm.net>
00011 //
00012 // This library is free software; you can redistribute it and/or
00013 // modify it under the terms of the GNU Lesser General Public
00014 // License as published by the Free Software Foundation; either
00015 // version 2.1 of the License, or (at your option) any later version.
00016 //
00017 // This library is distributed in the hope that it will be useful,
00018 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00019 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00020 // Lesser General Public License for more details.
00021 //
00022 // You should have received a copy of the GNU Lesser General Public
00023 // License along with this library; if not, write to the Free Software
00024 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00025 //
00026 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
00027 
00028 // (c) COPYRIGHT URI/MIT 1994-2002
00029 // Please read the full copyright statement in the file COPYRIGHT_URI.
00030 //
00031 // Authors:
00032 //      jhrg,jimg       James Gallagher <jgallagher@gso.uri.edu>
00033 //      dan             Dan Holloway <dholloway@gso.uri.edu>
00034 //      reza            Reza Nekovei <reza@intcomm.net>
00035 
00036 
00037 #include "config.h"
00038 
00039 //#define DODS_DEBUG
00040 #define FILE_METHODS 1
00041 
00042 static char rcsid[] not_used =
00043     { "$Id: Connect.cc 24370 2011-03-28 16:21:32Z jimg $"
00044     };
00045 
00046 #include <cstring>
00047 #include <fstream>
00048 #include <algorithm>
00049 
00050 #include "debug.h"
00051 #include "DataDDS.h"
00052 #include "Connect.h"
00053 #include "escaping.h"
00054 #include "RCReader.h"
00055 #include "DDXParserSAX2.h"
00056 #if FILE_METHODS
00057 #include "XDRFileUnMarshaller.h"
00058 #else
00059 #include "fdiostream.h"
00060 #include "XDRStreamUnMarshaller.h"
00061 #endif
00062 #include "mime_util.h"
00063 
00064 using std::cerr;
00065 using std::endl;
00066 using std::ifstream;
00067 using std::ofstream;
00068 using std::min;
00069 
00070 namespace libdap {
00071 
00074 void
00075 Connect::process_data(DataDDS &data, Response *rs)
00076 {
00077     DBG(cerr << "Entering Connect::process_data" << endl);
00078 
00079     data.set_version(rs->get_version());
00080     data.set_protocol(rs->get_protocol());
00081 
00082     DBG(cerr << "Entering process_data: d_stream = " << rs << endl);
00083     switch (rs->get_type()) {
00084     case dods_error: {
00085             Error e;
00086             if (!e.parse(rs->get_stream()))
00087                 throw InternalErr(__FILE__, __LINE__,
00088                                   "Could not parse the Error object returned by the server!");
00089             throw e;
00090         }
00091 
00092     case web_error:
00093         // Web errors (those reported in the return document's MIME header)
00094         // are processed by the WWW library.
00095         throw InternalErr(__FILE__, __LINE__, "An error was reported by the remote httpd; this should have been processed by HTTPConnect..");
00096 
00097     case dap4_data_ddx: {
00098             // Parse the DDX; throw an exception on error.
00099             DDXParser ddx_parser(data.get_factory());
00100 
00101             // Read the MPM boundary and then read the subsequent headers
00102             string boundary = read_multipart_boundary(rs->get_stream());
00103             DBG(cerr << "MPM Boundary: " << boundary << endl);
00104             read_multipart_headers(rs->get_stream(), "text/xml", dap4_ddx);
00105 
00106             // Parse the DDX, reading up to and including the next boundary.
00107             // Return the CID for the matching data part
00108             string data_cid;
00109             ddx_parser.intern_stream(rs->get_stream(), &data, data_cid, boundary);
00110 
00111             // Munge the CID into something we can work with
00112             data_cid = cid_to_header_value(data_cid);
00113             DBG(cerr << "Data CID: " << data_cid << endl);
00114 
00115             // Read the data part's MPM part headers (boundary was read by
00116             // DDXParse::intern)
00117             read_multipart_headers(rs->get_stream(),
00118                     "application/octet-stream", dap4_data, data_cid);
00119 
00120             // Now read the data
00121 #if FILE_METHODS
00122             XDRFileUnMarshaller um( rs->get_stream() ) ;
00123 #else
00124             fpistream in ( rs->get_stream() );
00125             XDRStreamUnMarshaller um( in ) ;
00126 #endif
00127 #if 0
00128             try {
00129 #endif
00130                 for (DDS::Vars_iter i = data.var_begin(); i != data.var_end();
00131                      i++) {
00132                     (*i)->deserialize(um, &data);
00133                 }
00134 #if 0
00135             }
00136             catch (Error &e) {
00137                 throw ;
00138             }
00139 #endif
00140             return;
00141         }
00142 
00143     case dods_data:
00144     default: {
00145             // Parse the DDS; throw an exception on error.
00146             data.parse(rs->get_stream());
00147 #if FILE_METHODS
00148             XDRFileUnMarshaller um( rs->get_stream() ) ;
00149 #else
00150             fpistream in ( rs->get_stream() );
00151             XDRStreamUnMarshaller um( in ) ;
00152 #endif
00153             // Load the DDS with data.
00154 #if 0
00155             try {
00156 #endif
00157                 for (DDS::Vars_iter i = data.var_begin(); i != data.var_end();
00158                      i++) {
00159                     (*i)->deserialize(um, &data);
00160                 }
00161 #if 0
00162             }
00163             catch (Error &e) {
00164                 throw ;
00165             }
00166 #endif
00167             return;
00168         }
00169     }
00170 }
00171 
00172 // Barely a parser... This is used when reading from local sources of DODS
00173 // Data objects. It simulates the important actions of the libwww MIME header
00174 // parser. Those actions fill in certain fields in the Connect object. jhrg
00175 // 5/20/97
00176 //
00177 // Make sure that this parser reads from data_source without disturbing the
00178 // information in data_source that follows the MIME header. Since the DDS
00179 // (which follows the MIME header) is parsed by a flex/bison scanner/parser,
00180 // make sure to use I/O calls that will mesh with ANSI C I/O calls. In the
00181 // old GNU libg++, the C++ calls were synchronized with the C calls, but that
00182 // may no longer be the case. 5/31/99 jhrg
00183 
00193 void
00194 Connect::parse_mime(Response *rs)
00195 {
00196     rs->set_version("dods/0.0"); // initial value; for backward compatibility.
00197     rs->set_protocol("2.0");
00198 
00199     FILE *data_source = rs->get_stream();
00200     string mime = get_next_mime_header(data_source);
00201     while (!mime.empty()) {
00202         string header, value;
00203         parse_mime_header(mime, header, value);
00204 
00205         // Note that this is an ordered list
00206         if (header == "content-description:") {
00207             DBG(cout << header << ": " << value << endl);
00208             rs->set_type(get_description_type(value));
00209         }
00210         // Use the value of xdods-server only if no other value has been read
00211         else if (header == "xdods-server:"
00212                  && rs->get_version() == "dods/0.0") {
00213             DBG(cout << header << ": " << value << endl);
00214             rs->set_version(value);
00215         }
00216         // This trumps 'xdods-server' and 'server'
00217         else if (header == "xopendap-server:") {
00218             DBG(cout << header << ": " << value << endl);
00219             rs->set_version(value);
00220         }
00221         else if (header == "xdap:") {
00222             DBG(cout << header << ": " << value << endl);
00223             rs->set_protocol(value);
00224         }
00225         // Only look for 'server' if no other header supplies this info.
00226         else if (rs->get_version() == "dods/0.0" && header == "server:") {
00227             DBG(cout << header << ": " << value << endl);
00228             rs->set_version(value);
00229         }
00230 
00231         mime = get_next_mime_header(data_source);
00232     }
00233 }
00234 
00235 // public mfuncs
00236 
00244 Connect::Connect(const string &n, string uname, string password)
00245 throw(Error, InternalErr)
00246         : d_http(0), d_version("unknown"), d_protocol("2.0")
00247 {
00248     string name = prune_spaces(n);
00249 
00250     // Figure out if the URL starts with 'http', if so, make sure that we
00251     // talk to an instance of HTTPConnect.
00252     if (name.find("http") == 0) {
00253         DBG(cerr << "Connect: The identifier is an http URL" << endl);
00254         d_http = new HTTPConnect(RCReader::instance());
00255 
00256         // Find and store any CE given with the URL.
00257         string::size_type dotpos = name.find('?');
00258         if (dotpos != name.npos) {
00259             _URL = name.substr(0, dotpos);
00260             string expr = name.substr(dotpos + 1);
00261 
00262             dotpos = expr.find('&');
00263             if (dotpos != expr.npos) {
00264                 _proj = expr.substr(0, dotpos);
00265                 _sel = expr.substr(dotpos); // XXX includes '&'
00266             }
00267             else {
00268                 _proj = expr;
00269                 _sel = "";
00270             }
00271         }
00272         else {
00273             _URL = name;
00274             _proj = "";
00275             _sel = "";
00276         }
00277 
00278         _local = false;
00279     }
00280     else {
00281         DBG(cerr << "Connect: The identifier is a local data source." << endl);
00282 
00283         d_http = 0;
00284         _URL = "";
00285         _local = true;  // local in this case means non-DAP
00286     }
00287 
00288     set_credentials(uname, password);
00289 }
00290 
00291 Connect::~Connect()
00292 {
00293     DBG2(cerr << "Entering the Connect dtor" << endl);
00294 
00295     if (d_http)
00296         delete d_http; d_http = 0;
00297 
00298     DBG2(cerr << "Leaving the Connect dtor" << endl);
00299 }
00300 
00308 string
00309 Connect::request_version()
00310 {
00311     string version_url = _URL + ".ver";
00312     if (_proj.length() + _sel.length())
00313         version_url = version_url + "?" + id2www_ce(_proj + _sel);
00314 
00315     Response *rs = 0;
00316     try {
00317         rs = d_http->fetch_url(version_url);
00318     }
00319     catch (Error &e) {
00320         delete rs; rs = 0;
00321         throw ;
00322     }
00323 
00324     d_version = rs->get_version();
00325     d_protocol = rs->get_protocol();
00326 
00327     delete rs; rs = 0;
00328 
00329     return d_version;
00330 }
00331 
00343 string
00344 Connect::request_protocol()
00345 {
00346     string version_url = _URL + ".ver";
00347     if (_proj.length() + _sel.length())
00348         version_url = version_url + "?" + id2www_ce(_proj + _sel);
00349 
00350     Response *rs = 0;
00351     try {
00352         rs = d_http->fetch_url(version_url);
00353     }
00354     catch (Error &e) {
00355         delete rs; rs = 0;
00356         throw ;
00357     }
00358 
00359     d_version = rs->get_version();
00360     d_protocol = rs->get_protocol();
00361 
00362     delete rs; rs = 0;
00363 
00364     return d_protocol;
00365 }
00366 
00374 void
00375 Connect::request_das(DAS &das)
00376 {
00377     string das_url = _URL + ".das";
00378     if (_proj.length() + _sel.length())
00379         das_url = das_url + "?" + id2www_ce(_proj + _sel);
00380 
00381     Response *rs = 0;
00382     try {
00383         rs = d_http->fetch_url(das_url);
00384     }
00385     catch (Error &e) {
00386         delete rs; rs = 0;
00387         throw ;
00388     }
00389 
00390     d_version = rs->get_version();
00391     d_protocol = rs->get_protocol();
00392 
00393     switch (rs->get_type()) {
00394     case dods_error: {
00395             Error e;
00396             if (!e.parse(rs->get_stream())) {
00397                 delete rs; rs = 0;
00398                 throw InternalErr(__FILE__, __LINE__,
00399                                   "Could not parse error returned from server.");
00400             }
00401             delete rs; rs = 0;
00402             throw e;
00403         }
00404 
00405     case web_error:
00406         // We should never get here; a web error should be picked up read_url
00407         // (called by fetch_url) and result in a thrown Error object.
00408         break;
00409 
00410     case dods_das:
00411     default:
00412         // DAS::parse throws an exception on error.
00413         try {
00414             das.parse(rs->get_stream()); // read and parse the das from a file
00415         }
00416         catch (Error &e) {
00417             delete rs; rs = 0;
00418             throw ;
00419         }
00420 
00421         break;
00422     }
00423 
00424     delete rs; rs = 0;
00425 }
00426 
00437 void
00438 Connect::request_das_url(DAS &das)
00439 {
00440     string use_url = _URL + "?" + _proj + _sel ;
00441     Response *rs = 0;
00442     try {
00443         rs = d_http->fetch_url(use_url);
00444     }
00445     catch (Error &e) {
00446         delete rs; rs = 0;
00447         throw ;
00448     }
00449 
00450     d_version = rs->get_version();
00451     d_protocol = rs->get_protocol();
00452 
00453     switch (rs->get_type()) {
00454     case dods_error: {
00455             Error e;
00456             if (!e.parse(rs->get_stream())) {
00457                 delete rs; rs = 0;
00458                 throw InternalErr(__FILE__, __LINE__,
00459                                   "Could not parse error returned from server.");
00460             }
00461             delete rs; rs = 0;
00462             throw e;
00463         }
00464 
00465     case web_error:
00466         // We should never get here; a web error should be picked up read_url
00467         // (called by fetch_url) and result in a thrown Error object.
00468         break;
00469 
00470     case dods_das:
00471     default:
00472         // DAS::parse throws an exception on error.
00473         try {
00474             das.parse(rs->get_stream()); // read and parse the das from a file
00475         }
00476         catch (Error &e) {
00477             delete rs; rs = 0;
00478             throw ;
00479         }
00480 
00481         break;
00482     }
00483 
00484     delete rs; rs = 0;
00485 }
00486 
00500 void
00501 Connect::request_dds(DDS &dds, string expr)
00502 {
00503     string proj, sel;
00504     string::size_type dotpos = expr.find('&');
00505     if (dotpos != expr.npos) {
00506         proj = expr.substr(0, dotpos);
00507         sel = expr.substr(dotpos);
00508     }
00509     else {
00510         proj = expr;
00511         sel = "";
00512     }
00513 
00514     string dds_url = _URL + ".dds" + "?"
00515                      + id2www_ce(_proj + proj + _sel + sel);
00516 
00517     Response *rs = 0;
00518     try {
00519         rs = d_http->fetch_url(dds_url);
00520     }
00521     catch (Error &e) {
00522         delete rs; rs = 0;
00523         throw ;
00524     }
00525 
00526     d_version = rs->get_version();
00527     d_protocol = rs->get_protocol();
00528 
00529     switch (rs->get_type()) {
00530     case dods_error: {
00531             Error e;
00532             if (!e.parse(rs->get_stream())) {
00533                 delete rs; rs = 0;
00534                 throw InternalErr(__FILE__, __LINE__,
00535                                   "Could not parse error returned from server.");
00536             }
00537             delete rs; rs = 0;
00538             throw e;
00539         }
00540 
00541     case web_error:
00542         // We should never get here; a web error should be picked up read_url
00543         // (called by fetch_url) and result in a thrown Error object.
00544         break;
00545 
00546     case dods_dds:
00547     default:
00548         // DDS::prase throws an exception on error.
00549         try {
00550             dds.parse(rs->get_stream()); // read and parse the dds from a file
00551         }
00552         catch (Error &e) {
00553             delete rs; rs = 0;
00554             throw ;
00555         }
00556         break;
00557     }
00558 
00559     delete rs; rs = 0;
00560 }
00561 
00578 void
00579 Connect::request_dds_url(DDS &dds)
00580 {
00581     string use_url = _URL + "?" + _proj + _sel ;
00582     Response *rs = 0;
00583     try {
00584         rs = d_http->fetch_url(use_url);
00585     }
00586     catch (Error &e) {
00587         delete rs; rs = 0;
00588         throw ;
00589     }
00590 
00591     d_version = rs->get_version();
00592     d_protocol = rs->get_protocol();
00593 
00594     switch (rs->get_type()) {
00595     case dods_error: {
00596             Error e;
00597             if (!e.parse(rs->get_stream())) {
00598                 delete rs; rs = 0;
00599                 throw InternalErr(__FILE__, __LINE__,
00600                                   "Could not parse error returned from server.");
00601             }
00602             delete rs; rs = 0;
00603             throw e;
00604         }
00605 
00606     case web_error:
00607         // We should never get here; a web error should be picked up read_url
00608         // (called by fetch_url) and result in a thrown Error object.
00609         break;
00610 
00611     case dods_dds:
00612     default:
00613         // DDS::prase throws an exception on error.
00614         try {
00615             dds.parse(rs->get_stream()); // read and parse the dds from a file
00616         }
00617         catch (Error &e) {
00618             delete rs; rs = 0;
00619             throw ;
00620         }
00621         break;
00622     }
00623 
00624     delete rs; rs = 0;
00625 }
00626 
00638 void
00639 Connect::request_ddx(DDS &dds, string expr)
00640 {
00641     string proj, sel;
00642     string::size_type dotpos = expr.find('&');
00643     if (dotpos != expr.npos) {
00644         proj = expr.substr(0, dotpos);
00645         sel = expr.substr(dotpos);
00646     }
00647     else {
00648         proj = expr;
00649         sel = "";
00650     }
00651 
00652     string ddx_url = _URL + ".ddx" + "?"
00653                      + id2www_ce(_proj + proj + _sel + sel);
00654 
00655     Response *rs = 0;
00656     try {
00657         rs = d_http->fetch_url(ddx_url);
00658     }
00659     catch (Error &e) {
00660         delete rs; rs = 0;
00661         throw ;
00662     }
00663 
00664     d_version = rs->get_version();
00665     d_protocol = rs->get_protocol();
00666 
00667     switch (rs->get_type()) {
00668     case dods_error: {
00669             Error e;
00670             if (!e.parse(rs->get_stream())) {
00671                 delete rs; rs = 0;
00672                 throw InternalErr(__FILE__, __LINE__,
00673                                   "Could not parse error returned from server.");
00674             }
00675             delete rs; rs = 0;
00676             throw e;
00677         }
00678 
00679     case web_error:
00680         // We should never get here; a web error should be picked up read_url
00681         // (called by fetch_url) and result in a thrown Error object.
00682         break;
00683 
00684     case dap4_ddx:
00685     case dods_ddx:
00686         try {
00687             string blob;
00688 
00689             DDXParser ddxp(dds.get_factory());
00690             ddxp.intern_stream(rs->get_stream(), &dds, blob);
00691         }
00692         catch (Error &e) {
00693             delete rs; rs = 0;
00694             throw ;
00695         }
00696         break;
00697 
00698     default:
00699         delete rs; rs = 0;
00700         throw Error("The site did not return a valid response (it lacked the\n\
00701 expected content description header value of 'dap4-ddx' and\n\
00702 instead returned '" + long_to_string(rs->get_type()) + "').\n\
00703 This may indicate that the server at the site is not correctly\n\
00704 configured, or that the URL has changed.");
00705     }
00706 
00707     delete rs; rs = 0;
00708 }
00709 
00712 void
00713 Connect::request_ddx_url(DDS &dds)
00714 {
00715     string use_url = _URL + "?" + _proj + _sel ;
00716 
00717     Response *rs = 0;
00718     try {
00719         rs = d_http->fetch_url(use_url);
00720     }
00721     catch (Error &e) {
00722         delete rs; rs = 0;
00723         throw ;
00724     }
00725 
00726     d_version = rs->get_version();
00727     d_protocol = rs->get_protocol();
00728 
00729     switch (rs->get_type()) {
00730     case dods_error: {
00731             Error e;
00732             if (!e.parse(rs->get_stream())) {
00733                 delete rs; rs = 0;
00734                 throw InternalErr(__FILE__, __LINE__,
00735                                   "Could not parse error returned from server.");
00736             }
00737             delete rs; rs = 0;
00738             throw e;
00739         }
00740 
00741     case web_error:
00742         // We should never get here; a web error should be picked up read_url
00743         // (called by fetch_url) and result in a thrown Error object.
00744         break;
00745 
00746     case dap4_ddx:
00747     case dods_ddx:
00748         try {
00749             string blob;
00750 
00751             DDXParser ddxp(dds.get_factory());
00752             ddxp.intern_stream(rs->get_stream(), &dds, blob);
00753         }
00754         catch (Error &e) {
00755             delete rs; rs = 0;
00756             throw ;
00757         }
00758         break;
00759 
00760     default:
00761         delete rs; rs = 0;
00762         throw Error("The site did not return a valid response (it lacked the\n\
00763 expected content description header value of 'dap4-ddx' and\n\
00764 instead returned '" + long_to_string(rs->get_type()) + "').\n\
00765 This may indicate that the server at the site is not correctly\n\
00766 configured, or that the URL has changed.");
00767     }
00768 
00769     delete rs; rs = 0;
00770 }
00771 
00787 void
00788 Connect::request_data(DataDDS &data, string expr)
00789 {
00790     string proj, sel;
00791     string::size_type dotpos = expr.find('&');
00792     if (dotpos != expr.npos) {
00793         proj = expr.substr(0, dotpos);
00794         sel = expr.substr(dotpos);
00795     }
00796     else {
00797         proj = expr;
00798         sel = "";
00799     }
00800 
00801     string data_url = _URL + ".dods?"
00802                       + id2www_ce(_proj + proj + _sel + sel);
00803 
00804     Response *rs = 0;
00805     // We need to catch Error exceptions to ensure calling close_output.
00806     try {
00807         rs = d_http->fetch_url(data_url);
00808 
00809         d_version = rs->get_version();
00810         d_protocol = rs->get_protocol();
00811 
00812         process_data(data, rs);
00813         delete rs; rs = 0;
00814     }
00815     catch (Error &e) {
00816         delete rs; rs = 0;
00817         throw ;
00818     }
00819 }
00820 
00838 void
00839 Connect::request_data_url(DataDDS &data)
00840 {
00841     string use_url = _URL + "?" + _proj + _sel ;
00842     Response *rs = 0;
00843     // We need to catch Error exceptions to ensure calling close_output.
00844     try {
00845         rs = d_http->fetch_url(use_url);
00846 
00847         d_version = rs->get_version();
00848         d_protocol = rs->get_protocol();
00849 
00850         process_data(data, rs);
00851         delete rs; rs = 0;
00852     }
00853     catch (Error &e) {
00854         delete rs; rs = 0;
00855         throw ;
00856     }
00857 }
00858 
00859 void
00860 Connect::request_data_ddx(DataDDS &data, string expr)
00861 {
00862     string proj, sel;
00863     string::size_type dotpos = expr.find('&');
00864     if (dotpos != expr.npos) {
00865         proj = expr.substr(0, dotpos);
00866         sel = expr.substr(dotpos);
00867     }
00868     else {
00869         proj = expr;
00870         sel = "";
00871     }
00872 
00873     string data_url = _URL + ".dap?"
00874                       + id2www_ce(_proj + proj + _sel + sel);
00875 
00876     Response *rs = 0;
00877     // We need to catch Error exceptions to ensure calling close_output.
00878     try {
00879         rs = d_http->fetch_url(data_url);
00880 
00881         d_version = rs->get_version();
00882         d_protocol = rs->get_protocol();
00883 
00884         process_data(data, rs);
00885         delete rs; rs = 0;
00886     }
00887     catch (Error &e) {
00888         delete rs; rs = 0;
00889         throw ;
00890     }
00891 }
00892 
00893 void
00894 Connect::request_data_ddx_url(DataDDS &data)
00895 {
00896     string use_url = _URL + "?" + _proj + _sel ;
00897     Response *rs = 0;
00898     // We need to catch Error exceptions to ensure calling close_output.
00899     try {
00900         rs = d_http->fetch_url(use_url);
00901 
00902         d_version = rs->get_version();
00903         d_protocol = rs->get_protocol();
00904 
00905         process_data(data, rs);
00906         delete rs; rs = 0;
00907     }
00908     catch (Error &e) {
00909         delete rs; rs = 0;
00910         throw ;
00911     }
00912 }
00913 
00927 void
00928 Connect::read_data(DataDDS &data, Response *rs)
00929 {
00930     if (!rs)
00931         throw InternalErr(__FILE__, __LINE__, "Response object is null.");
00932 
00933     // Read from data_source and parse the MIME headers specific to DAP2/4.
00934     parse_mime(rs);
00935 
00936     read_data_no_mime(data, rs);
00937 }
00938 
00939 // This function looks at the input stream and makes its best guess at what
00940 // lies in store for downstream processing code. Definitely heuristic.
00941 // Assumptions:
00942 // #1 The current file position is past any MIME headers (if they were present).
00943 // #2 We must reset the FILE* position to the start of the DDS or DDX headers
00944 static void
00945 divine_type_information(Response *rs)
00946 {
00947     // Consume whitespace
00948     char c = getc(rs->get_stream());
00949     while (isspace(c)) {
00950         c = getc(rs->get_stream());
00951     }
00952 
00953     // The heuristic here is that a DataDDX is a multipart MIME document and
00954     // The first non space character found after the headers is the start of
00955     // the first part which looks like '--<boundary>' while a DataDDS starts
00956     // with a DDS (;Dataset {' ...). I take into account that our parsers have
00957     // accepted both 'Dataset' and 'dataset' for a long time.
00958     switch (c) {
00959     case '-':
00960         rs->set_type(dap4_data_ddx);
00961         break;
00962     case 'D':
00963     case 'd':
00964         rs->set_type(dods_data);
00965         break;
00966     default:
00967         throw InternalErr(__FILE__, __LINE__, "Could not determine type of response object in stream.");
00968     }
00969 
00970     ungetc(c, rs->get_stream());
00971 }
00972 
00985 void
00986 Connect::read_data_no_mime(DataDDS &data, Response *rs)
00987 {
00988     if (rs->get_type() == unknown_type)
00989         divine_type_information(rs);
00990 
00991     switch (rs->get_type()) {
00992     case dods_data:
00993         d_version = rs->get_version();
00994         d_protocol = rs->get_protocol();
00995         process_data(data, rs);
00996         break;
00997     case dap4_data_ddx:
00998         process_data(data, rs);
00999         d_version = rs->get_version();
01000         d_protocol = data.get_protocol();
01001         break;
01002     default:
01003         throw InternalErr(__FILE__, __LINE__, "Should have been a DataDDS or DataDDX.");
01004     }
01005 }
01006 
01007 bool
01008 Connect::is_local()
01009 {
01010     return _local;
01011 }
01012 
01029 string
01030 Connect::URL(bool ce)
01031 {
01032     if (_local)
01033         throw InternalErr(__FILE__, __LINE__,
01034                           "URL(): This call is only valid for a DAP data source.");
01035 
01036     if (ce)
01037         return _URL + "?" + _proj + _sel;
01038     else
01039         return _URL;
01040 }
01041 
01050 string
01051 Connect::CE()
01052 {
01053     if (_local)
01054         throw InternalErr(__FILE__, __LINE__,
01055                           "CE(): This call is only valid for a DAP data source.");
01056 
01057     return _proj + _sel;
01058 }
01059 
01065 void
01066 Connect::set_credentials(string u, string p)
01067 {
01068     if (d_http)
01069         d_http->set_credentials(u, p);
01070 }
01071 
01075 void
01076 Connect::set_accept_deflate(bool deflate)
01077 {
01078     if (d_http)
01079         d_http->set_accept_deflate(deflate);
01080 }
01081 
01087 void
01088 Connect::set_xdap_protocol(int major, int minor)
01089 {
01090     if (d_http)
01091         d_http->set_xdap_protocol(major, minor);
01092 }
01093 
01097 void
01098 Connect::set_cache_enabled(bool cache)
01099 {
01100     if (d_http)
01101         d_http->set_cache_enabled(cache);
01102 }
01103 
01104 bool
01105 Connect::is_cache_enabled()
01106 {
01107     bool status;
01108     DBG(cerr << "Entering is_cache_enabled (" << hex << d_http << dec
01109         << ")... ");
01110     if (d_http)
01111         status = d_http->is_cache_enabled();
01112     else
01113         status = false;
01114     DBGN(cerr << "exiting" << endl);
01115     return status;
01116 }
01117 
01118 } // namespace libdap