libdap
Updated for version 3.17.0
|
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