add cookie support

This commit is contained in:
David Rose 2004-08-27 17:25:35 +00:00
parent 770732d644
commit 12c443e6fb
8 changed files with 722 additions and 6 deletions

View File

@ -22,8 +22,9 @@
extractor.h \
httpAuthorization.I httpAuthorization.h \
httpBasicAuthorization.I httpBasicAuthorization.h \
httpClient.I httpClient.h \
httpChannel.I httpChannel.h \
httpClient.I httpClient.h \
httpCookie.I httpCookie.h \
httpDate.I httpDate.h \
httpDigestAuthorization.I httpDigestAuthorization.h \
httpEntityTag.I httpEntityTag.h \
@ -50,8 +51,9 @@
extractor.cxx \
httpAuthorization.cxx \
httpBasicAuthorization.cxx \
httpClient.cxx \
httpChannel.cxx \
httpClient.cxx \
httpCookie.cxx \
httpDate.cxx \
httpDigestAuthorization.cxx \
httpEntityTag.cxx \
@ -76,8 +78,9 @@
extractor.h \
httpAuthorization.I httpAuthorization.h \
httpBasicAuthorization.I httpBasicAuthorization.h \
httpClient.I httpClient.h \
httpChannel.I httpChannel.h \
httpClient.I httpClient.h \
httpCookie.I httpCookie.h \
httpDate.I httpDate.h \
httpDigestAuthorization.I httpDigestAuthorization.h \
httpEntityTag.I httpEntityTag.h \

View File

@ -2,6 +2,7 @@
#include "httpBasicAuthorization.cxx"
#include "httpChannel.cxx"
#include "httpClient.cxx"
#include "httpCookie.cxx"
#include "httpDate.cxx"
#include "httpDigestAuthorization.cxx"
#include "httpEntityTag.cxx"

View File

@ -18,6 +18,7 @@
#include "httpChannel.h"
#include "httpClient.h"
#include "httpCookie.h"
#include "bioStream.h"
#include "ssl_utils.h"
#include "chunkedStream.h"
@ -791,7 +792,11 @@ reached_done_state() {
<< "Unable to download body.\n";
}
return false;
} else {
if (_state != S_reading_body) {
_body_stream = NULL;
}
_started_download = true;
_last_run_time = ClockObject::get_global_clock()->get_real_time();
return true;
@ -1831,7 +1836,12 @@ run_begin_body() {
reset_to_new();
} else {
nassertr(_body_stream == NULL, false);
// We shouldn't already be in the middle of reading some other
// body when we come here.
nassertd(_body_stream == NULL) {
reset_to_new();
return false;
}
_body_stream = read_body();
if (_body_stream == (ISocketStream *)NULL) {
if (downloader_cat.is_debug()) {
@ -1841,7 +1851,9 @@ run_begin_body() {
reset_to_new();
} else {
_state = S_reading_body;
if (_state != S_reading_body) {
_body_stream = NULL;
}
}
}
@ -3115,6 +3127,8 @@ make_header() {
break;
}
_client->send_cookies(stream, _request.get_url());
if (!_body.empty()) {
stream
<< "Content-Type: application/x-www-form-urlencoded\r\n"
@ -3220,6 +3234,10 @@ store_header_field(const string &field_name, const string &field_value) {
(*hi).second += ", ";
(*hi).second += field_value;
}
if (field_name == "set-cookie") {
_client->set_cookie(HTTPCookie(field_value, _request.get_url()));
}
}
#ifndef NDEBUG

View File

@ -609,6 +609,140 @@ get_username(const string &server, const string &realm) const {
return string();
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::set_cookie
// Access: Published
// Description: Stores the indicated cookie in the client's list of
// cookies, as if it had been received from a server.
////////////////////////////////////////////////////////////////////
void HTTPClient::
set_cookie(const HTTPCookie &cookie) {
if (cookie.is_expired()) {
clear_cookie(cookie);
} else {
pair<Cookies::iterator, bool> result = _cookies.insert(cookie);
if (!result.second) {
// We already had a cookie matching the supplied domain/path/name,
// so replace it.
const HTTPCookie &orig_cookie = *result.first;
((HTTPCookie &)orig_cookie).update_from(cookie);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::clear_cookie
// Access: Published
// Description: Removes the cookie with the matching domain/path/name
// from the client's list of cookies. Returns true if
// it was removed, false if the cookie was not matched.
////////////////////////////////////////////////////////////////////
bool HTTPClient::
clear_cookie(const HTTPCookie &cookie) {
Cookies::iterator ci = _cookies.find(cookie);
if (ci != _cookies.end()) {
_cookies.erase(ci);
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::clear_all_cookies
// Access: Published
// Description: Removes the all stored cookies from the client.
////////////////////////////////////////////////////////////////////
void HTTPClient::
clear_all_cookies() {
_cookies.clear();
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::has_cookie
// Access: Published
// Description: Returns true if there is a cookie in the client
// matching the given cookie's domain/path/name, false
// otherwise.
////////////////////////////////////////////////////////////////////
bool HTTPClient::
has_cookie(const HTTPCookie &cookie) const {
Cookies::const_iterator ci = _cookies.find(cookie);
return (ci != _cookies.end());
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::get_cookie
// Access: Published
// Description: Looks up and returns the cookie in the client
// matching the given cookie's domain/path/name. If
// there is no matching cookie, returns an empty cookie.
////////////////////////////////////////////////////////////////////
HTTPCookie HTTPClient::
get_cookie(const HTTPCookie &cookie) const {
Cookies::const_iterator ci = _cookies.find(cookie);
if (ci != _cookies.end()) {
return (*ci);
}
return HTTPCookie();
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::write_cookies
// Access: Published
// Description: Outputs the complete list of cookies stored on the
// client, for all domains, including the expired
// cookies (which will normally not be sent back to a
// host).
////////////////////////////////////////////////////////////////////
void HTTPClient::
write_cookies(ostream &out) const {
Cookies::const_iterator ci;
for (ci = _cookies.begin(); ci != _cookies.end(); ++ci) {
out << *ci << "\n";
}
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::send_cookies
// Access: Published
// Description: Writes to the indicated ostream a set of Cookie:
// lines for sending the cookies appropriate to the
// indicated URL along with an HTTP request. This also
// removes expired cookies.
////////////////////////////////////////////////////////////////////
void HTTPClient::
send_cookies(ostream &out, const URLSpec &url) {
HTTPDate now = HTTPDate::now();
bool any_expired = false;
Cookies::const_iterator ci;
for (ci = _cookies.begin(); ci != _cookies.end(); ++ci) {
const HTTPCookie &cookie = (*ci);
if (cookie.is_expired(now)) {
any_expired = true;
} else if (cookie.matches_url(url)) {
out << "Cookie: " << cookie.get_name() << "="
<< cookie.get_value() << "\r\n";
}
}
if (any_expired) {
Cookies new_cookies;
Cookies::const_iterator ci;
for (ci = _cookies.begin(); ci != _cookies.end(); ++ci) {
const HTTPCookie &cookie = (*ci);
if (!cookie.is_expired(now)) {
new_cookies.insert(new_cookies.end(), cookie);
}
}
_cookies.swap(new_cookies);
}
}
////////////////////////////////////////////////////////////////////
// Function: HTTPClient::load_client_certificate
// Access: Published

View File

@ -31,10 +31,12 @@
#include "urlSpec.h"
#include "httpAuthorization.h"
#include "httpEnum.h"
#include "httpCookie.h"
#include "globPattern.h"
#include "pointerTo.h"
#include "pvector.h"
#include "pmap.h"
#include "pset.h"
#include <openssl/ssl.h>
@ -82,6 +84,15 @@ PUBLISHED:
void set_username(const string &server, const string &realm, const string &username);
string get_username(const string &server, const string &realm) const;
void set_cookie(const HTTPCookie &cookie);
bool clear_cookie(const HTTPCookie &cookie);
void clear_all_cookies();
bool has_cookie(const HTTPCookie &cookie) const;
HTTPCookie get_cookie(const HTTPCookie &cookie) const;
void write_cookies(ostream &out) const;
void send_cookies(ostream &out, const URLSpec &url);
INLINE void set_client_certificate_filename(const Filename &filename);
INLINE void set_client_certificate_pem(const string &pem);
INLINE void set_client_certificate_passphrase(const string &passphrase);
@ -157,7 +168,7 @@ private:
typedef pmap<string, string> Usernames;
Usernames _usernames;
typedef map<string, PT(HTTPAuthorization) > Realms;
typedef pmap<string, PT(HTTPAuthorization) > Realms;
class Domain {
public:
Realms _realms;
@ -165,6 +176,9 @@ private:
typedef pmap<string, Domain> Domains;
Domains _proxy_domains, _www_domains;
typedef pset<HTTPCookie> Cookies;
Cookies _cookies;
Filename _client_certificate_filename;
string _client_certificate_pem;
string _client_certificate_passphrase;

View File

@ -0,0 +1,232 @@
// Filename: httpCookie.I
// Created by: drose (26Aug04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::Constructor
// Access: Published
// Description: Constructs an empty cookie.
////////////////////////////////////////////////////////////////////
INLINE HTTPCookie::
HTTPCookie() :
_secure(false)
{
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::Constructor
// Access: Published
// Description: Constructs a cookie according to the indicated
// string, presumably the tag of a Set-Cookie header.
// There is no way to detect a formatting error in the
// string with this constructor.
////////////////////////////////////////////////////////////////////
INLINE HTTPCookie::
HTTPCookie(const string &format, const URLSpec &url) {
parse_set_cookie(format, url);
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::Constructor
// Access: Published
// Description: Constructs a cookie with the indicated name, path,
// and domain values, but no other data. This is most
// useful for looking up an existing cookie in the
// HTTPClient.
////////////////////////////////////////////////////////////////////
INLINE HTTPCookie::
HTTPCookie(const string &name, const string &path, const string &domain) :
_name(name),
_path(path),
_domain(domain),
_secure(false)
{
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::Destructor
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE HTTPCookie::
~HTTPCookie() {
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::set_name
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE void HTTPCookie::
set_name(const string &name) {
_name = name;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::get_name
// Access: Published
// Description: Returns the name of the cookie. This is the key
// value specified by the server.
////////////////////////////////////////////////////////////////////
INLINE const string &HTTPCookie::
get_name() const {
return _name;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::set_value
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE void HTTPCookie::
set_value(const string &value) {
_value = value;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::get_value
// Access: Published
// Description: Returns the value of the cookie. This is the
// arbitrary string associated with the cookie's name,
// as specified by the server.
////////////////////////////////////////////////////////////////////
INLINE const string &HTTPCookie::
get_value() const {
return _value;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::set_domain
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE void HTTPCookie::
set_domain(const string &domain) {
_domain = domain;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::get_domain
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE const string &HTTPCookie::
get_domain() const {
return _domain;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::set_path
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE void HTTPCookie::
set_path(const string &path) {
_path = path;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::get_path
// Access: Published
// Description: Returns the prefix of the URL paths on the server for
// which this cookie will be sent.
////////////////////////////////////////////////////////////////////
INLINE const string &HTTPCookie::
get_path() const {
return _path;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::set_expires
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE void HTTPCookie::
set_expires(const HTTPDate &expires) {
_expires = expires;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::clear_expires
// Access: Published
// Description: Removes the expiration date on the cookie.
////////////////////////////////////////////////////////////////////
INLINE void HTTPCookie::
clear_expires() {
_expires = HTTPDate();
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::has_expires
// Access: Published
// Description: Returns true if the cookie has an expiration date,
// false otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool HTTPCookie::
has_expires() const {
return _expires.is_valid();
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::get_expires
// Access: Published
// Description: Returns the expiration date of the cookie if it is
// set, or an invalid date if it is not.
////////////////////////////////////////////////////////////////////
INLINE HTTPDate HTTPCookie::
get_expires() const {
return _expires;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::set_secure
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
INLINE void HTTPCookie::
set_secure(bool secure) {
_secure = secure;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::get_secure
// Access: Published
// Description: Returns true if the server has indicated this is a
// "secure" cookie which should only be sent over an
// HTTPS channel.
////////////////////////////////////////////////////////////////////
INLINE bool HTTPCookie::
get_secure() const {
return _secure;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::is_expired
// Access: Published
// Description: Returns true if the cookie's expiration date is
// before the indicated date, false otherwise.
////////////////////////////////////////////////////////////////////
INLINE bool HTTPCookie::
is_expired(const HTTPDate &now) const {
return _expires.is_valid() && _expires < now;
}
INLINE ostream &operator << (ostream &out, const HTTPCookie &cookie) {
cookie.output(out);
return out;
}

View File

@ -0,0 +1,221 @@
// Filename: httpCookie.cxx
// Created by: drose (26Aug04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "httpCookie.h"
#ifdef HAVE_SSL
#include "ctype.h"
#include "httpChannel.h"
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::operator <
// Access: Published
// Description: The sorting operator allows the cookies to be stored
// in a single dictionary; it returns nonequal only if
// the cookies are different in name, path, or domain.
////////////////////////////////////////////////////////////////////
bool HTTPCookie::
operator < (const HTTPCookie &other) const {
if (_domain != other._domain) {
return _domain < other._domain;
}
if (_path != other._path) {
// We use reverse sorting on the path, so that cookies with longer
// paths will be sent to the server before cookies with shorter
// paths.
return _path > other._path;
}
if (_name != other._name) {
return _name < other._name;
}
return false;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::update_from
// Access: Published
// Description: Assuming the operator < method, above, has already
// evaluated these two cookies as equal, then assign the
// remaining values (value, expiration date, secure
// flag) from the indicated cookie. This is guaranteed
// not to change the ordering of the cookie in a set,
// and so can be used to update an existing cookie
// within a set with new values.
////////////////////////////////////////////////////////////////////
void HTTPCookie::
update_from(const HTTPCookie &other) {
nassertv(!(other < *this) && !(*this < other));
_value = other._value;
_expires = other._expires;
_secure = other._secure;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::parse_set_cookie
// Access: Published
// Description: Separates out the parameter/value pairs of the
// Set-Cookie header and assigns the values of the
// cookie appropriate. Returns true if the header is
// parsed correctly, false if something is not
// understood.
////////////////////////////////////////////////////////////////////
bool HTTPCookie::
parse_set_cookie(const string &format, const URLSpec &url) {
_name = string();
_value = string();
_domain = url.get_server();
_path = url.get_path();
_expires = HTTPDate();
_secure = false;
bool okflag = true;
bool first_param = true;
size_t start = 0;
while (start < format.length() && isspace(format[start])) {
start++;
}
size_t semicolon = format.find(';', start);
while (semicolon != string::npos) {
if (!parse_cookie_param(format.substr(start, semicolon - start),
first_param)) {
okflag = false;
}
first_param = false;
start = semicolon + 1;
while (start < format.length() && isspace(format[start])) {
start++;
}
semicolon = format.find(';', start);
}
if (!parse_cookie_param(format.substr(start), first_param)) {
okflag = false;
}
return okflag;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::matches_url
// Access: Published
// Description: Returns true if the cookie is appropriate to send
// with the indicated URL request, false otherwise.
////////////////////////////////////////////////////////////////////
bool HTTPCookie::
matches_url(const URLSpec &url) const {
string server = url.get_server();
if (server == _domain ||
(server.length() > _domain.length() &&
server.substr(server.length() - _domain.length()) == _domain &&
server[server.length() - _domain.length() - 1] == '.')) {
// The domain matches.
string path = url.get_path();
if (path.length() >= _path.length() &&
path.substr(0, _path.length()) == _path) {
// The path matches too.
if (_secure && !url.is_ssl()) {
// Oops, can't send a secure cookie over a non-secure connection.
return false;
}
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::output
// Access: Published
// Description:
////////////////////////////////////////////////////////////////////
void HTTPCookie::
output(ostream &out) const {
out << _name << "=" << _value
<< "; path=" << _path << "; domain=" << _domain;
if (has_expires()) {
out << "; expires=" << _expires;
}
if (_secure) {
out << "; secure";
}
}
////////////////////////////////////////////////////////////////////
// Function: HTTPCookie::parse_cookie_param
// Access: Private
// Description: Called internally by parse_set_cookie() with each
// parameter=value pair split out from the header
// string. first_param will be true for the first
// parameter (which has special meaning). This should
// return true on success, false on failure.
////////////////////////////////////////////////////////////////////
bool HTTPCookie::
parse_cookie_param(const string &param, bool first_param) {
size_t equals = param.find('=');
string key, value;
if (equals == string::npos) {
key = param;
} else {
key = param.substr(0, equals);
value = param.substr(equals + 1);
}
if (first_param) {
_name = key;
_value = value;
} else {
key = HTTPChannel::downcase(key);
if (key == "expires") {
_expires = HTTPDate(value);
if (!_expires.is_valid()) {
return false;
}
} else if (key == "path") {
_path = value;
} else if (key == "domain") {
_domain = HTTPChannel::downcase(value);
} else if (key == "secure") {
_secure = true;
} else {
return false;
}
}
return true;
}
#endif // HAVE_SSL

View File

@ -0,0 +1,93 @@
// Filename: httpCookie.h
// Created by: drose (26Aug04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef HTTPCOOKIE_H
#define HTTPCOOKIE_H
#include "pandabase.h"
// This module requires OpenSSL to compile, even if you do not intend
// to use this to establish https connections; this is because it uses
// the OpenSSL library to portably handle all of the socket
// communications.
#ifdef HAVE_SSL
#include "httpDate.h"
#include "urlSpec.h"
////////////////////////////////////////////////////////////////////
// Class : HTTPCookie
// Description : A cookie sent from an HTTP server to be stored on the
// client and returned when the path and/or domain
// matches.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEXPRESS HTTPCookie {
PUBLISHED:
INLINE HTTPCookie();
INLINE HTTPCookie(const string &format, const URLSpec &url);
INLINE HTTPCookie(const string &name, const string &path, const string &domain);
INLINE ~HTTPCookie();
INLINE void set_name(const string &name);
INLINE const string &get_name() const;
INLINE void set_value(const string &value);
INLINE const string &get_value() const;
INLINE void set_domain(const string &domain);
INLINE const string &get_domain() const;
INLINE void set_path(const string &path);
INLINE const string &get_path() const;
INLINE void set_expires(const HTTPDate &expires);
INLINE void clear_expires();
INLINE bool has_expires() const;
INLINE HTTPDate get_expires() const;
INLINE void set_secure(bool flag);
INLINE bool get_secure() const;
bool operator < (const HTTPCookie &other) const;
void update_from(const HTTPCookie &other);
bool parse_set_cookie(const string &format, const URLSpec &url);
INLINE bool is_expired(const HTTPDate &now = HTTPDate::now()) const;
bool matches_url(const URLSpec &url) const;
void output(ostream &out) const;
private:
bool parse_cookie_param(const string &param, bool first_param);
string _name;
string _value;
string _domain;
string _path;
HTTPDate _expires;
bool _secure;
};
INLINE ostream &operator << (ostream &out, const HTTPCookie &cookie);
#include "httpCookie.I"
#endif // HAVE_SSL
#endif