add get_file_size()

This commit is contained in:
David Rose 2002-10-03 03:02:52 +00:00
parent 66f53d3764
commit 26961c650f
13 changed files with 254 additions and 98 deletions

View File

@ -32,8 +32,9 @@ IChunkedStream() : istream(&_buf) {
// Description:
////////////////////////////////////////////////////////////////////
INLINE IChunkedStream::
IChunkedStream(istream *source, bool owns_source) : istream(&_buf) {
open(source, owns_source);
IChunkedStream(istream *source, bool owns_source,
HTTPDocument *doc) : istream(&_buf) {
open(source, owns_source, doc);
}
////////////////////////////////////////////////////////////////////
@ -42,9 +43,9 @@ IChunkedStream(istream *source, bool owns_source) : istream(&_buf) {
// Description:
////////////////////////////////////////////////////////////////////
INLINE IChunkedStream &IChunkedStream::
open(istream *source, bool owns_source) {
open(istream *source, bool owns_source, HTTPDocument *doc) {
clear(0);
_buf.open_read(source, owns_source);
_buf.open_read(source, owns_source, doc);
return *this;
}

View File

@ -23,6 +23,8 @@
#include "chunkedStreamBuf.h"
class HTTPDocument;
////////////////////////////////////////////////////////////////////
// Class : IChunkedStream
// Description : An input stream object that reads data from a source
@ -34,9 +36,11 @@
class EXPCL_PANDAEXPRESS IChunkedStream : public istream {
public:
INLINE IChunkedStream();
INLINE IChunkedStream(istream *source, bool owns_source);
INLINE IChunkedStream(istream *source, bool owns_source,
HTTPDocument *doc = NULL);
INLINE IChunkedStream &open(istream *source, bool owns_source);
INLINE IChunkedStream &open(istream *source, bool owns_source,
HTTPDocument *doc = NULL);
INLINE IChunkedStream &close();
private:

View File

@ -64,14 +64,26 @@ ChunkedStreamBuf::
////////////////////////////////////////////////////////////////////
// Function: ChunkedStreamBuf::open_read
// Access: Public
// Description:
// Description: If the document pointer is non-NULL, it will be
// updated with the length of the file as it is derived
// from the chunked encoding.
////////////////////////////////////////////////////////////////////
void ChunkedStreamBuf::
open_read(istream *source, bool owns_source) {
open_read(istream *source, bool owns_source, HTTPDocument *doc) {
_source = source;
_owns_source = owns_source;
_chunk_remaining = 0;
_done = false;
_doc = doc;
if (_doc != (HTTPDocument *)NULL) {
_doc->_file_size = 0;
// Read a little bit from the file to get the first chunk (and
// therefore the file size, or at least the size of the first
// chunk).
underflow();
}
}
////////////////////////////////////////////////////////////////////
@ -157,6 +169,10 @@ read_chars(char *start, size_t length) {
return 0;
}
if (_doc != (HTTPDocument *)NULL) {
_doc->_file_size += (size_t)chunk_size;
}
_chunk_remaining = (size_t)chunk_size;
return read_chars(start, length);
}

View File

@ -20,6 +20,8 @@
#define CHUNKEDSTREAMBUF_H
#include "pandabase.h"
#include "httpDocument.h"
#include "pointerTo.h"
////////////////////////////////////////////////////////////////////
// Class : ChunkedStreamBuf
@ -31,7 +33,7 @@ public:
ChunkedStreamBuf();
virtual ~ChunkedStreamBuf();
void open_read(istream *source, bool owns_source);
void open_read(istream *source, bool owns_source, HTTPDocument *doc);
void close_read();
protected:
@ -44,6 +46,8 @@ private:
bool _owns_source;
size_t _chunk_remaining;
bool _done;
PT(HTTPDocument) _doc;
};
#endif

View File

@ -803,7 +803,7 @@ read(istream &read_stream, bool want_server_info) {
uchar *header_buf = new uchar[_header_length];
// Read the header
read_stream.read((char *)header_buf, _header_length);
if (read_stream.gcount() != _header_length) {
if (read_stream.gcount() != (size_t)_header_length) {
downloader_cat.error() << "DownloadDb::read() - Empty file" << endl;
return false;
}

View File

@ -16,16 +16,16 @@
//
////////////////////////////////////////////////////////////////////
#include "config_downloader.h"
#include <error_utils.h>
#include <filename.h>
#include "downloader.h"
#include "config_downloader.h"
#include "urlSpec.h"
#include "error_utils.h"
#include "filename.h"
#include <errno.h>
#include <math.h>
#include "downloader.h"
#if !defined(WIN32_VC)
#include <sys/time.h>
#include <netinet/in.h>
@ -92,12 +92,11 @@ Downloader::
// Description:
////////////////////////////////////////////////////////////////////
int Downloader::
connect_to_server_by_proxy(const string &proxy_name, uint proxy_port,
const string &server_name) {
if (connect_to_server(proxy_name, proxy_port) != EU_success) {
connect_to_server_by_proxy(const URLSpec &proxy, const string &server_name) {
if (connect_to_server(proxy.get_server(), proxy.get_port()) != EU_success) {
downloader_cat.error()
<< "Downloader::connect_to_server_by_proxy() - could not connect to: "
<< proxy_name << endl;
<< proxy << endl;
return EU_error_abort;
}
@ -544,7 +543,7 @@ run(void) {
}
// Attempt to receive the bytes from the socket
int fret;
int fret = 0;
// Handle the case of a fast connection
if (_receive_size > (ulong)MAX_RECEIVE_BYTES) {
int repeat = (int)(_receive_size / MAX_RECEIVE_BYTES);
@ -667,7 +666,7 @@ run_to_ram(void) {
}
// Attempt to receive the bytes from the socket
int fret;
int fret = 0;
// Handle the case of a fast connection
if (_receive_size > (ulong)MAX_RECEIVE_BYTES) {
int repeat = (int)(_receive_size / MAX_RECEIVE_BYTES);

View File

@ -37,6 +37,8 @@
#include <resolv.h>
#endif
class URLSpec;
////////////////////////////////////////////////////////////////////
// Class : Downloader
// Description :
@ -46,8 +48,7 @@ PUBLISHED:
Downloader(void);
virtual ~Downloader(void);
int connect_to_server_by_proxy(const string &proxy_name, uint proxy_port,
const string &server_name);
int connect_to_server_by_proxy(const URLSpec &proxy, const string &server_name);
int connect_to_server(const string &name, uint port=80);
void disconnect_from_server(void);

View File

@ -39,6 +39,30 @@ get_proxy() const {
return _proxy;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::set_http_version
// Access: Published
// Description: Specifies the version of HTTP that the client uses to
// identify itself to the server. The default is HV_11,
// or HTTP 1.0; you can set this to HV_10 (HTTP 1.0) to
// request the server use the older interface.
////////////////////////////////////////////////////////////////////
INLINE void HTTPClient::
set_http_version(HTTPClient::HTTPVersion version) {
_http_version = version;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::get_http_version
// Access: Published
// Description: Returns the client's current setting for HTTP
// version. See set_http_version().
////////////////////////////////////////////////////////////////////
INLINE HTTPClient::HTTPVersion HTTPClient::
get_http_version() const {
return _http_version;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::set_verify_ssl
// Access: Published
@ -68,3 +92,37 @@ INLINE bool HTTPClient::
get_verify_ssl() const {
return _verify_ssl;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::get_document
// Access: Published
// Description: Opens the named document for reading, or if body is
// nonempty, posts data for a particular URL and
// retrieves the response. Returns a new HTTPDocument
// object whether the document is successfully read or
// not; you can test is_valid() and get_return_code() to
// determine whether the document was retrieved.
////////////////////////////////////////////////////////////////////
INLINE PT(HTTPDocument) HTTPClient::
get_document(const URLSpec &url, const string &body) {
const char *method = "GET";
if (!body.empty()) {
method = "POST";
}
return make_request(method, url, body);
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::get_header
// Access: Published
// Description: Like get_document(), except only the header
// associated with the file is retrieved. This may be
// used to test for existence of the file; it might also
// return the size of the file (if the server gives us
// this information).
////////////////////////////////////////////////////////////////////
INLINE PT(HTTPDocument) HTTPClient::
get_header(const URLSpec &url) {
return make_request("HEAD", url, string());
}

View File

@ -43,6 +43,7 @@ X509_STORE *HTTPClient::_x509_store = NULL;
////////////////////////////////////////////////////////////////////
HTTPClient::
HTTPClient() {
_http_version = HV_11;
_verify_ssl = verify_ssl;
make_ctx();
}
@ -54,6 +55,9 @@ HTTPClient() {
////////////////////////////////////////////////////////////////////
HTTPClient::
HTTPClient(const HTTPClient &copy) {
// We can initialize these to default values because the operator =
// function will copy them in a second.
_http_version = HV_11;
_verify_ssl = verify_ssl;
make_ctx();
@ -68,6 +72,7 @@ HTTPClient(const HTTPClient &copy) {
void HTTPClient::
operator = (const HTTPClient &copy) {
_proxy = copy._proxy;
_http_version = copy._http_version;
set_verify_ssl(copy._verify_ssl);
clear_expected_servers();
@ -99,6 +104,25 @@ HTTPClient::
clear_expected_servers();
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::get_http_version_string
// Access: Published
// Description: Returns the current HTTP version setting as a string,
// e.g. "HTTP/1.0" or "HTTP/1.1".
////////////////////////////////////////////////////////////////////
string HTTPClient::
get_http_version_string() const {
switch (_http_version) {
case HV_10:
return "HTTP/1.0";
case HV_11:
return "HTTP/1.1";
}
return "unknown";
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::load_certificates
// Access: Published
@ -183,36 +207,6 @@ clear_expected_servers() {
_expected_servers.clear();
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::get_document
// Access: Published
// Description: Opens the named document for reading, or if body is
// nonempty, posts data for a particular URL and
// retrieves the response. Returns a new HTTPDocument
// object whether the document is successfully read or
// not; you can test is_valid() and get_return_code() to
// determine whether the document was retrieved.
////////////////////////////////////////////////////////////////////
PT(HTTPDocument) HTTPClient::
get_document(const URLSpec &url, const string &body) {
BIO *bio;
if (_proxy.empty()) {
if (url.get_scheme() == "https") {
bio = get_https(url, body);
} else {
bio = get_http(url, body);
}
} else {
if (url.get_scheme() == "https") {
bio = get_https_proxy(url, body);
} else {
bio = get_http_proxy(url, body);
}
}
return new HTTPDocument(bio, true);
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::make_ctx
@ -379,6 +373,36 @@ load_verify_locations(SSL_CTX *ctx, const Filename &ca_file) {
return count;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::make_request
// Access: Private
// Description: Chooses a suitable mechanism to handle this request,
// based on whether it is an http or https request, and
// according to whether we have a proxy in place.
// Issues the request and returns an HTTPDocument that
// represents the results.
////////////////////////////////////////////////////////////////////
PT(HTTPDocument) HTTPClient::
make_request(const string &method, const URLSpec &url, const string &body) {
BIO *bio;
if (_proxy.empty()) {
if (url.get_scheme() == "https") {
bio = get_https(method, url, body);
} else {
bio = get_http(method, url, body);
}
} else {
if (url.get_scheme() == "https") {
bio = get_https_proxy(method, url, body);
} else {
bio = get_http_proxy(method, url, body);
}
}
return new HTTPDocument(bio, true);
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::get_https
// Access: Private
@ -386,7 +410,7 @@ load_verify_locations(SSL_CTX *ctx, const Filename &ca_file) {
// document.
////////////////////////////////////////////////////////////////////
BIO *HTTPClient::
get_http(const URLSpec &url, const string &body) {
get_http(const string &method, const URLSpec &url, const string &body) {
ostringstream server;
server << url.get_server() << ":" << url.get_port();
string server_str = server.str();
@ -405,7 +429,7 @@ get_http(const URLSpec &url, const string &body) {
return NULL;
}
send_get_request(bio, url.get_path(), url.get_server(), body);
send_request(bio, method, url.get_path(), url.get_server(), body);
return bio;
}
@ -416,7 +440,7 @@ get_http(const URLSpec &url, const string &body) {
// document.
////////////////////////////////////////////////////////////////////
BIO *HTTPClient::
get_https(const URLSpec &url, const string &body) {
get_https(const string &method, const URLSpec &url, const string &body) {
ostringstream server;
server << url.get_server() << ":" << url.get_port();
string server_str = server.str();
@ -438,7 +462,7 @@ get_https(const URLSpec &url, const string &body) {
BIO *sbio = make_https_connection(bio, url);
if (sbio != (BIO *)NULL) {
send_get_request(sbio, url.get_path(), url.get_server(), body);
send_request(sbio, method, url.get_path(), url.get_server(), body);
}
return sbio;
}
@ -450,7 +474,7 @@ get_https(const URLSpec &url, const string &body) {
// http document.
////////////////////////////////////////////////////////////////////
BIO *HTTPClient::
get_http_proxy(const URLSpec &url, const string &body) {
get_http_proxy(const string &method, const URLSpec &url, const string &body) {
ostringstream proxy_server;
proxy_server << _proxy.get_server() << ":" << _proxy.get_port();
string proxy_server_str = proxy_server.str();
@ -470,7 +494,7 @@ get_http_proxy(const URLSpec &url, const string &body) {
return NULL;
}
send_get_request(bio, url, url.get_server(), body);
send_request(bio, method, url, url.get_server(), body);
return bio;
}
@ -481,7 +505,7 @@ get_http_proxy(const URLSpec &url, const string &body) {
// document.
////////////////////////////////////////////////////////////////////
BIO *HTTPClient::
get_https_proxy(const URLSpec &url, const string &body) {
get_https_proxy(const string &method, const URLSpec &url, const string &body) {
// First, ask the proxy to open a connection for us.
ostringstream proxy_server;
proxy_server << _proxy.get_server() << ":" << _proxy.get_port();
@ -505,7 +529,7 @@ get_https_proxy(const URLSpec &url, const string &body) {
{
ostringstream request;
request
<< "CONNECT " << url.get_authority() << " HTTP/1.1\r\n"
<< "CONNECT " << url.get_authority() << " " << get_http_version_string()
<< "\r\n";
string request_str = request.str();
@ -540,7 +564,7 @@ get_https_proxy(const URLSpec &url, const string &body) {
// us.)
if ((doc->get_status_code() / 100) == 4) {
BIO_free_all(bio);
return get_http_proxy(url, body);
return get_http_proxy(method, url, body);
}
}
return NULL;
@ -557,7 +581,7 @@ get_https_proxy(const URLSpec &url, const string &body) {
BIO *sbio = make_https_connection(bio, url);
if (sbio != (BIO *)NULL) {
send_get_request(sbio, url.get_path(), url.get_server(), body);
send_request(sbio, method, url.get_path(), url.get_server(), body);
}
return sbio;
}
@ -630,7 +654,7 @@ make_https_connection(BIO *bio, const URLSpec &url) const {
downloader_cat.info()
<< "Server is " << common_name << " from " << org_unit_name
<< " of " << org_name << "\n";
<< " / " << org_name << "\n";
if (!verify_server(subject)) {
downloader_cat.info()
@ -824,34 +848,30 @@ certificate signing
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::send_get_request
// Function: HTTPClient::send_request
// Access: Private
// Description: Sends the appropriate GET or POST request to the
// server on the indicated connection.
// Description: Sends the appropriate GET or POST (or whatever)
// request to the server on the indicated connection.
////////////////////////////////////////////////////////////////////
void HTTPClient::
send_get_request(BIO *bio,
const string &path, const string &server,
const string &body) const {
send_request(BIO *bio, const string &method,
const string &path, const string &server,
const string &body) const {
ostringstream request;
if (body.empty()) {
request
<< method << " " << path << " " << get_http_version_string() << "\r\n";
if (_http_version > HV_10) {
request
<< "GET " << path << " HTTP/1.1\r\n"
<< "Host: " << server << "\r\n"
<< "Connection: close\r\n"
<< "\r\n";
} else {
request
<< "POST " << path << " HTTP/1.1\r\n"
<< "Host: " << server << "\r\n"
<< "Connection: close\r\n"
<< "Content-type: application/x-www-form-urlencoded\r\n"
<< "Content-Length: " << body.length() << "\r\n"
<< "\r\n"
<< body;
<< "Connection: close\r\n";
}
request
<< "\r\n"
<< body;
string request_str = request.str();
#ifndef NDEBUG
if (downloader_cat.is_debug()) {

View File

@ -60,6 +60,15 @@ PUBLISHED:
INLINE void set_proxy(const URLSpec &proxy);
INLINE const URLSpec &get_proxy() const;
enum HTTPVersion {
HV_10, // HTTP 1.0
HV_11, // HTTP 1.1
};
INLINE void set_http_version(HTTPVersion version);
INLINE HTTPVersion get_http_version() const;
string get_http_version_string() const;
bool load_certificates(const Filename &filename);
INLINE void set_verify_ssl(bool verify_ssl);
@ -68,22 +77,27 @@ PUBLISHED:
bool add_expected_server(const string &server_attributes);
void clear_expected_servers();
PT(HTTPDocument) get_document(const URLSpec &url, const string &body = string());
INLINE PT(HTTPDocument) get_document(const URLSpec &url,
const string &body = string());
INLINE PT(HTTPDocument) get_header(const URLSpec &url);
private:
void make_ctx();
static void initialize_ssl();
static int load_verify_locations(SSL_CTX *ctx, const Filename &ca_file);
BIO *get_http(const URLSpec &url, const string &body);
BIO *get_https(const URLSpec &url, const string &body);
BIO *get_http_proxy(const URLSpec &url, const string &body);
BIO *get_https_proxy(const URLSpec &url, const string &body);
PT(HTTPDocument) make_request(const string &method, const URLSpec &url,
const string &body);
BIO *get_http(const string &method, const URLSpec &url, const string &body);
BIO *get_https(const string &method, const URLSpec &url, const string &body);
BIO *get_http_proxy(const string &method, const URLSpec &url, const string &body);
BIO *get_https_proxy(const string &method, const URLSpec &url, const string &body);
BIO *make_https_connection(BIO *bio, const URLSpec &url) const;
void send_get_request(BIO *bio,
const string &path, const string &server,
const string &body) const;
void send_request(BIO *bio, const string &method,
const string &path, const string &server,
const string &body) const;
bool verify_server(X509_NAME *subject) const;
static X509_NAME *parse_x509_name(const string &source);
@ -101,6 +115,7 @@ private:
#endif
URLSpec _proxy;
HTTPVersion _http_version;
bool _verify_ssl;
// List of allowable SSL servers to connect to. If the list is

View File

@ -64,3 +64,17 @@ INLINE const string &HTTPDocument::
get_status_string() const {
return _status_string;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPDocument::get_file_size
// Access: Published
// Description: Returns the size of the file, if it is known.
// Returns 0 if the file size is not known. This may
// increase as the file is read due to the nature of
// HTTP/1.1 requests which can change their minds
// midstream about how much data they're sending you.
////////////////////////////////////////////////////////////////////
INLINE size_t HTTPDocument::
get_file_size() const {
return _file_size;
}

View File

@ -31,9 +31,12 @@ TypeHandle HTTPDocument::_type_handle;
////////////////////////////////////////////////////////////////////
HTTPDocument::
HTTPDocument(BIO *bio, bool owns_bio) {
_file_size = 0;
if (bio != (BIO *)NULL) {
_source = new IBioStream(bio, owns_bio);
read_headers();
determine_content_length();
} else {
_source = (IBioStream *)NULL;
@ -94,7 +97,8 @@ is_regular_file() const {
// Description: Opens the document for reading. Returns a newly
// allocated istream on success (which you should
// eventually delete when you are done reading).
// Returns NULL on failure.
// Returns NULL on failure. This may only be called
// once for a particular HTTPDocument.
////////////////////////////////////////////////////////////////////
istream *HTTPDocument::
open_read_file() const {
@ -109,14 +113,13 @@ open_read_file() const {
(*si) = tolower(*si);
}
istream *result = _source;
if (transfer_coding == "chunked") {
return new IChunkedStream(_source, false);
} else {
istream *result = _source;
((HTTPDocument *)this)->_source = (IBioStream *)NULL;
return result;
result = new IChunkedStream(_source, true, (HTTPDocument *)this);
}
((HTTPDocument *)this)->_source = (IBioStream *)NULL;
return result;
}
////////////////////////////////////////////////////////////////////
@ -256,4 +259,19 @@ read_headers() {
// A blank line terminates the headers.
}
////////////////////////////////////////////////////////////////////
// Function: HTTPDocument::determine_content_length
// Access: Private
// Description: Determines the file size based on the Content-Length
// field if it has been supplied.
////////////////////////////////////////////////////////////////////
void HTTPDocument::
determine_content_length() {
string content_length = get_header_value("Content-Length");
if (!content_length.empty()) {
_file_size = atoi(content_length.c_str());
}
}
#endif // HAVE_SSL

View File

@ -57,10 +57,13 @@ PUBLISHED:
INLINE const string &get_status_string() const;
string get_header_value(const string &key) const;
INLINE size_t get_file_size() const;
void write_headers(ostream &out) const;
private:
void read_headers();
void determine_content_length();
IBioStream *_source;
@ -71,6 +74,8 @@ private:
typedef pmap<string, string> Headers;
Headers _headers;
size_t _file_size;
public:
virtual TypeHandle get_type() const {
@ -88,6 +93,7 @@ public:
private:
static TypeHandle _type_handle;
friend class ChunkedStreamBuf;
};
#include "httpDocument.I"