Make HttpsClient more flexable and easier to use.

This commit is contained in:
Arthur Britto
2012-04-05 16:19:05 -07:00
parent 71be855cd6
commit 29afd2eeaf
2 changed files with 133 additions and 37 deletions

View File

@@ -16,34 +16,38 @@ using namespace boost::asio;
HttpsClient::HttpsClient( HttpsClient::HttpsClient(
boost::asio::io_service& io_service, boost::asio::io_service& io_service,
const std::string strDomain, const unsigned short port,
const std::string strPath, const std::string strPath,
unsigned short port,
std::size_t responseMax std::size_t responseMax
) : ) :
mCtx(boost::asio::ssl::context::sslv23), mCtx(boost::asio::ssl::context::sslv23),
mResolver(io_service), mResolver(io_service),
mQuery(strDomain, boost::lexical_cast<std::string>(port),
ip::resolver_query_base::numeric_service|ip::resolver_query_base::numeric_service),
mSocketSsl(io_service, mCtx), mSocketSsl(io_service, mCtx),
mResponse(responseMax), mResponse(responseMax),
mStrDomain(strDomain), mStrPath(strPath),
mPort(port),
mDeadline(io_service) mDeadline(io_service)
{ {
std::ostream osRequest(&mRequest);
osRequest <<
"GET " << strPath << " HTTP/1.0\r\n"
"Host: " << mStrDomain << "\r\n"
"Accept: */*\r\n" // YYY Do we need this line?
"Connection: close\r\n\r\n";
} }
void HttpsClient::httpsGet( void HttpsClient::httpsGet(
std::deque<std::string> deqSites,
boost::posix_time::time_duration timeout, boost::posix_time::time_duration timeout,
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete) { boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete) {
mDeqSites = deqSites;
mComplete = complete; mComplete = complete;
mTimeout = timeout;
httpsNext();
}
void HttpsClient::httpsNext()
{
std::cerr << "Fetch: " << mDeqSites[0] << std::endl;
boost::shared_ptr<boost::asio::ip::tcp::resolver::query> query(new boost::asio::ip::tcp::resolver::query(mDeqSites[0], boost::lexical_cast<std::string>(mPort),
ip::resolver_query_base::numeric_service|ip::resolver_query_base::numeric_service));
mQuery = query;
mCtx.set_default_verify_paths(mShutdown); mCtx.set_default_verify_paths(mShutdown);
if (!mShutdown) if (!mShutdown)
@@ -53,7 +57,7 @@ void HttpsClient::httpsGet(
if (!mShutdown) if (!mShutdown)
{ {
mDeadline.expires_from_now(timeout, mShutdown); mDeadline.expires_from_now(mTimeout, mShutdown);
std::cerr << "expires_from_now: " << mShutdown.message() << std::endl; std::cerr << "expires_from_now: " << mShutdown.message() << std::endl;
} }
@@ -69,9 +73,9 @@ void HttpsClient::httpsGet(
if (!mShutdown) if (!mShutdown)
{ {
std::cerr << "Resolving: " << mStrDomain << std::endl; std::cerr << "Resolving: " << mDeqSites[0] << std::endl;
mResolver.async_resolve(mQuery, mResolver.async_resolve(*mQuery,
boost::bind( boost::bind(
&HttpsClient::handleResolve, &HttpsClient::handleResolve,
shared_from_this(), shared_from_this(),
@@ -90,11 +94,11 @@ void HttpsClient::handleDeadline(const boost::system::error_code& ecResult)
// Timer canceled because deadline no longer needed. // Timer canceled because deadline no longer needed.
std::cerr << "Deadline cancelled." << std::endl; std::cerr << "Deadline cancelled." << std::endl;
// Do nothing. // Do nothing. Aborter is done.
} }
else if (ecResult) else if (ecResult)
{ {
std::cerr << "Deadline error: " << mStrDomain << ": " << ecResult.message() << std::endl; std::cerr << "Deadline error: " << mDeqSites[0] << ": " << ecResult.message() << std::endl;
// Can't do anything sound. // Can't do anything sound.
abort(); abort();
@@ -117,7 +121,7 @@ void HttpsClient::handleDeadline(const boost::system::error_code& ecResult)
if (ec_shutdown) if (ec_shutdown)
{ {
std::cerr << "Shutdown error: " << mStrDomain << ": " << ec_shutdown.message() << std::endl; std::cerr << "Shutdown error: " << mDeqSites[0] << ": " << ec_shutdown.message() << std::endl;
} }
} }
} }
@@ -132,7 +136,7 @@ void HttpsClient::handleResolve(
if (mShutdown) if (mShutdown)
{ {
std::cerr << "Resolve error: " << mStrDomain << ": " << mShutdown.message() << std::endl; std::cerr << "Resolve error: " << mDeqSites[0] << ": " << mShutdown.message() << std::endl;
invokeComplete(mShutdown); invokeComplete(mShutdown);
} }
@@ -168,11 +172,11 @@ void HttpsClient::handleConnect(const boost::system::error_code& ecResult)
mSocketSsl.set_verify_mode(boost::asio::ssl::verify_peer); mSocketSsl.set_verify_mode(boost::asio::ssl::verify_peer);
// XXX Verify semantics of RFC 2818 are what we want. // XXX Verify semantics of RFC 2818 are what we want.
mSocketSsl.set_verify_callback(boost::asio::ssl::rfc2818_verification(mStrDomain), mShutdown); mSocketSsl.set_verify_callback(boost::asio::ssl::rfc2818_verification(mDeqSites[0]), mShutdown);
if (mShutdown) if (mShutdown)
{ {
std::cerr << "set_verify_callback: " << mStrDomain << ": " << mShutdown.message() << std::endl; std::cerr << "set_verify_callback: " << mDeqSites[0] << ": " << mShutdown.message() << std::endl;
} }
} }
@@ -204,6 +208,14 @@ void HttpsClient::handleRequest(const boost::system::error_code& ecResult)
{ {
std::cerr << "SSL session started." << std::endl; std::cerr << "SSL session started." << std::endl;
std::ostream osRequest(&mRequest);
osRequest <<
"GET " << mStrPath << " HTTP/1.0\r\n"
"Host: " << mDeqSites[0] << "\r\n"
"Accept: */*\r\n" // YYY Do we need this line?
"Connection: close\r\n\r\n";
boost::asio::async_write( boost::asio::async_write(
mSocketSsl, mSocketSsl,
mRequest, mRequest,
@@ -277,7 +289,16 @@ void HttpsClient::invokeComplete(const boost::system::error_code& ecResult, std:
std::cerr << "Deadline cancel error: " << ecCancel.message() << std::endl; std::cerr << "Deadline cancel error: " << ecCancel.message() << std::endl;
} }
mComplete(ecResult ? ecResult : ecCancel, strData); mDeqSites.pop_front();
if (mDeqSites.empty())
{
mComplete(ecResult ? ecResult : ecCancel, strData);
}
else
{
httpsNext();
}
} }
void HttpsClient::parseData() void HttpsClient::parseData()
@@ -293,8 +314,9 @@ void HttpsClient::parseData()
&& !smMatch[1].compare("200") && !smMatch[1].compare("200")
&& boost::regex_match(strData, smMatch, reBody); // Match body. && boost::regex_match(strData, smMatch, reBody); // Match body.
std::cerr << "Match: " << bMatch << std::endl; // std::cerr << "Data:" << strData << std::endl;
std::cerr << "Body:" << smMatch[1] << std::endl; // std::cerr << "Match: " << bMatch << std::endl;
// std::cerr << "Body:" << smMatch[1] << std::endl;
if (bMatch) if (bMatch)
{ {
@@ -308,4 +330,52 @@ void HttpsClient::parseData()
invokeComplete(boost::system::error_code(errc::bad_address, system_category())); invokeComplete(boost::system::error_code(errc::bad_address, system_category()));
} }
} }
void HttpsClient::httpsGet(
boost::asio::io_service& io_service,
std::deque<std::string> deqSites,
const unsigned short port,
const std::string strPath,
std::size_t responseMax,
boost::posix_time::time_duration timeout,
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete) {
boost::shared_ptr<HttpsClient> client(new HttpsClient(io_service, port, strPath, responseMax));
client->httpsGet(deqSites, timeout, complete);
}
void HttpsClient::httpsGet(
boost::asio::io_service& io_service,
std::string strSite,
const unsigned short port,
const std::string strPath,
std::size_t responseMax,
boost::posix_time::time_duration timeout,
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete) {
std::deque<std::string> deqSites(1, strSite);
boost::shared_ptr<HttpsClient> client(new HttpsClient(io_service, port, strPath, responseMax));
client->httpsGet(deqSites, timeout, complete);
}
bool HttpsClient::httpsParseUrl(const std::string strUrl, std::string& strDomain, std::string& strPath)
{
static boost::regex reUrl("(?i)\\`https://([^/]+)(/.*)\\'"); // https://DOMAINPATH
boost::smatch smMatch;
bool bMatch = boost::regex_match(strUrl, smMatch, reUrl); // Match status code.
if (bMatch)
{
strDomain = smMatch[0];
strDomain = smMatch[1];
}
return bMatch;
}
// vim:ts=4 // vim:ts=4

View File

@@ -1,6 +1,7 @@
#ifndef _HTTPS_CLIENT_ #ifndef _HTTPS_CLIENT_
#define _HTTPS_CLIENT_ #define _HTTPS_CLIENT_
#include <deque>
#include <string> #include <string>
#include <boost/asio.hpp> #include <boost/asio.hpp>
@@ -18,19 +19,23 @@
class HttpsClient : public boost::enable_shared_from_this<HttpsClient> class HttpsClient : public boost::enable_shared_from_this<HttpsClient>
{ {
private: private:
boost::asio::ssl::context mCtx; boost::asio::ssl::context mCtx;
boost::asio::ip::tcp::resolver mResolver; boost::asio::ip::tcp::resolver mResolver;
boost::asio::ip::tcp::resolver::query mQuery; boost::shared_ptr<boost::asio::ip::tcp::resolver::query> mQuery;
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> mSocketSsl; boost::asio::ssl::stream<boost::asio::ip::tcp::socket> mSocketSsl;
boost::asio::streambuf mRequest; boost::asio::streambuf mRequest;
boost::asio::streambuf mResponse; boost::asio::streambuf mResponse;
std::string mStrDomain; const std::string mStrPath;
const unsigned short mPort;
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> mComplete; boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> mComplete;
boost::asio::deadline_timer mDeadline; boost::asio::deadline_timer mDeadline;
// If not success, we are shutting down. // If not success, we are shutting down.
boost::system::error_code mShutdown; boost::system::error_code mShutdown;
std::deque<std::string> mDeqSites;
boost::posix_time::time_duration mTimeout;
void handleDeadline(const boost::system::error_code& ecResult); void handleDeadline(const boost::system::error_code& ecResult);
@@ -44,23 +49,44 @@ private:
void handleWrite(const boost::system::error_code& ecResult); void handleWrite(const boost::system::error_code& ecResult);
void handleData(const boost::system::error_code& ecResult); void handleData(const boost::system::error_code& ecResult);
void invokeComplete(const boost::system::error_code& ecResult, std::string strData = "");
void parseData(); void parseData();
void httpsNext();
void invokeComplete(const boost::system::error_code& ecResult, std::string strData = "");
public: public:
HttpsClient( HttpsClient(
boost::asio::io_service& io_service, boost::asio::io_service& io_service,
const std::string strDomain, const unsigned short port,
const std::string strPath, const std::string strPath,
unsigned short port,
std::size_t responseMax std::size_t responseMax
); );
void httpsGet( void httpsGet(
std::deque<std::string> deqSites,
boost::posix_time::time_duration timeout, boost::posix_time::time_duration timeout,
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete); boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete);
static void httpsGet(
boost::asio::io_service& io_service,
std::deque<std::string> deqSites,
const unsigned short port,
const std::string strPath,
std::size_t responseMax,
boost::posix_time::time_duration timeout,
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete);
static void httpsGet(
boost::asio::io_service& io_service,
std::string strSite,
const unsigned short port,
const std::string strPath,
std::size_t responseMax,
boost::posix_time::time_duration timeout,
boost::function<void(const boost::system::error_code& ecResult, std::string& strData)> complete);
static bool httpsParseUrl(const std::string strUrl, std::string& strDomain, std::string& strPath);
}; };
#endif #endif
// vim:ts=4 // vim:ts=4