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 #include <cassert> 00038 #include <cstring> 00039 00040 #include "D4Connect.h" 00041 #include "HTTPConnect.h" 00042 #include "Response.h" 00043 #include "DMR.h" 00044 #include "D4Group.h" 00045 00046 #include "D4ParserSax2.h" 00047 #include "chunked_stream.h" 00048 #include "chunked_istream.h" 00049 #include "D4StreamUnMarshaller.h" 00050 00051 #include "escaping.h" 00052 #include "mime_util.h" 00053 #include "debug.h" 00054 00055 using namespace std; 00056 00057 namespace libdap { 00058 00059 00062 void D4Connect::process_dmr(DMR &dmr, Response &rs) 00063 { 00064 DBG(cerr << "Entering D4Connect::process_dmr" << endl); 00065 00066 dmr.set_dap_version(rs.get_protocol()); 00067 00068 DBG(cerr << "Entering process_data: d_stream = " << rs << endl); 00069 switch (rs.get_type()) { 00070 case dap4_error: { 00071 #if 0 00072 Error e; 00073 if (!e.parse(rs.get_stream())) 00074 throw InternalErr(__FILE__, __LINE__, "Could not parse the Error object returned by the server!"); 00075 throw e; 00076 #endif 00077 throw InternalErr(__FILE__, __LINE__, "DAP4 errors not processed yet: FIXME!"); 00078 } 00079 00080 case web_error: 00081 // Web errors (those reported in the return document's MIME header) 00082 // are processed by the WWW library. 00083 throw InternalErr(__FILE__, __LINE__, 00084 "An error was reported by the remote httpd; this should have been processed by HTTPConnect.."); 00085 00086 case dap4_dmr: { 00087 // parse the DMR 00088 try { 00089 D4ParserSax2 parser; 00090 parser.intern(*rs.get_cpp_stream(), &dmr, /*debug*/false); 00091 } 00092 catch (Error &e) { 00093 cerr << "Exception: " << e.get_error_message() << endl; 00094 return; 00095 } 00096 catch (std::exception &e) { 00097 cerr << "Exception: " << e.what() << endl; 00098 return; 00099 } 00100 catch (...) { 00101 cerr << "Exception: unknown error" << endl; 00102 return; 00103 } 00104 00105 return; 00106 } 00107 00108 default: 00109 throw Error("Unknown response type"); 00110 } 00111 } 00112 00115 void D4Connect::process_data(DMR &data, Response &rs) 00116 { 00117 DBG(cerr << "Entering D4Connect::process_data" << endl); 00118 00119 assert(rs.get_cpp_stream()); // DAP4 code uses cpp streams 00120 00121 data.set_dap_version(rs.get_protocol()); 00122 00123 DBG(cerr << "Entering process_data: d_stream = " << rs << endl); 00124 switch (rs.get_type()) { 00125 case dap4_error: { 00126 #if 0 00127 Error e; 00128 if (!e.parse(rs.get_cpp_stream())) 00129 throw InternalErr(__FILE__, __LINE__, "Could not parse the Error object returned by the server!"); 00130 throw e; 00131 #endif 00132 throw InternalErr(__FILE__, __LINE__, "DAP4 errors not processed yet: FIXME!"); 00133 } 00134 00135 case web_error: 00136 // Web errors (those reported in the return document's MIME header) 00137 // are processed by the WWW library. 00138 throw InternalErr(__FILE__, __LINE__, 00139 "An error was reported by the remote httpd; this should have been processed by HTTPConnect.."); 00140 00141 case dap4_data: { 00142 #if BYTE_ORDER_PREFIX 00143 // Read the byte-order byte; used later on 00144 char byte_order; 00145 *rs.get_cpp_stream() >> byte_order; 00146 //if (debug) cerr << "Byte order: " << ((byte_order) ? "big endian" : "little endian") << endl; 00147 #endif 00148 // get a chunked input stream 00149 #if BYTE_ORDER_PREFIX 00150 chunked_istream cis(*rs.get_cpp_stream(), 1024, byte_order); 00151 #else 00152 chunked_istream cis(*(rs.get_cpp_stream()), CHUNK_SIZE); 00153 #endif 00154 // parse the DMR, stopping when the boundary is found. 00155 try { 00156 // force chunk read 00157 // get chunk size 00158 int chunk_size = cis.read_next_chunk(); 00159 if (chunk_size < 0) 00160 throw Error("Found an unexpected end of input (EOF) while reading a DAP4 data response. (1)"); 00161 00162 // get chunk 00163 char chunk[chunk_size]; 00164 cis.read(chunk, chunk_size); 00165 // parse char * with given size 00166 D4ParserSax2 parser; 00167 // '-2' to discard the CRLF pair 00168 parser.intern(chunk, chunk_size - 2, &data, /*debug*/false); 00169 } 00170 catch (Error &e) { 00171 cerr << "Exception: " << e.get_error_message() << endl; 00172 return; 00173 } 00174 catch (std::exception &e) { 00175 cerr << "Exception: " << e.what() << endl; 00176 return; 00177 } 00178 catch (...) { 00179 cerr << "Exception: unknown error" << endl; 00180 return; 00181 } 00182 00183 #if BYTE_ORDER_PREFIX 00184 D4StreamUnMarshaller um(cis, byte_order); 00185 #else 00186 D4StreamUnMarshaller um(cis, cis.twiddle_bytes()); 00187 #endif 00188 data.root()->deserialize(um, data); 00189 00190 return; 00191 } 00192 00193 default: 00194 throw Error("Unknown response type"); 00195 } 00196 } 00197 00206 void D4Connect::parse_mime(Response &rs) 00207 { 00208 rs.set_version("dods/0.0"); // initial value; for backward compatibility. 00209 rs.set_protocol("2.0"); 00210 00211 istream &data_source = *rs.get_cpp_stream(); 00212 string mime = get_next_mime_header(data_source); 00213 while (!mime.empty()) { 00214 string header, value; 00215 parse_mime_header(mime, header, value); 00216 00217 // Note that this is an ordered list 00218 if (header == "content-description") { 00219 DBG(cout << header << ": " << value << endl); 00220 rs.set_type(get_description_type(value)); 00221 } 00222 // Use the value of xdods-server only if no other value has been read 00223 else if (header == "xdods-server" && rs.get_version() == "dods/0.0") { 00224 DBG(cout << header << ": " << value << endl); 00225 rs.set_version(value); 00226 } 00227 // This trumps 'xdods-server' and 'server' 00228 else if (header == "xopendap-server") { 00229 DBG(cout << header << ": " << value << endl); 00230 rs.set_version(value); 00231 } 00232 else if (header == "xdap") { 00233 DBG(cout << header << ": " << value << endl); 00234 rs.set_protocol(value); 00235 } 00236 // Only look for 'server' if no other header supplies this info. 00237 else if (rs.get_version() == "dods/0.0" && header == "server") { 00238 DBG(cout << header << ": " << value << endl); 00239 rs.set_version(value); 00240 } 00241 00242 mime = get_next_mime_header(data_source); 00243 } 00244 } 00245 00246 // public mfuncs 00247 00254 D4Connect::D4Connect(const string &url, string uname, string password) : 00255 d_http(0), d_local(false), d_URL(""), d_dap4ce(""), d_server("unknown"), d_protocol("4.0") 00256 { 00257 string name = prune_spaces(url); 00258 00259 // Figure out if the URL starts with 'http', if so, make sure that we 00260 // talk to an instance of HTTPConnect. 00261 if (name.find("http") == 0) { 00262 DBG(cerr << "Connect: The identifier is an http URL" << endl); 00263 d_http = new HTTPConnect(RCReader::instance()); 00264 d_http->set_use_cpp_streams(true); 00265 00266 // Find and store any CE given with the URL. 00267 string::size_type dotpos = name.find('?'); 00268 if (dotpos != name.npos) { 00269 d_URL = name.substr(0, dotpos); 00270 d_dap4ce = name.substr(dotpos + 1); 00271 } 00272 else { 00273 d_URL = name; 00274 } 00275 } 00276 else { 00277 DBG(cerr << "Connect: The identifier is a local data source." << endl); 00278 d_local = true; // local in this case means non-DAP 00279 } 00280 00281 set_credentials(uname, password); 00282 } 00283 00284 D4Connect::~D4Connect() 00285 { 00286 if (d_http) delete d_http; 00287 } 00288 00289 void D4Connect::request_dmr(DMR &dmr, const string expr) 00290 { 00291 string url = d_URL + ".dmr" + "?" + id2www_ce(d_dap4ce + expr); 00292 00293 Response *rs = 0; 00294 try { 00295 rs = d_http->fetch_url(url); 00296 00297 d_server = rs->get_version(); 00298 d_protocol = rs->get_protocol(); 00299 00300 switch (rs->get_type()) { 00301 case unknown_type: // FIXME Pure hackery! 00302 cerr << "Response type unknown, assuming it's a DMR response." << endl; 00303 /* no break */ 00304 case dap4_dmr: { 00305 D4ParserSax2 parser; 00306 parser.intern(*rs->get_cpp_stream(), &dmr, false /* debug */); 00307 break; 00308 } 00309 00310 case dap4_error: 00311 throw InternalErr(__FILE__, __LINE__, "DAP4 errors are not processed yet."); 00312 00313 case web_error: 00314 // We should never get here; a web error should be picked up read_url 00315 // (called by fetch_url) and result in a thrown Error object. 00316 throw InternalErr(__FILE__, __LINE__, "Web error found where it should never be."); 00317 break; 00318 00319 default: 00320 throw InternalErr(__FILE__, __LINE__, "Response type not handled (got " 00321 + long_to_string(rs->get_type()) + ")."); 00322 } 00323 } 00324 catch (...) { 00325 delete rs; 00326 throw; 00327 } 00328 00329 delete rs; 00330 } 00331 00332 void D4Connect::request_dap4_data(DMR &dmr, const string expr) 00333 { 00334 string url = d_URL + ".dap" + "?" + id2www_ce(d_dap4ce + expr); 00335 00336 Response *rs = 0; 00337 try { 00338 rs = d_http->fetch_url(url); 00339 00340 d_server = rs->get_version(); 00341 d_protocol = rs->get_protocol(); 00342 00343 switch (rs->get_type()) { 00344 case unknown_type: // FIXME Pure hackery! 00345 cerr << "Response type unknown, assuming it's a DAP4 Data response." << endl; 00346 /* no break */ 00347 case dap4_data: { 00348 // TODO Move to a function 11/9/13 process_data()??? 00349 #if BYTE_ORDER_PREFIX 00350 istream &in = *rs->get_cpp_stream(); 00351 // Read the byte-order byte; used later on 00352 char byte_order; 00353 in >> byte_order; 00354 #endif 00355 00356 // get a chunked input stream 00357 #if BYTE_ORDER_PREFIX 00358 chunked_istream cis(*(rs->get_cpp_stream()), 1024, byte_order); 00359 #else 00360 chunked_istream cis(*(rs->get_cpp_stream()), CHUNK_SIZE); 00361 #endif 00362 00363 // parse the DMR, stopping when the boundary is found. 00364 00365 // force chunk read 00366 // get chunk size 00367 int chunk_size = cis.read_next_chunk(); 00368 if (chunk_size < 0) 00369 throw Error("Found an unexpected end of input (EOF) while reading a DAP4 data response. (2)"); 00370 00371 // get chunk 00372 char chunk[chunk_size]; 00373 cis.read(chunk, chunk_size); 00374 // parse char * with given size 00375 D4ParserSax2 parser; 00376 // '-2' to discard the CRLF pair 00377 parser.intern(chunk, chunk_size - 2, &dmr, false /*debug*/); 00378 00379 // Read data and store in the DMR 00380 #if BYTE_ORDER_PREFIX 00381 D4StreamUnMarshaller um(cis, byte_order); 00382 #else 00383 D4StreamUnMarshaller um(cis, cis.twiddle_bytes()); 00384 #endif 00385 dmr.root()->deserialize(um, dmr); 00386 00387 break; 00388 } 00389 00390 case dap4_error: 00391 throw InternalErr(__FILE__, __LINE__, "DAP4 errors are not processed yet."); 00392 00393 case web_error: 00394 // We should never get here; a web error should be picked up read_url 00395 // (called by fetch_url) and result in a thrown Error object. 00396 throw InternalErr(__FILE__, __LINE__, "Web error found where it should never be."); 00397 break; 00398 00399 default: 00400 throw InternalErr(__FILE__, __LINE__, "Response type not handled (got " 00401 + long_to_string(rs->get_type()) + ")."); 00402 } 00403 } 00404 catch (...) { 00405 delete rs; 00406 throw; 00407 } 00408 00409 delete rs; 00410 } 00411 00412 void 00413 D4Connect::read_dmr(DMR &dmr, Response &rs) 00414 { 00415 parse_mime(rs); 00416 if (rs.get_type() == unknown_type) 00417 throw Error("Unknown response type."); 00418 00419 read_dmr_no_mime(dmr, rs); 00420 } 00421 00422 void 00423 D4Connect::read_dmr_no_mime(DMR &dmr, Response &rs) 00424 { 00425 // Assume callers know what they are doing 00426 if (rs.get_type() == unknown_type) 00427 rs.set_type(dap4_dmr); 00428 00429 switch (rs.get_type()) { 00430 case dap4_dmr: 00431 process_dmr(dmr, rs); 00432 d_server = rs.get_version(); 00433 d_protocol = dmr.dap_version(); 00434 break; 00435 default: 00436 throw Error("Expected a DAP4 DMR response."); 00437 } 00438 } 00439 00440 void 00441 D4Connect::read_data(DMR &data, Response &rs) 00442 { 00443 parse_mime(rs); 00444 if (rs.get_type() == unknown_type) 00445 throw Error("Unknown response type."); 00446 00447 read_data_no_mime(data, rs); 00448 } 00449 00450 void D4Connect::read_data_no_mime(DMR &data, Response &rs) 00451 { 00452 // Assume callers know what they are doing 00453 if (rs.get_type() == unknown_type) 00454 rs.set_type(dap4_data); 00455 00456 switch (rs.get_type()) { 00457 case dap4_data: 00458 process_data(data, rs); 00459 d_server = rs.get_version(); 00460 d_protocol = data.dap_version(); 00461 break; 00462 default: 00463 throw Error("Expected a DAP4 Data response."); 00464 } 00465 } 00466 00472 void D4Connect::set_credentials(string u, string p) 00473 { 00474 if (d_http) 00475 d_http->set_credentials(u, p); 00476 } 00477 00481 void D4Connect::set_accept_deflate(bool deflate) 00482 { 00483 if (d_http) 00484 d_http->set_accept_deflate(deflate); 00485 } 00486 00492 void D4Connect::set_xdap_protocol(int major, int minor) 00493 { 00494 if (d_http) 00495 d_http->set_xdap_protocol(major, minor); 00496 } 00497 00501 void D4Connect::set_cache_enabled(bool cache) 00502 { 00503 if (d_http) 00504 d_http->set_cache_enabled(cache); 00505 } 00506 00507 bool D4Connect::is_cache_enabled() 00508 { 00509 if (d_http) 00510 return d_http->is_cache_enabled(); 00511 else 00512 return false; 00513 } 00514 00515 } // namespace libdap