summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften/Network')
-rw-r--r--Swiften/Network/BOSHConnection.cpp46
-rw-r--r--Swiften/Network/BOSHConnection.h18
-rw-r--r--Swiften/Network/BOSHConnectionPool.cpp34
-rw-r--r--Swiften/Network/BOSHConnectionPool.h11
-rw-r--r--Swiften/Network/CachingNameOnlyDomainNameResolver.cpp32
-rw-r--r--Swiften/Network/CachingNameOnlyDomainNameResolver.h32
-rw-r--r--Swiften/Network/Connector.cpp4
-rw-r--r--Swiften/Network/Connector.h7
-rw-r--r--Swiften/Network/HTTPConnectProxiedConnection.cpp35
-rw-r--r--Swiften/Network/HTTPConnectProxiedConnection.h19
-rw-r--r--Swiften/Network/HTTPConnectProxiedConnectionFactory.cpp12
-rw-r--r--Swiften/Network/HTTPConnectProxiedConnectionFactory.h19
-rw-r--r--Swiften/Network/SConscript1
-rw-r--r--Swiften/Network/StaticDomainNameResolver.cpp29
-rw-r--r--Swiften/Network/StaticDomainNameResolver.h5
-rw-r--r--Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp38
-rw-r--r--Swiften/Network/UnitTest/BOSHConnectionTest.cpp14
-rw-r--r--Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp55
18 files changed, 300 insertions, 111 deletions
diff --git a/Swiften/Network/BOSHConnection.cpp b/Swiften/Network/BOSHConnection.cpp
index ea84400..73f8ed6 100644
--- a/Swiften/Network/BOSHConnection.cpp
+++ b/Swiften/Network/BOSHConnection.cpp
@@ -1,168 +1,175 @@
/*
* Copyright (c) 2011 Thilo Cestonaro
* Licensed under the simplified BSD license.
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
/*
* Copyright (c) 2011 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#include <Swiften/Network/BOSHConnection.h>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/lexical_cast.hpp>
#include <string>
-#include <Swiften/Network/ConnectionFactory.h>
#include <Swiften/Base/Log.h>
#include <Swiften/Base/String.h>
#include <Swiften/Base/Concat.h>
#include <Swiften/Base/ByteArray.h>
#include <Swiften/Network/HostAddressPort.h>
-#include <Swiften/Network/TLSConnection.h>
#include <Swiften/Parser/BOSHBodyExtractor.h>
namespace Swift {
-BOSHConnection::BOSHConnection(const URL& boshURL, ConnectionFactory* connectionFactory, XMLParserFactory* parserFactory, TLSContextFactory* tlsFactory)
- : boshURL_(boshURL),
- connectionFactory_(connectionFactory),
+BOSHConnection::BOSHConnection(const URL& boshURL, Connector::ref connector, XMLParserFactory* parserFactory)
+ : boshURL_(boshURL),
+ connector_(connector),
parserFactory_(parserFactory),
sid_(),
waitingForStartResponse_(false),
pending_(false),
- tlsFactory_(tlsFactory),
connectionReady_(false)
{
}
BOSHConnection::~BOSHConnection() {
+ cancelConnector();
if (connection_) {
- connection_->onConnectFinished.disconnect(boost::bind(&BOSHConnection::handleConnectionConnectFinished, shared_from_this(), _1));
connection_->onDataRead.disconnect(boost::bind(&BOSHConnection::handleDataRead, shared_from_this(), _1));
connection_->onDisconnected.disconnect(boost::bind(&BOSHConnection::handleDisconnected, shared_from_this(), _1));
}
disconnect();
}
void BOSHConnection::connect() {
- Connection::ref rawConnection = connectionFactory_->createConnection();
- connection_ = (boshURL_.getScheme() == "https") ? boost::make_shared<TLSConnection>(rawConnection, tlsFactory_) : rawConnection;
- connection_->onConnectFinished.connect(boost::bind(&BOSHConnection::handleConnectionConnectFinished, shared_from_this(), _1));
- connection_->onDataRead.connect(boost::bind(&BOSHConnection::handleDataRead, shared_from_this(), _1));
- connection_->onDisconnected.connect(boost::bind(&BOSHConnection::handleDisconnected, shared_from_this(), _1));
- connection_->connect(HostAddressPort(HostAddress(boshURL_.getHost()), boshURL_.getPort()));
+ connector_->onConnectFinished.connect(boost::bind(&BOSHConnection::handleConnectFinished, shared_from_this(), _1));
+ connector_->start();
+}
+
+void BOSHConnection::cancelConnector() {
+ if (connector_) {
+ connector_->onConnectFinished.disconnect(boost::bind(&BOSHConnection::handleConnectFinished, shared_from_this(), _1));
+ connector_->stop();
+ connector_.reset();
+ }
}
void BOSHConnection::disconnect() {
+ cancelConnector();
if(connection_) {
connection_->disconnect();
sid_ = "";
}
}
void BOSHConnection::restartStream() {
write(createSafeByteArray(""), true, false);
}
void BOSHConnection::terminateStream() {
write(createSafeByteArray(""), false, true);
}
void BOSHConnection::write(const SafeByteArray& data) {
write(data, false, false);
}
-std::pair<SafeByteArray, size_t> BOSHConnection::createHTTPRequest(const SafeByteArray& data, bool streamRestart, bool terminate, long rid, const std::string& sid, const URL& boshURL) {
+std::pair<SafeByteArray, size_t> BOSHConnection::createHTTPRequest(const SafeByteArray& data, bool streamRestart, bool terminate, unsigned long long rid, const std::string& sid, const URL& boshURL) {
size_t size;
std::stringstream content;
SafeByteArray contentTail = createSafeByteArray("</body>");
std::stringstream header;
content << "<body rid='" << rid << "' sid='" << sid << "'";
if (streamRestart) {
content << " xmpp:restart='true' xmlns:xmpp='urn:xmpp:xbosh'";
}
if (terminate) {
content << " type='terminate'";
}
content << " xmlns='http://jabber.org/protocol/httpbind'>";
SafeByteArray safeContent = createSafeByteArray(content.str());
safeContent.insert(safeContent.end(), data.begin(), data.end());
safeContent.insert(safeContent.end(), contentTail.begin(), contentTail.end());
size = safeContent.size();
header << "POST /" << boshURL.getPath() << " HTTP/1.1\r\n"
<< "Host: " << boshURL.getHost() << ":" << boshURL.getPort() << "\r\n"
/*<< "Accept-Encoding: deflate\r\n"*/
<< "Content-Type: text/xml; charset=utf-8\r\n"
<< "Content-Length: " << size << "\r\n\r\n";
SafeByteArray safeHeader = createSafeByteArray(header.str());
safeHeader.insert(safeHeader.end(), safeContent.begin(), safeContent.end());
return std::pair<SafeByteArray, size_t>(safeHeader, size);
}
void BOSHConnection::write(const SafeByteArray& data, bool streamRestart, bool terminate) {
assert(connectionReady_);
assert(!sid_.empty());
SafeByteArray safeHeader = createHTTPRequest(data, streamRestart, terminate, rid_, sid_, boshURL_).first;
onBOSHDataWritten(safeHeader);
connection_->write(safeHeader);
pending_ = true;
SWIFT_LOG(debug) << "write data: " << safeByteArrayToString(safeHeader) << std::endl;
}
-void BOSHConnection::handleConnectionConnectFinished(bool error) {
- connection_->onConnectFinished.disconnect(boost::bind(&BOSHConnection::handleConnectionConnectFinished, shared_from_this(), _1));
- connectionReady_ = !error;
- onConnectFinished(error);
+void BOSHConnection::handleConnectFinished(Connection::ref connection) {
+ cancelConnector();
+ connectionReady_ = connection;
+ if (connectionReady_) {
+ connection_ = connection;
+ connection_->onDataRead.connect(boost::bind(&BOSHConnection::handleDataRead, shared_from_this(), _1));
+ connection_->onDisconnected.connect(boost::bind(&BOSHConnection::handleDisconnected, shared_from_this(), _1));
+ }
+ onConnectFinished(!connectionReady_);
}
-void BOSHConnection::startStream(const std::string& to, unsigned long rid) {
+void BOSHConnection::startStream(const std::string& to, unsigned long long rid) {
assert(connectionReady_);
// Session Creation Request
std::stringstream content;
std::stringstream header;
content << "<body content='text/xml; charset=utf-8'"
<< " hold='1'"
<< " to='" << to << "'"
<< " rid='" << rid << "'"
<< " ver='1.6'"
<< " wait='60'" /* FIXME: we probably want this configurable*/
/*<< " ack='0'" FIXME: support acks */
<< " xml:lang='en'"
<< " xmlns:xmpp='urn:xmpp:bosh'"
<< " xmpp:version='1.0'"
<< " xmlns='http://jabber.org/protocol/httpbind' />";
std::string contentString = content.str();
header << "POST /" << boshURL_.getPath() << " HTTP/1.1\r\n"
<< "Host: " << boshURL_.getHost() << ":" << boshURL_.getPort() << "\r\n"
/*<< "Accept-Encoding: deflate\r\n"*/
<< "Content-Type: text/xml; charset=utf-8\r\n"
<< "Content-Length: " << contentString.size() << "\r\n\r\n"
<< contentString;
waitingForStartResponse_ = true;
SafeByteArray safeHeader = createSafeByteArray(header.str());
onBOSHDataWritten(safeHeader);
connection_->write(safeHeader);
SWIFT_LOG(debug) << "write stream header: " << safeByteArrayToString(safeHeader) << std::endl;
}
void BOSHConnection::handleDataRead(boost::shared_ptr<SafeByteArray> data) {
onBOSHDataRead(*data);
@@ -233,48 +240,49 @@ BOSHError::Type BOSHConnection::parseTerminationCondition(const std::string& tex
}
else if (text == "policy-violation") {
condition = BOSHError::PolicyViolation;
}
else if (text == "remote-connection-failed") {
condition = BOSHError::RemoteConnectionFailed;
}
else if (text == "remote-stream-error") {
condition = BOSHError::RemoteStreamError;
}
else if (text == "see-other-uri") {
condition = BOSHError::SeeOtherURI;
}
else if (text == "system-shutdown") {
condition = BOSHError::SystemShutdown;
}
else if (text == "") {
condition = BOSHError::NoError;
}
return condition;
}
const std::string& BOSHConnection::getSID() {
return sid_;
}
void BOSHConnection::setRID(unsigned long long rid) {
rid_ = rid;
}
void BOSHConnection::setSID(const std::string& sid) {
sid_ = sid;
}
void BOSHConnection::handleDisconnected(const boost::optional<Connection::Error>& error) {
+ cancelConnector();
onDisconnected(error);
sid_ = "";
connectionReady_ = false;
}
bool BOSHConnection::isReadyToSend() {
/* Without pipelining you need to not send more without first receiving the response */
/* With pipelining you can. Assuming we can't, here */
return connectionReady_ && !pending_ && !waitingForStartResponse_ && !sid_.empty();
}
}
diff --git a/Swiften/Network/BOSHConnection.h b/Swiften/Network/BOSHConnection.h
index 4d53253..d9fa016 100644
--- a/Swiften/Network/BOSHConnection.h
+++ b/Swiften/Network/BOSHConnection.h
@@ -1,107 +1,107 @@
/*
* Copyright (c) 2011 Thilo Cestonaro
* Licensed under the simplified BSD license.
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
/*
* Copyright (c) 2011 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#pragma once
#include <boost/enable_shared_from_this.hpp>
#include <Swiften/Network/Connection.h>
+#include <Swiften/Network/Connector.h>
#include <Swiften/Network/HostAddressPort.h>
#include <Swiften/Base/String.h>
#include <Swiften/Base/URL.h>
#include <Swiften/Base/Error.h>
#include <Swiften/Session/SessionStream.h>
namespace boost {
class thread;
namespace system {
class error_code;
}
}
class BOSHConnectionTest;
namespace Swift {
- class ConnectionFactory;
class XMLParserFactory;
class TLSContextFactory;
class BOSHError : public SessionStream::Error {
public:
enum Type {BadRequest, HostGone, HostUnknown, ImproperAddressing,
InternalServerError, ItemNotFound, OtherRequest, PolicyViolation,
RemoteConnectionFailed, RemoteStreamError, SeeOtherURI, SystemShutdown, UndefinedCondition,
NoError};
BOSHError(Type type) : SessionStream::Error(SessionStream::Error::ConnectionReadError), type(type) {}
Type getType() {return type;}
typedef boost::shared_ptr<BOSHError> ref;
private:
Type type;
};
class BOSHConnection : public boost::enable_shared_from_this<BOSHConnection> {
public:
typedef boost::shared_ptr<BOSHConnection> ref;
- static ref create(const URL& boshURL, ConnectionFactory* connectionFactory, XMLParserFactory* parserFactory, TLSContextFactory* tlsFactory) {
- return ref(new BOSHConnection(boshURL, connectionFactory, parserFactory, tlsFactory));
+ static ref create(const URL& boshURL, Connector::ref connector, XMLParserFactory* parserFactory) {
+ return ref(new BOSHConnection(boshURL, connector, parserFactory));
}
virtual ~BOSHConnection();
virtual void connect();
virtual void disconnect();
virtual void write(const SafeByteArray& data);
const std::string& getSID();
void setRID(unsigned long long rid);
void setSID(const std::string& sid);
- void startStream(const std::string& to, unsigned long rid);
+ void startStream(const std::string& to, unsigned long long rid);
void terminateStream();
bool isReadyToSend();
void restartStream();
boost::signal<void (bool /* error */)> onConnectFinished;
boost::signal<void (bool /* error */)> onDisconnected;
boost::signal<void (BOSHError::ref)> onSessionTerminated;
boost::signal<void (const std::string& /*sid*/, size_t /*requests*/)> onSessionStarted;
boost::signal<void (const SafeByteArray&)> onXMPPDataRead;
boost::signal<void (const SafeByteArray&)> onBOSHDataRead;
boost::signal<void (const SafeByteArray&)> onBOSHDataWritten;
boost::signal<void (const std::string&)> onHTTPError;
private:
friend class ::BOSHConnectionTest;
- BOSHConnection(const URL& boshURL, ConnectionFactory* connectionFactory, XMLParserFactory* parserFactory, TLSContextFactory* tlsFactory);
+ BOSHConnection(const URL& boshURL, Connector::ref connector, XMLParserFactory* parserFactory);
- static std::pair<SafeByteArray, size_t> createHTTPRequest(const SafeByteArray& data, bool streamRestart, bool terminate, long rid, const std::string& sid, const URL& boshURL);
- void handleConnectionConnectFinished(bool error);
+ static std::pair<SafeByteArray, size_t> createHTTPRequest(const SafeByteArray& data, bool streamRestart, bool terminate, unsigned long long rid, const std::string& sid, const URL& boshURL);
+ void handleConnectFinished(Connection::ref);
void handleDataRead(boost::shared_ptr<SafeByteArray> data);
void handleDisconnected(const boost::optional<Connection::Error>& error);
void write(const SafeByteArray& data, bool streamRestart, bool terminate); /* FIXME: refactor */
BOSHError::Type parseTerminationCondition(const std::string& text);
+ void cancelConnector();
URL boshURL_;
- ConnectionFactory* connectionFactory_;
+ Connector::ref connector_;
XMLParserFactory* parserFactory_;
boost::shared_ptr<Connection> connection_;
std::string sid_;
bool waitingForStartResponse_;
unsigned long long rid_;
SafeByteArray buffer_;
bool pending_;
- TLSContextFactory* tlsFactory_;
bool connectionReady_;
};
}
diff --git a/Swiften/Network/BOSHConnectionPool.cpp b/Swiften/Network/BOSHConnectionPool.cpp
index a30bf7b..7d43f42 100644
--- a/Swiften/Network/BOSHConnectionPool.cpp
+++ b/Swiften/Network/BOSHConnectionPool.cpp
@@ -1,83 +1,88 @@
/*
* Copyright (c) 2011 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#include <Swiften/Network/BOSHConnectionPool.h>
#include <climits>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <Swiften/Base/foreach.h>
#include <Swiften/Base/SafeString.h>
#include <Swiften/Network/TLSConnectionFactory.h>
#include <Swiften/Network/HTTPConnectProxiedConnectionFactory.h>
+#include <Swiften/Network/CachingNameOnlyDomainNameResolver.h>
namespace Swift {
-BOSHConnectionPool::BOSHConnectionPool(const URL& boshURL, ConnectionFactory* connectionFactory, XMLParserFactory* parserFactory, TLSContextFactory* tlsFactory, const std::string& to, long initialRID, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, const SafeString& boshHTTPConnectProxyAuthPassword) :
+BOSHConnectionPool::BOSHConnectionPool(const URL& boshURL, DomainNameResolver* realResolver, ConnectionFactory* connectionFactoryParameter, XMLParserFactory* parserFactory, TLSContextFactory* tlsFactory, TimerFactory* timerFactory, EventLoop* eventLoop, const std::string& to, unsigned long long initialRID, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, const SafeString& boshHTTPConnectProxyAuthPassword) :
boshURL(boshURL),
- connectionFactory(connectionFactory),
+ connectionFactory(connectionFactoryParameter),
xmlParserFactory(parserFactory),
tlsFactory(tlsFactory),
+ timerFactory(timerFactory),
rid(initialRID),
pendingTerminate(false),
to(to),
requestLimit(2),
restartCount(0),
pendingRestart(false) {
- tlsConnectionFactory = NULL;
- if (boshHTTPConnectProxyURL.empty()) {
- connectProxyFactory = NULL;
- }
- else {
- ConnectionFactory* rawFactory = connectionFactory;
+
+ if (!boshHTTPConnectProxyURL.empty()) {
if (boshHTTPConnectProxyURL.getScheme() == "https") {
- tlsConnectionFactory = new TLSConnectionFactory(tlsFactory, rawFactory);
- rawFactory = tlsConnectionFactory;
+ connectionFactory = new TLSConnectionFactory(tlsFactory, connectionFactory);
+ myConnectionFactories.push_back(connectionFactory);
}
- connectProxyFactory = new HTTPConnectProxiedConnectionFactory(rawFactory, HostAddressPort(HostAddress(boshHTTPConnectProxyURL.getHost()), boshHTTPConnectProxyURL.getPort()), boshHTTPConnectProxyAuthID, boshHTTPConnectProxyAuthPassword);
+ connectionFactory = new HTTPConnectProxiedConnectionFactory(realResolver, connectionFactory, timerFactory, eventLoop, boshHTTPConnectProxyURL.getHost(), boshHTTPConnectProxyURL.getPort(), boshHTTPConnectProxyAuthID, boshHTTPConnectProxyAuthPassword);
}
+ if (boshURL.getScheme() == "https") {
+ connectionFactory = new TLSConnectionFactory(tlsFactory, connectionFactory);
+ myConnectionFactories.push_back(connectionFactory);
+ }
+ resolver = new CachingNameOnlyDomainNameResolver(realResolver, eventLoop);
createConnection();
}
BOSHConnectionPool::~BOSHConnectionPool() {
close();
- delete connectProxyFactory;
- delete tlsConnectionFactory;
+ foreach (ConnectionFactory* factory, myConnectionFactories) {
+ delete factory;
+ }
+ delete resolver;
}
void BOSHConnectionPool::write(const SafeByteArray& data) {
dataQueue.push_back(data);
tryToSendQueuedData();
}
void BOSHConnectionPool::handleDataRead(const SafeByteArray& data) {
onXMPPDataRead(data);
tryToSendQueuedData(); /* Will rebalance the connections */
}
void BOSHConnectionPool::restartStream() {
BOSHConnection::ref connection = getSuitableConnection();
if (connection) {
pendingRestart = false;
rid++;
connection->setRID(rid);
connection->restartStream();
restartCount++;
}
else {
pendingRestart = true;
}
}
void BOSHConnectionPool::writeFooter() {
pendingTerminate = true;
tryToSendQueuedData();
}
void BOSHConnectionPool::close() {
/* TODO: Send a terminate here. */
std::vector<BOSHConnection::ref> connectionCopies = connections;
foreach (BOSHConnection::ref connection, connectionCopies) {
@@ -177,71 +182,72 @@ void BOSHConnectionPool::tryToSendQueuedData() {
suitableConnection->write(createSafeByteArray(""));
}
else {
/* My thought process I went through when writing this, to aid anyone else confused why this can happen...
*
* What to do here? I think this isn't possible.
If you didn't have two connections, suitable would have made one.
If you have two connections and neither is suitable, pending would be true.
If you have a non-pending connection, it's suitable.
If I decide to do something here, remove assert above.
Ah! Yes, because there's a period between creating the connection and it being connected. */
}
}
}
}
}
void BOSHConnectionPool::handleHTTPError(const std::string& /*errorCode*/) {
handleSessionTerminated(boost::make_shared<BOSHError>(BOSHError::UndefinedCondition));
}
void BOSHConnectionPool::handleConnectionDisconnected(bool error, BOSHConnection::ref connection) {
destroyConnection(connection);
if (false && error) {
handleSessionTerminated(boost::make_shared<BOSHError>(BOSHError::UndefinedCondition));
}
else {
/* We might have just freed up a connection slot to send with */
tryToSendQueuedData();
}
}
boost::shared_ptr<BOSHConnection> BOSHConnectionPool::createConnection() {
- BOSHConnection::ref connection = BOSHConnection::create(boshURL, connectProxyFactory ? connectProxyFactory : connectionFactory, xmlParserFactory, tlsFactory);
+ Connector::ref connector = Connector::create(boshURL.getHost(), resolver, connectionFactory, timerFactory, boshURL.getPort());
+ BOSHConnection::ref connection = BOSHConnection::create(boshURL, connector, xmlParserFactory);
connection->onXMPPDataRead.connect(boost::bind(&BOSHConnectionPool::handleDataRead, this, _1));
connection->onSessionStarted.connect(boost::bind(&BOSHConnectionPool::handleSessionStarted, this, _1, _2));
connection->onBOSHDataRead.connect(boost::bind(&BOSHConnectionPool::handleBOSHDataRead, this, _1));
connection->onBOSHDataWritten.connect(boost::bind(&BOSHConnectionPool::handleBOSHDataWritten, this, _1));
connection->onDisconnected.connect(boost::bind(&BOSHConnectionPool::handleConnectionDisconnected, this, _1, connection));
connection->onConnectFinished.connect(boost::bind(&BOSHConnectionPool::handleConnectFinished, this, _1, connection));
connection->onSessionTerminated.connect(boost::bind(&BOSHConnectionPool::handleSessionTerminated, this, _1));
connection->onHTTPError.connect(boost::bind(&BOSHConnectionPool::handleHTTPError, this, _1));
connection->connect();
connections.push_back(connection);
return connection;
}
void BOSHConnectionPool::destroyConnection(boost::shared_ptr<BOSHConnection> connection) {
connections.erase(std::remove(connections.begin(), connections.end(), connection), connections.end());
connection->onXMPPDataRead.disconnect(boost::bind(&BOSHConnectionPool::handleDataRead, this, _1));
connection->onSessionStarted.disconnect(boost::bind(&BOSHConnectionPool::handleSessionStarted, this, _1, _2));
connection->onBOSHDataRead.disconnect(boost::bind(&BOSHConnectionPool::handleBOSHDataRead, this, _1));
connection->onBOSHDataWritten.disconnect(boost::bind(&BOSHConnectionPool::handleBOSHDataWritten, this, _1));
connection->onDisconnected.disconnect(boost::bind(&BOSHConnectionPool::handleConnectionDisconnected, this, _1, connection));
connection->onConnectFinished.disconnect(boost::bind(&BOSHConnectionPool::handleConnectFinished, this, _1, connection));
connection->onSessionTerminated.disconnect(boost::bind(&BOSHConnectionPool::handleSessionTerminated, this, _1));
connection->onHTTPError.disconnect(boost::bind(&BOSHConnectionPool::handleHTTPError, this, _1));
}
void BOSHConnectionPool::handleSessionTerminated(BOSHError::ref error) {
onSessionTerminated(error);
}
void BOSHConnectionPool::handleBOSHDataRead(const SafeByteArray& data) {
onBOSHDataRead(data);
}
void BOSHConnectionPool::handleBOSHDataWritten(const SafeByteArray& data) {
onBOSHDataWritten(data);
diff --git a/Swiften/Network/BOSHConnectionPool.h b/Swiften/Network/BOSHConnectionPool.h
index cc354b8..8bc0a7c 100644
--- a/Swiften/Network/BOSHConnectionPool.h
+++ b/Swiften/Network/BOSHConnectionPool.h
@@ -1,67 +1,70 @@
/*
* Copyright (c) 2011 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#pragma once
#include <vector>
#include <Swiften/Base/SafeString.h>
#include <Swiften/Network/BOSHConnection.h>
namespace Swift {
class HTTPConnectProxiedConnectionFactory;
class TLSConnectionFactory;
+ class CachingNameOnlyDomainNameResolver;
+ class EventLoop;
class BOSHConnectionPool : public boost::bsignals::trackable {
public:
- BOSHConnectionPool(const URL& boshURL, ConnectionFactory* connectionFactory, XMLParserFactory* parserFactory, TLSContextFactory* tlsFactory, const std::string& to, long initialRID, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, const SafeString& boshHTTPConnectProxyAuthPassword);
+ BOSHConnectionPool(const URL& boshURL, DomainNameResolver* resolver, ConnectionFactory* connectionFactory, XMLParserFactory* parserFactory, TLSContextFactory* tlsFactory, TimerFactory* timerFactory, EventLoop* eventLoop, const std::string& to, unsigned long long initialRID, const URL& boshHTTPConnectProxyURL, const SafeString& boshHTTPConnectProxyAuthID, const SafeString& boshHTTPConnectProxyAuthPassword);
~BOSHConnectionPool();
void write(const SafeByteArray& data);
void writeFooter();
void close();
void restartStream();
boost::signal<void (BOSHError::ref)> onSessionTerminated;
boost::signal<void ()> onSessionStarted;
boost::signal<void (const SafeByteArray&)> onXMPPDataRead;
boost::signal<void (const SafeByteArray&)> onBOSHDataRead;
boost::signal<void (const SafeByteArray&)> onBOSHDataWritten;
private:
void handleDataRead(const SafeByteArray& data);
void handleSessionStarted(const std::string& sid, size_t requests);
void handleBOSHDataRead(const SafeByteArray& data);
void handleBOSHDataWritten(const SafeByteArray& data);
void handleSessionTerminated(BOSHError::ref condition);
void handleConnectFinished(bool, BOSHConnection::ref connection);
void handleConnectionDisconnected(bool error, BOSHConnection::ref connection);
void handleHTTPError(const std::string& errorCode);
private:
BOSHConnection::ref createConnection();
void destroyConnection(BOSHConnection::ref connection);
void tryToSendQueuedData();
BOSHConnection::ref getSuitableConnection();
private:
URL boshURL;
ConnectionFactory* connectionFactory;
XMLParserFactory* xmlParserFactory;
TLSContextFactory* tlsFactory;
+ TimerFactory* timerFactory;
std::vector<BOSHConnection::ref> connections;
std::string sid;
- unsigned long rid;
+ unsigned long long rid;
std::vector<SafeByteArray> dataQueue;
bool pendingTerminate;
std::string to;
size_t requestLimit;
int restartCount;
bool pendingRestart;
- HTTPConnectProxiedConnectionFactory* connectProxyFactory;
- TLSConnectionFactory* tlsConnectionFactory;
+ std::vector<ConnectionFactory*> myConnectionFactories;
+ CachingNameOnlyDomainNameResolver* resolver;
};
}
diff --git a/Swiften/Network/CachingNameOnlyDomainNameResolver.cpp b/Swiften/Network/CachingNameOnlyDomainNameResolver.cpp
new file mode 100644
index 0000000..a83bebd
--- /dev/null
+++ b/Swiften/Network/CachingNameOnlyDomainNameResolver.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2012 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swiften/Network/CachingNameOnlyDomainNameResolver.h>
+
+#include <boost/smart_ptr/make_shared.hpp>
+
+namespace Swift {
+CachingNameOnlyDomainNameResolver::CachingNameOnlyDomainNameResolver(DomainNameResolver* realResolver, EventLoop* eventLoop) : realResolver(realResolver) {
+ staticResolver = boost::make_shared<StaticDomainNameResolver>(eventLoop);
+}
+
+CachingNameOnlyDomainNameResolver::~CachingNameOnlyDomainNameResolver() {
+
+}
+
+DomainNameServiceQuery::ref CachingNameOnlyDomainNameResolver::createServiceQuery(const std::string& name) {
+ return staticResolver->createServiceQuery(name);
+}
+
+DomainNameAddressQuery::ref CachingNameOnlyDomainNameResolver::createAddressQuery(const std::string& name) {
+ return realResolver->createAddressQuery(name);
+}
+
+void CachingNameOnlyDomainNameResolver::handleAddressQueryResult(const std::string hostname, const std::vector<HostAddress>& address, boost::optional<DomainNameResolveError> error) {
+ //FIXME: Cache
+}
+
+}
diff --git a/Swiften/Network/CachingNameOnlyDomainNameResolver.h b/Swiften/Network/CachingNameOnlyDomainNameResolver.h
new file mode 100644
index 0000000..d9e78e6
--- /dev/null
+++ b/Swiften/Network/CachingNameOnlyDomainNameResolver.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2012 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include <Swiften/Network/DomainNameResolver.h>
+#include <Swiften/Network/StaticDomainNameResolver.h>
+
+
+namespace Swift {
+ class EventLoop;
+ class CachingNameOnlyDomainNameResolver : public DomainNameResolver {
+ public:
+ CachingNameOnlyDomainNameResolver(DomainNameResolver* realResolver, EventLoop* eventLoop);
+ ~CachingNameOnlyDomainNameResolver();
+
+ virtual DomainNameServiceQuery::ref createServiceQuery(const std::string& name);
+ virtual DomainNameAddressQuery::ref createAddressQuery(const std::string& name);
+
+ private:
+ void handleAddressQueryResult(const std::string hostname, const std::vector<HostAddress>& address, boost::optional<DomainNameResolveError> error);
+
+ private:
+ DomainNameResolver* realResolver;
+ boost::shared_ptr<StaticDomainNameResolver> staticResolver;
+ };
+}
diff --git a/Swiften/Network/Connector.cpp b/Swiften/Network/Connector.cpp
index 8f35aba..378875b 100644
--- a/Swiften/Network/Connector.cpp
+++ b/Swiften/Network/Connector.cpp
@@ -1,55 +1,55 @@
/*
* Copyright (c) 2010 Remko Tronçon
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#include <Swiften/Network/Connector.h>
#include <boost/bind.hpp>
#include <iostream>
#include <Swiften/Network/ConnectionFactory.h>
#include <Swiften/Network/DomainNameResolver.h>
#include <Swiften/Network/DomainNameAddressQuery.h>
#include <Swiften/Network/TimerFactory.h>
#include <Swiften/Base/Log.h>
namespace Swift {
-Connector::Connector(const std::string& hostname, DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory) : hostname(hostname), resolver(resolver), connectionFactory(connectionFactory), timerFactory(timerFactory), timeoutMilliseconds(0), queriedAllServices(true) {
+Connector::Connector(const std::string& hostname, DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, int defaultPort) : hostname(hostname), resolver(resolver), connectionFactory(connectionFactory), timerFactory(timerFactory), defaultPort(defaultPort), timeoutMilliseconds(0), queriedAllServices(true) {
}
void Connector::setTimeoutMilliseconds(int milliseconds) {
timeoutMilliseconds = milliseconds;
}
void Connector::start() {
SWIFT_LOG(debug) << "Starting connector for " << hostname << std::endl;
//std::cout << "Connector::start()" << std::endl;
assert(!currentConnection);
assert(!serviceQuery);
assert(!timer);
queriedAllServices = false;
serviceQuery = resolver->createServiceQuery("_xmpp-client._tcp." + hostname);
serviceQuery->onResult.connect(boost::bind(&Connector::handleServiceQueryResult, shared_from_this(), _1));
if (timeoutMilliseconds > 0) {
timer = timerFactory->createTimer(timeoutMilliseconds);
timer->onTick.connect(boost::bind(&Connector::handleTimeout, shared_from_this()));
timer->start();
}
serviceQuery->run();
}
void Connector::stop() {
finish(boost::shared_ptr<Connection>());
}
void Connector::queryAddress(const std::string& hostname) {
assert(!addressQuery);
addressQuery = resolver->createAddressQuery(hostname);
addressQuery->onResult.connect(boost::bind(&Connector::handleAddressQueryResult, shared_from_this(), _1, _2));
addressQuery->run();
}
void Connector::handleServiceQueryResult(const std::vector<DomainNameServiceQuery::Result>& result) {
@@ -73,71 +73,71 @@ void Connector::tryNextServiceOrFallback() {
else {
SWIFT_LOG(debug) << "Querying next address" << std::endl;
queryAddress(serviceQueryResults.front().hostname);
}
}
void Connector::handleAddressQueryResult(const std::vector<HostAddress>& addresses, boost::optional<DomainNameResolveError> error) {
SWIFT_LOG(debug) << addresses.size() << " addresses" << std::endl;
addressQuery.reset();
if (error || addresses.empty()) {
if (!serviceQueryResults.empty()) {
serviceQueryResults.pop_front();
}
tryNextServiceOrFallback();
}
else {
addressQueryResults = std::deque<HostAddress>(addresses.begin(), addresses.end());
tryNextAddress();
}
}
void Connector::tryNextAddress() {
if (addressQueryResults.empty()) {
SWIFT_LOG(debug) << "Done trying addresses. Moving on." << std::endl;
// Done trying all addresses. Move on to the next host.
if (!serviceQueryResults.empty()) {
serviceQueryResults.pop_front();
}
tryNextServiceOrFallback();
}
else {
SWIFT_LOG(debug) << "Trying next address" << std::endl;
HostAddress address = addressQueryResults.front();
addressQueryResults.pop_front();
- int port = 5222;
+ int port = defaultPort;
if (!serviceQueryResults.empty()) {
port = serviceQueryResults.front().port;
}
tryConnect(HostAddressPort(address, port));
}
}
void Connector::tryConnect(const HostAddressPort& target) {
assert(!currentConnection);
SWIFT_LOG(debug) << "Trying to connect to " << target.getAddress().toString() << ":" << target.getPort() << std::endl;
currentConnection = connectionFactory->createConnection();
currentConnection->onConnectFinished.connect(boost::bind(&Connector::handleConnectionConnectFinished, shared_from_this(), _1));
currentConnection->connect(target);
}
void Connector::handleConnectionConnectFinished(bool error) {
SWIFT_LOG(debug) << "ConnectFinished: " << (error ? "error" : "success") << std::endl;
currentConnection->onConnectFinished.disconnect(boost::bind(&Connector::handleConnectionConnectFinished, shared_from_this(), _1));
if (error) {
currentConnection.reset();
if (!addressQueryResults.empty()) {
tryNextAddress();
}
else {
if (!serviceQueryResults.empty()) {
serviceQueryResults.pop_front();
}
tryNextServiceOrFallback();
}
}
else {
finish(currentConnection);
}
}
diff --git a/Swiften/Network/Connector.h b/Swiften/Network/Connector.h
index 8336299..8f2c359 100644
--- a/Swiften/Network/Connector.h
+++ b/Swiften/Network/Connector.h
@@ -1,70 +1,71 @@
/*
* Copyright (c) 2010 Remko Tronçon
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#pragma once
#include <deque>
#include <Swiften/Base/boost_bsignals.h>
#include <boost/shared_ptr.hpp>
#include <Swiften/Network/DomainNameServiceQuery.h>
#include <Swiften/Network/Connection.h>
#include <Swiften/Network/Timer.h>
#include <Swiften/Network/HostAddressPort.h>
#include <string>
#include <Swiften/Network/DomainNameResolveError.h>
namespace Swift {
class DomainNameAddressQuery;
class DomainNameResolver;
class ConnectionFactory;
class TimerFactory;
class Connector : public boost::bsignals::trackable, public boost::enable_shared_from_this<Connector> {
public:
typedef boost::shared_ptr<Connector> ref;
- static Connector::ref create(const std::string& hostname, DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory) {
- return ref(new Connector(hostname, resolver, connectionFactory, timerFactory));
+ static Connector::ref create(const std::string& hostname, DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, int defaultPort = 5222) {
+ return ref(new Connector(hostname, resolver, connectionFactory, timerFactory, defaultPort));
}
void setTimeoutMilliseconds(int milliseconds);
void start();
void stop();
boost::signal<void (boost::shared_ptr<Connection>)> onConnectFinished;
private:
- Connector(const std::string& hostname, DomainNameResolver*, ConnectionFactory*, TimerFactory*);
+ Connector(const std::string& hostname, DomainNameResolver*, ConnectionFactory*, TimerFactory*, int defaultPort);
void handleServiceQueryResult(const std::vector<DomainNameServiceQuery::Result>& result);
void handleAddressQueryResult(const std::vector<HostAddress>& address, boost::optional<DomainNameResolveError> error);
void queryAddress(const std::string& hostname);
void tryNextServiceOrFallback();
void tryNextAddress();
void tryConnect(const HostAddressPort& target);
void handleConnectionConnectFinished(bool error);
void finish(boost::shared_ptr<Connection>);
void handleTimeout();
private:
std::string hostname;
DomainNameResolver* resolver;
ConnectionFactory* connectionFactory;
TimerFactory* timerFactory;
+ int defaultPort;
int timeoutMilliseconds;
boost::shared_ptr<Timer> timer;
boost::shared_ptr<DomainNameServiceQuery> serviceQuery;
std::deque<DomainNameServiceQuery::Result> serviceQueryResults;
boost::shared_ptr<DomainNameAddressQuery> addressQuery;
std::deque<HostAddress> addressQueryResults;
bool queriedAllServices;
boost::shared_ptr<Connection> currentConnection;
};
};
diff --git a/Swiften/Network/HTTPConnectProxiedConnection.cpp b/Swiften/Network/HTTPConnectProxiedConnection.cpp
index 9ef63f0..512381f 100644
--- a/Swiften/Network/HTTPConnectProxiedConnection.cpp
+++ b/Swiften/Network/HTTPConnectProxiedConnection.cpp
@@ -1,111 +1,124 @@
/*
* Copyright (c) 2010-2011 Thilo Cestonaro
* Licensed under the simplified BSD license.
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
/*
- * Copyright (c) 2011 Kevin Smith
+ * Copyright (c) 2011-2012 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#include <Swiften/Network/HTTPConnectProxiedConnection.h>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/lexical_cast.hpp>
#include <Swiften/Base/Algorithm.h>
#include <Swiften/Base/Log.h>
#include <Swiften/Base/String.h>
#include <Swiften/Base/ByteArray.h>
#include <Swiften/Network/HostAddressPort.h>
#include <Swiften/Network/ConnectionFactory.h>
+#include <Swiften/Network/CachingNameOnlyDomainNameResolver.h>
#include <Swiften/StringCodecs/Base64.h>
using namespace Swift;
-HTTPConnectProxiedConnection::HTTPConnectProxiedConnection(ConnectionFactory* connectionFactory, HostAddressPort proxy, const SafeString& authID, const SafeString& authPassword) : connectionFactory_(connectionFactory), proxy_(proxy), server_(HostAddressPort(HostAddress("0.0.0.0"), 0)), authID_(authID), authPassword_(authPassword) {
+HTTPConnectProxiedConnection::HTTPConnectProxiedConnection(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, EventLoop* eventLoop, const std::string& proxyHost, int proxyPort, const SafeString& authID, const SafeString& authPassword) : connectionFactory_(connectionFactory), timerFactory_(timerFactory), proxyHost_(proxyHost), proxyPort_(proxyPort), server_(HostAddressPort(HostAddress("0.0.0.0"), 0)), authID_(authID), authPassword_(authPassword) {
+ resolver_ = new CachingNameOnlyDomainNameResolver(resolver, eventLoop);
connected_ = false;
}
HTTPConnectProxiedConnection::~HTTPConnectProxiedConnection() {
+ cancelConnector();
+ delete resolver_;
if (connection_) {
- connection_->onConnectFinished.disconnect(boost::bind(&HTTPConnectProxiedConnection::handleConnectionConnectFinished, shared_from_this(), _1));
connection_->onDataRead.disconnect(boost::bind(&HTTPConnectProxiedConnection::handleDataRead, shared_from_this(), _1));
connection_->onDisconnected.disconnect(boost::bind(&HTTPConnectProxiedConnection::handleDisconnected, shared_from_this(), _1));
}
if (connected_) {
std::cerr << "Warning: Connection was still established." << std::endl;
}
}
+void HTTPConnectProxiedConnection::cancelConnector() {
+ if (connector_) {
+ connector_->onConnectFinished.disconnect(boost::bind(&HTTPConnectProxiedConnection::handleConnectFinished, shared_from_this(), _1));
+ connector_->stop();
+ connector_.reset();
+ }
+}
+
void HTTPConnectProxiedConnection::connect(const HostAddressPort& server) {
server_ = server;
- connection_ = connectionFactory_->createConnection();
- connection_->onConnectFinished.connect(boost::bind(&HTTPConnectProxiedConnection::handleConnectionConnectFinished, shared_from_this(), _1));
- connection_->onDataRead.connect(boost::bind(&HTTPConnectProxiedConnection::handleDataRead, shared_from_this(), _1));
- connection_->onDisconnected.connect(boost::bind(&HTTPConnectProxiedConnection::handleDisconnected, shared_from_this(), _1));
- connection_->connect(proxy_);
+ connector_ = Connector::create(proxyHost_, resolver_, connectionFactory_, timerFactory_, proxyPort_);
+ connector_->onConnectFinished.connect(boost::bind(&HTTPConnectProxiedConnection::handleConnectFinished, shared_from_this(), _1));
+ connector_->start();
}
void HTTPConnectProxiedConnection::listen() {
assert(false);
connection_->listen();
}
void HTTPConnectProxiedConnection::disconnect() {
connected_ = false;
connection_->disconnect();
}
void HTTPConnectProxiedConnection::handleDisconnected(const boost::optional<Error>& error) {
onDisconnected(error);
}
void HTTPConnectProxiedConnection::write(const SafeByteArray& data) {
connection_->write(data);
}
-void HTTPConnectProxiedConnection::handleConnectionConnectFinished(bool error) {
- connection_->onConnectFinished.disconnect(boost::bind(&HTTPConnectProxiedConnection::handleConnectionConnectFinished, shared_from_this(), _1));
- if (!error) {
+void HTTPConnectProxiedConnection::handleConnectFinished(Connection::ref connection) {
+ cancelConnector();
+ if (connection) {
+ connection_ = connection;
+ connection_->onDataRead.connect(boost::bind(&HTTPConnectProxiedConnection::handleDataRead, shared_from_this(), _1));
+ connection_->onDisconnected.connect(boost::bind(&HTTPConnectProxiedConnection::handleDisconnected, shared_from_this(), _1));
+
std::stringstream connect;
connect << "CONNECT " << server_.getAddress().toString() << ":" << server_.getPort() << " HTTP/1.1\r\n";
SafeByteArray data = createSafeByteArray(connect.str());
if (!authID_.empty() && !authPassword_.empty()) {
append(data, createSafeByteArray("Proxy-Authorization: Basic "));
SafeByteArray credentials = authID_;
append(credentials, createSafeByteArray(":"));
append(credentials, authPassword_);
append(data, Base64::encode(credentials));
append(data, createSafeByteArray("\r\n"));
}
append(data, createSafeByteArray("\r\n"));
SWIFT_LOG(debug) << "HTTP Proxy send headers: " << byteArrayToString(ByteArray(data.begin(), data.end())) << std::endl;
connection_->write(data);
}
else {
onConnectFinished(true);
}
}
void HTTPConnectProxiedConnection::handleDataRead(boost::shared_ptr<SafeByteArray> data) {
if (!connected_) {
SWIFT_LOG(debug) << byteArrayToString(ByteArray(data->begin(), data->end())) << std::endl;
std::vector<std::string> tmp = String::split(byteArrayToString(ByteArray(data->begin(), data->end())), ' ');
if(tmp.size() > 1) {
int status = boost::lexical_cast<int> (tmp[1].c_str());
SWIFT_LOG(debug) << "Proxy Status: " << status << std::endl;
if (status / 100 == 2) { // all 2XX states are OK
connected_ = true;
onConnectFinished(false);
return;
}
SWIFT_LOG(debug) << "HTTP Proxy returned an error: " << byteArrayToString(ByteArray(data->begin(), data->end())) << std::endl;
}
disconnect();
diff --git a/Swiften/Network/HTTPConnectProxiedConnection.h b/Swiften/Network/HTTPConnectProxiedConnection.h
index 02d3edd..8318ecc 100644
--- a/Swiften/Network/HTTPConnectProxiedConnection.h
+++ b/Swiften/Network/HTTPConnectProxiedConnection.h
@@ -1,64 +1,71 @@
/*
* Copyright (c) 2010-2011 Thilo Cestonaro
* Licensed under the simplified BSD license.
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
/*
- * Copyright (c) 2011 Kevin Smith
+ * Copyright (c) 2011-2012 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#pragma once
#include <boost/enable_shared_from_this.hpp>
#include <Swiften/Network/Connection.h>
+#include <Swiften/Network/Connector.h>
#include <Swiften/Network/HostAddressPort.h>
#include <Swiften/Base/SafeString.h>
namespace boost {
class thread;
namespace system {
class error_code;
}
}
namespace Swift {
class ConnectionFactory;
+ class EventLoop;
class HTTPConnectProxiedConnection : public Connection, public boost::enable_shared_from_this<HTTPConnectProxiedConnection> {
public:
typedef boost::shared_ptr<HTTPConnectProxiedConnection> ref;
~HTTPConnectProxiedConnection();
- static ref create(ConnectionFactory* connectionFactory, HostAddressPort proxy, const SafeString& authID, const SafeString& authPassword) {
- return ref(new HTTPConnectProxiedConnection(connectionFactory, proxy, authID, authPassword));
+ static ref create(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, EventLoop* eventLoop, const std::string& proxyHost, int proxyPort, const SafeString& authID, const SafeString& authPassword) {
+ return ref(new HTTPConnectProxiedConnection(resolver, connectionFactory, timerFactory, eventLoop, proxyHost, proxyPort, authID, authPassword));
}
virtual void listen();
virtual void connect(const HostAddressPort& address);
virtual void disconnect();
virtual void write(const SafeByteArray& data);
virtual HostAddressPort getLocalAddress() const;
private:
- HTTPConnectProxiedConnection(ConnectionFactory* connectionFactory, HostAddressPort proxy, const SafeString& authID, const SafeString& authPassword);
+ HTTPConnectProxiedConnection(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, EventLoop* eventLoop, const std::string& proxyHost, int proxyPort, const SafeString& authID, const SafeString& authPassword);
- void handleConnectionConnectFinished(bool error);
+ void handleConnectFinished(Connection::ref connection);
void handleDataRead(boost::shared_ptr<SafeByteArray> data);
void handleDisconnected(const boost::optional<Error>& error);
+ void cancelConnector();
private:
bool connected_;
+ DomainNameResolver* resolver_;
ConnectionFactory* connectionFactory_;
- HostAddressPort proxy_;
+ TimerFactory* timerFactory_;
+ std::string proxyHost_;
+ int proxyPort_;
HostAddressPort server_;
SafeByteArray authID_;
SafeByteArray authPassword_;
+ Connector::ref connector_;
boost::shared_ptr<Connection> connection_;
};
}
diff --git a/Swiften/Network/HTTPConnectProxiedConnectionFactory.cpp b/Swiften/Network/HTTPConnectProxiedConnectionFactory.cpp
index 6ad0228..14d702c 100644
--- a/Swiften/Network/HTTPConnectProxiedConnectionFactory.cpp
+++ b/Swiften/Network/HTTPConnectProxiedConnectionFactory.cpp
@@ -1,24 +1,30 @@
/*
+ * Copyright (c) 2012 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+/*
* Copyright (c) 2010-2011 Thilo Cestonaro
* Licensed under the simplified BSD license.
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
#include <Swiften/Network/HTTPConnectProxiedConnectionFactory.h>
#include <Swiften/Network/HTTPConnectProxiedConnection.h>
namespace Swift {
-HTTPConnectProxiedConnectionFactory::HTTPConnectProxiedConnectionFactory(ConnectionFactory* connectionFactory, const HostAddressPort& proxy) : connectionFactory_(connectionFactory), proxy_(proxy), authID_(""), authPassword_("") {
+HTTPConnectProxiedConnectionFactory::HTTPConnectProxiedConnectionFactory(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, EventLoop* eventLoop, const std::string& proxyHost, int proxyPort) : resolver_(resolver), connectionFactory_(connectionFactory), timerFactory_(timerFactory), eventLoop_(eventLoop), proxyHost_(proxyHost), proxyPort_(proxyPort), authID_(""), authPassword_("") {
}
-HTTPConnectProxiedConnectionFactory::HTTPConnectProxiedConnectionFactory(ConnectionFactory* connectionFactory, const HostAddressPort& proxy, const SafeString& authID, const SafeString& authPassword) : connectionFactory_(connectionFactory), proxy_(proxy), authID_(authID), authPassword_(authPassword) {
+HTTPConnectProxiedConnectionFactory::HTTPConnectProxiedConnectionFactory(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, EventLoop* eventLoop, const std::string& proxyHost, int proxyPort, const SafeString& authID, const SafeString& authPassword) : resolver_(resolver), connectionFactory_(connectionFactory), timerFactory_(timerFactory), eventLoop_(eventLoop), proxyHost_(proxyHost), proxyPort_(proxyPort), authID_(authID), authPassword_(authPassword) {
}
boost::shared_ptr<Connection> HTTPConnectProxiedConnectionFactory::createConnection() {
- return HTTPConnectProxiedConnection::create(connectionFactory_, proxy_, authID_, authPassword_);
+ return HTTPConnectProxiedConnection::create(resolver_, connectionFactory_, timerFactory_, eventLoop_, proxyHost_, proxyPort_, authID_, authPassword_);
}
}
diff --git a/Swiften/Network/HTTPConnectProxiedConnectionFactory.h b/Swiften/Network/HTTPConnectProxiedConnectionFactory.h
index ef3af66..7d003e8 100644
--- a/Swiften/Network/HTTPConnectProxiedConnectionFactory.h
+++ b/Swiften/Network/HTTPConnectProxiedConnectionFactory.h
@@ -1,27 +1,40 @@
/*
+ * Copyright (c) 2012 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+/*
* Copyright (c) 2010-2011 Thilo Cestonaro
* Licensed under the simplified BSD license.
* See Documentation/Licenses/BSD-simplified.txt for more information.
*/
#pragma once
#include <Swiften/Network/ConnectionFactory.h>
#include <Swiften/Network/HostAddressPort.h>
#include <Swiften/Base/SafeString.h>
namespace Swift {
+ class DomainNameResolver;
+ class TimerFactory;
+ class EventLoop;
class HTTPConnectProxiedConnectionFactory : public ConnectionFactory {
public:
- HTTPConnectProxiedConnectionFactory(ConnectionFactory* connectionFactory, const HostAddressPort& proxy);
- HTTPConnectProxiedConnectionFactory(ConnectionFactory* connectionFactory, const HostAddressPort& proxy, const SafeString& authID, const SafeString& authPassword);
+ HTTPConnectProxiedConnectionFactory(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, EventLoop* eventLoop, const std::string& proxyHost, int proxyPort);
+ HTTPConnectProxiedConnectionFactory(DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory, EventLoop* eventLoop, const std::string& proxyHost, int proxyPort, const SafeString& authID, const SafeString& authPassword);
virtual boost::shared_ptr<Connection> createConnection();
private:
+ DomainNameResolver* resolver_;
ConnectionFactory* connectionFactory_;
- HostAddressPort proxy_;
+ TimerFactory* timerFactory_;
+ EventLoop* eventLoop_;
+ std::string proxyHost_;
+ int proxyPort_;
SafeString authID_;
SafeString authPassword_;
};
}
diff --git a/Swiften/Network/SConscript b/Swiften/Network/SConscript
index 6975fe6..0d0abbd 100644
--- a/Swiften/Network/SConscript
+++ b/Swiften/Network/SConscript
@@ -1,54 +1,55 @@
Import("swiften_env")
myenv = swiften_env.Clone()
myenv.MergeFlags(myenv["LIBIDN_FLAGS"])
if myenv.get("HAVE_CARES", False) :
myenv.MergeFlags(myenv.get("CARES_FLAGS", {}))
sourceList = [
"HTTPConnectProxiedConnection.cpp",
"HTTPConnectProxiedConnectionFactory.cpp",
"SOCKS5ProxiedConnection.cpp",
"SOCKS5ProxiedConnectionFactory.cpp",
"BoostConnection.cpp",
"BoostConnectionFactory.cpp",
"BoostConnectionServer.cpp",
"BoostConnectionServerFactory.cpp",
"BoostIOServiceThread.cpp",
"BOSHConnection.cpp",
"BOSHConnectionPool.cpp",
+ "CachingNameOnlyDomainNameResolver.cpp",
"ConnectionFactory.cpp",
"ConnectionServer.cpp",
"ConnectionServerFactory.cpp",
"DummyConnection.cpp",
"FakeConnection.cpp",
"ChainedConnector.cpp",
"Connector.cpp",
"Connection.cpp",
"TimerFactory.cpp",
"DummyTimerFactory.cpp",
"BoostTimerFactory.cpp",
"DomainNameResolver.cpp",
"DomainNameAddressQuery.cpp",
"DomainNameServiceQuery.cpp",
"PlatformDomainNameResolver.cpp",
"PlatformDomainNameServiceQuery.cpp",
"PlatformDomainNameAddressQuery.cpp",
"StaticDomainNameResolver.cpp",
"HostAddress.cpp",
"HostAddressPort.cpp",
"NetworkFactories.cpp",
"BoostNetworkFactories.cpp",
"NetworkEnvironment.cpp",
"Timer.cpp",
"TLSConnection.cpp",
"TLSConnectionFactory.cpp",
"BoostTimer.cpp",
"ProxyProvider.cpp",
"NullProxyProvider.cpp",
"NATTraverser.cpp",
"NullNATTraverser.cpp",
"NATTraversalGetPublicIPRequest.cpp",
"NATTraversalForwardPortRequest.cpp",
"NATTraversalRemovePortForwardingRequest.cpp",
"NATTraversalInterface.cpp",
diff --git a/Swiften/Network/StaticDomainNameResolver.cpp b/Swiften/Network/StaticDomainNameResolver.cpp
index 76ab411..ee18ee5 100644
--- a/Swiften/Network/StaticDomainNameResolver.cpp
+++ b/Swiften/Network/StaticDomainNameResolver.cpp
@@ -1,104 +1,119 @@
/*
* Copyright (c) 2010 Remko Tronçon
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#include <Swiften/Network/StaticDomainNameResolver.h>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#include <Swiften/Network/DomainNameResolveError.h>
+#include <Swiften/EventLoop/EventOwner.h>
#include <string>
using namespace Swift;
namespace {
struct ServiceQuery : public DomainNameServiceQuery, public boost::enable_shared_from_this<ServiceQuery> {
- ServiceQuery(const std::string& service, Swift::StaticDomainNameResolver* resolver, EventLoop* eventLoop) : eventLoop(eventLoop), service(service), resolver(resolver) {}
+ ServiceQuery(const std::string& service, Swift::StaticDomainNameResolver* resolver, EventLoop* eventLoop, boost::shared_ptr<EventOwner> owner) : eventLoop(eventLoop), service(service), resolver(resolver), owner(owner) {}
virtual void run() {
if (!resolver->getIsResponsive()) {
return;
}
std::vector<DomainNameServiceQuery::Result> results;
for(StaticDomainNameResolver::ServicesCollection::const_iterator i = resolver->getServices().begin(); i != resolver->getServices().end(); ++i) {
if (i->first == service) {
results.push_back(i->second);
}
}
- eventLoop->postEvent(boost::bind(&ServiceQuery::emitOnResult, shared_from_this(), results));
+ eventLoop->postEvent(boost::bind(&ServiceQuery::emitOnResult, shared_from_this(), results), owner);
}
void emitOnResult(std::vector<DomainNameServiceQuery::Result> results) {
onResult(results);
}
EventLoop* eventLoop;
std::string service;
StaticDomainNameResolver* resolver;
+ boost::shared_ptr<EventOwner> owner;
};
struct AddressQuery : public DomainNameAddressQuery, public boost::enable_shared_from_this<AddressQuery> {
- AddressQuery(const std::string& host, StaticDomainNameResolver* resolver, EventLoop* eventLoop) : eventLoop(eventLoop), host(host), resolver(resolver) {}
+ AddressQuery(const std::string& host, StaticDomainNameResolver* resolver, EventLoop* eventLoop, boost::shared_ptr<EventOwner> owner) : eventLoop(eventLoop), host(host), resolver(resolver), owner(owner) {}
virtual void run() {
if (!resolver->getIsResponsive()) {
return;
}
StaticDomainNameResolver::AddressesMap::const_iterator i = resolver->getAddresses().find(host);
if (i != resolver->getAddresses().end()) {
eventLoop->postEvent(
boost::bind(&AddressQuery::emitOnResult, shared_from_this(), i->second, boost::optional<DomainNameResolveError>()));
}
else {
- eventLoop->postEvent(boost::bind(&AddressQuery::emitOnResult, shared_from_this(), std::vector<HostAddress>(), boost::optional<DomainNameResolveError>(DomainNameResolveError())));
+ eventLoop->postEvent(boost::bind(&AddressQuery::emitOnResult, shared_from_this(), std::vector<HostAddress>(), boost::optional<DomainNameResolveError>(DomainNameResolveError())), owner);
}
}
void emitOnResult(std::vector<HostAddress> results, boost::optional<DomainNameResolveError> error) {
onResult(results, error);
}
EventLoop* eventLoop;
std::string host;
StaticDomainNameResolver* resolver;
+ boost::shared_ptr<EventOwner> owner;
};
}
+class StaticDomainNameResolverEventOwner : public EventOwner {
+ public:
+ ~StaticDomainNameResolverEventOwner() {
+
+ }
+};
+
+
namespace Swift {
-StaticDomainNameResolver::StaticDomainNameResolver(EventLoop* eventLoop) : eventLoop(eventLoop), isResponsive(true) {
+StaticDomainNameResolver::StaticDomainNameResolver(EventLoop* eventLoop) : eventLoop(eventLoop), isResponsive(true), owner(new StaticDomainNameResolverEventOwner()) {
+}
+
+StaticDomainNameResolver::~StaticDomainNameResolver() {
+ eventLoop->removeEventsFromOwner(owner);
}
void StaticDomainNameResolver::addAddress(const std::string& domain, const HostAddress& address) {
addresses[domain].push_back(address);
}
void StaticDomainNameResolver::addService(const std::string& service, const DomainNameServiceQuery::Result& result) {
services.push_back(std::make_pair(service, result));
}
void StaticDomainNameResolver::addXMPPClientService(const std::string& domain, const HostAddressPort& address) {
static int hostid = 0;
std::string hostname(std::string("host-") + boost::lexical_cast<std::string>(hostid));
hostid++;
addService("_xmpp-client._tcp." + domain, ServiceQuery::Result(hostname, address.getPort(), 0, 0));
addAddress(hostname, address.getAddress());
}
void StaticDomainNameResolver::addXMPPClientService(const std::string& domain, const std::string& hostname, int port) {
addService("_xmpp-client._tcp." + domain, ServiceQuery::Result(hostname, port, 0, 0));
}
boost::shared_ptr<DomainNameServiceQuery> StaticDomainNameResolver::createServiceQuery(const std::string& name) {
- return boost::shared_ptr<DomainNameServiceQuery>(new ServiceQuery(name, this, eventLoop));
+ return boost::shared_ptr<DomainNameServiceQuery>(new ServiceQuery(name, this, eventLoop, owner));
}
boost::shared_ptr<DomainNameAddressQuery> StaticDomainNameResolver::createAddressQuery(const std::string& name) {
- return boost::shared_ptr<DomainNameAddressQuery>(new AddressQuery(name, this, eventLoop));
+ return boost::shared_ptr<DomainNameAddressQuery>(new AddressQuery(name, this, eventLoop, owner));
}
}
diff --git a/Swiften/Network/StaticDomainNameResolver.h b/Swiften/Network/StaticDomainNameResolver.h
index a72db7c..29d1629 100644
--- a/Swiften/Network/StaticDomainNameResolver.h
+++ b/Swiften/Network/StaticDomainNameResolver.h
@@ -1,60 +1,59 @@
/*
* Copyright (c) 2010 Remko Tronçon
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#pragma once
#include <vector>
#include <map>
#include <Swiften/Network/HostAddress.h>
#include <Swiften/Network/HostAddressPort.h>
#include <Swiften/Network/DomainNameResolver.h>
#include <Swiften/Network/DomainNameServiceQuery.h>
#include <Swiften/Network/DomainNameAddressQuery.h>
#include <Swiften/EventLoop/EventLoop.h>
namespace Swift {
-
-
class StaticDomainNameResolver : public DomainNameResolver {
public:
typedef std::map<std::string, std::vector<HostAddress> > AddressesMap;
typedef std::vector< std::pair<std::string, DomainNameServiceQuery::Result> > ServicesCollection;
public:
StaticDomainNameResolver(EventLoop* eventLoop);
+ ~StaticDomainNameResolver();
void addAddress(const std::string& domain, const HostAddress& address);
void addService(const std::string& service, const DomainNameServiceQuery::Result& result);
void addXMPPClientService(const std::string& domain, const HostAddressPort&);
void addXMPPClientService(const std::string& domain, const std::string& host, int port);
const AddressesMap& getAddresses() const {
return addresses;
}
const ServicesCollection& getServices() const {
return services;
}
bool getIsResponsive() const {
return isResponsive;
}
void setIsResponsive(bool b) {
isResponsive = b;
}
virtual boost::shared_ptr<DomainNameServiceQuery> createServiceQuery(const std::string& name);
virtual boost::shared_ptr<DomainNameAddressQuery> createAddressQuery(const std::string& name);
-
private:
EventLoop* eventLoop;
bool isResponsive;
AddressesMap addresses;
ServicesCollection services;
+ boost::shared_ptr<EventOwner> owner;
};
}
diff --git a/Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp b/Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp
index 1bfee10..82762c5 100644
--- a/Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp
+++ b/Swiften/Network/UnitTest/BOSHConnectionPoolTest.cpp
@@ -1,322 +1,352 @@
/*
* Copyright (c) 2011 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#include <QA/Checker/IO.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <boost/optional.hpp>
#include <boost/bind.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/lexical_cast.hpp>
#include <Swiften/Base/Algorithm.h>
#include <Swiften/Network/Connection.h>
#include <Swiften/Network/ConnectionFactory.h>
#include <Swiften/Network/BOSHConnection.h>
#include <Swiften/Network/BOSHConnectionPool.h>
#include <Swiften/Network/HostAddressPort.h>
+#include <Swiften/Network/StaticDomainNameResolver.h>
+#include <Swiften/Network/DummyTimerFactory.h>
#include <Swiften/EventLoop/DummyEventLoop.h>
#include <Swiften/Parser/PlatformXMLParserFactory.h>
+
+
using namespace Swift;
typedef boost::shared_ptr<BOSHConnectionPool> PoolRef;
+
class BOSHConnectionPoolTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(BOSHConnectionPoolTest);
CPPUNIT_TEST(testConnectionCount_OneWrite);
CPPUNIT_TEST(testConnectionCount_TwoWrites);
CPPUNIT_TEST(testConnectionCount_ThreeWrites);
CPPUNIT_TEST(testConnectionCount_ThreeWrites_ManualConnect);
CPPUNIT_TEST(testConnectionCount_ThreeWritesTwoReads);
CPPUNIT_TEST(testSession);
CPPUNIT_TEST(testWrite_Empty);
CPPUNIT_TEST_SUITE_END();
public:
void setUp() {
to = "wonderland.lit";
path = "http-bind";
port = "5280";
sid = "MyShinySID";
initial = "<body wait='60' "
"inactivity='30' "
"polling='5' "
"requests='2' "
"hold='1' "
"maxpause='120' "
"sid='" + sid + "' "
"ver='1.6' "
"from='wonderland.lit' "
"xmlns='http://jabber.org/protocol/httpbind'/>";
eventLoop = new DummyEventLoop();
connectionFactory = new MockConnectionFactory(eventLoop);
boshURL = URL("http", to, 5280, path);
sessionTerminated = 0;
sessionStarted = 0;
initialRID = 2349876;
xmppDataRead.clear();
boshDataRead.clear();
boshDataWritten.clear();
+ resolver = new StaticDomainNameResolver(eventLoop);
+ resolver->addAddress(to, HostAddress("127.0.0.1"));
+ timerFactory = new DummyTimerFactory();
}
void tearDown() {
eventLoop->processEvents();
delete connectionFactory;
+ delete resolver;
+ delete timerFactory;
delete eventLoop;
}
void testConnectionCount_OneWrite() {
PoolRef testling = createTestling();
CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size());
eventLoop->processEvents();
CPPUNIT_ASSERT_EQUAL(0, sessionStarted);
readResponse(initial, connectionFactory->connections[0]);
CPPUNIT_ASSERT_EQUAL(1, sessionStarted);
CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size());
testling->write(createSafeByteArray("<blah/>"));
eventLoop->processEvents();
CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size());
CPPUNIT_ASSERT_EQUAL(1, sessionStarted);
}
void testConnectionCount_TwoWrites() {
PoolRef testling = createTestling();
CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size());
eventLoop->processEvents();
readResponse(initial, connectionFactory->connections[0]);
+ eventLoop->processEvents();
testling->write(createSafeByteArray("<blah/>"));
+ eventLoop->processEvents();
CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size());
testling->write(createSafeByteArray("<bleh/>"));
eventLoop->processEvents();
+ eventLoop->processEvents();
CPPUNIT_ASSERT_EQUAL(st(2), connectionFactory->connections.size());
}
void testConnectionCount_ThreeWrites() {
PoolRef testling = createTestling();
CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size());
eventLoop->processEvents();
readResponse(initial, connectionFactory->connections[0]);
testling->restartStream();
readResponse("<body/>", connectionFactory->connections[0]);
testling->restartStream();
readResponse("<body/>", connectionFactory->connections[0]);
testling->write(createSafeByteArray("<blah/>"));
testling->write(createSafeByteArray("<bleh/>"));
testling->write(createSafeByteArray("<bluh/>"));
eventLoop->processEvents();
CPPUNIT_ASSERT(st(2) >= connectionFactory->connections.size());
}
void testConnectionCount_ThreeWrites_ManualConnect() {
connectionFactory->autoFinishConnect = false;
PoolRef testling = createTestling();
CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size());
CPPUNIT_ASSERT_EQUAL(st(0), boshDataWritten.size()); /* Connection not connected yet, can't send data */
connectionFactory->connections[0]->onConnectFinished(false);
eventLoop->processEvents();
CPPUNIT_ASSERT_EQUAL(st(1), boshDataWritten.size()); /* Connection finished, stream header sent */
readResponse(initial, connectionFactory->connections[0]);
+ eventLoop->processEvents();
CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size());
CPPUNIT_ASSERT_EQUAL(st(1), boshDataWritten.size()); /* Don't respond to initial data with a holding call */
testling->restartStream();
+ eventLoop->processEvents();
readResponse("<body/>", connectionFactory->connections[0]);
+ eventLoop->processEvents();
testling->restartStream();
+ eventLoop->processEvents();
testling->write(createSafeByteArray("<blah/>"));
+ eventLoop->processEvents();
CPPUNIT_ASSERT_EQUAL(st(2), connectionFactory->connections.size());
CPPUNIT_ASSERT_EQUAL(st(3), boshDataWritten.size()); /* New connection isn't up yet. */
connectionFactory->connections[1]->onConnectFinished(false);
eventLoop->processEvents();
CPPUNIT_ASSERT_EQUAL(st(4), boshDataWritten.size()); /* New connection ready. */
testling->write(createSafeByteArray("<bleh/>"));
+ eventLoop->processEvents();
testling->write(createSafeByteArray("<bluh/>"));
CPPUNIT_ASSERT_EQUAL(st(4), boshDataWritten.size()); /* New data can't be sent, no free connections. */
eventLoop->processEvents();
CPPUNIT_ASSERT(st(2) >= connectionFactory->connections.size());
}
void testConnectionCount_ThreeWritesTwoReads() {
boost::shared_ptr<MockConnection> c0;
boost::shared_ptr<MockConnection> c1;
long rid = initialRID;
PoolRef testling = createTestling();
CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size());
c0 = connectionFactory->connections[0];
eventLoop->processEvents();
CPPUNIT_ASSERT_EQUAL(st(1), boshDataWritten.size()); /* header*/
rid++;
readResponse(initial, c0);
CPPUNIT_ASSERT_EQUAL(st(1), boshDataWritten.size());
CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size());
CPPUNIT_ASSERT(!c0->pending);
rid++;
testling->restartStream();
+ eventLoop->processEvents();
readResponse("<body/>", connectionFactory->connections[0]);
rid++;
testling->write(createSafeByteArray("<blah/>"));
eventLoop->processEvents();
CPPUNIT_ASSERT_EQUAL(st(2), connectionFactory->connections.size()); /* 0 was waiting for response, open and send on 1 */
CPPUNIT_ASSERT_EQUAL(st(4), boshDataWritten.size()); /* data */
c1 = connectionFactory->connections[1];
std::string fullBody = "<body rid='" + boost::lexical_cast<std::string>(rid) + "' sid='" + sid + "' xmlns='http://jabber.org/protocol/httpbind'><blah/></body>"; /* check empty write */
CPPUNIT_ASSERT_EQUAL(fullBody, lastBody());
CPPUNIT_ASSERT(c0->pending);
CPPUNIT_ASSERT(c1->pending);
rid++;
readResponse("<body xmlns='http://jabber.org/protocol/httpbind'><message><splatploing/></message></body>", c0); /* Doesn't include necessary attributes - as the support is improved this'll start to fail */
eventLoop->processEvents();
CPPUNIT_ASSERT(!c0->pending);
CPPUNIT_ASSERT(c1->pending);
CPPUNIT_ASSERT_EQUAL(st(4), boshDataWritten.size()); /* don't send empty in [0], still have [1] waiting */
CPPUNIT_ASSERT_EQUAL(st(2), connectionFactory->connections.size());
rid++;
readResponse("<body xmlns='http://jabber.org/protocol/httpbind'><message><splatploing><blittlebarg/></splatploing></message></body>", c1);
eventLoop->processEvents();
CPPUNIT_ASSERT(!c1->pending);
CPPUNIT_ASSERT(c0->pending);
CPPUNIT_ASSERT_EQUAL(st(5), boshDataWritten.size()); /* empty to make room */
CPPUNIT_ASSERT_EQUAL(st(2), connectionFactory->connections.size());
rid++;
testling->write(createSafeByteArray("<bleh/>"));
+ eventLoop->processEvents();
CPPUNIT_ASSERT(c0->pending);
CPPUNIT_ASSERT(c1->pending);
CPPUNIT_ASSERT_EQUAL(st(6), boshDataWritten.size()); /* data */
rid++;
testling->write(createSafeByteArray("<bluh/>"));
CPPUNIT_ASSERT(c0->pending);
CPPUNIT_ASSERT(c1->pending);
CPPUNIT_ASSERT_EQUAL(st(6), boshDataWritten.size()); /* Don't send data, no room */
eventLoop->processEvents();
CPPUNIT_ASSERT_EQUAL(st(2), connectionFactory->connections.size());
}
void testSession() {
to = "prosody.doomsong.co.uk";
+ resolver->addAddress("prosody.doomsong.co.uk", HostAddress("127.0.0.1"));
path = "http-bind/";
boshURL = URL("http", to, 5280, path);
PoolRef testling = createTestling();
CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size());
eventLoop->processEvents();
CPPUNIT_ASSERT_EQUAL(st(1), boshDataWritten.size()); /* header*/
CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size());
std::string response = "<body authid='743da605-4c2e-4de1-afac-ac040dd4a940' xmpp:version='1.0' xmlns:stream='http://etherx.jabber.org/streams' xmlns:xmpp='urn:xmpp:xbosh' inactivity='60' wait='60' polling='5' secure='true' hold='1' from='prosody.doomsong.co.uk' ver='1.6' sid='743da605-4c2e-4de1-afac-ac040dd4a940' requests='2' xmlns='http://jabber.org/protocol/httpbind'><stream:features><auth xmlns='http://jabber.org/features/iq-auth'/><mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><mechanism>SCRAM-SHA-1</mechanism><mechanism>DIGEST-MD5</mechanism></mechanisms></stream:features></body>";
readResponse(response, connectionFactory->connections[0]);
eventLoop->processEvents();
CPPUNIT_ASSERT_EQUAL(st(1), boshDataWritten.size());
CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size());
std::string send = "<auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" mechanism=\"SCRAM-SHA-1\">biwsbj1hZG1pbixyPWZhOWE5ZDhiLWZmMDctNGE4Yy04N2E3LTg4YWRiNDQxZGUwYg==</auth>";
testling->write(createSafeByteArray(send));
eventLoop->processEvents();
CPPUNIT_ASSERT_EQUAL(st(2), boshDataWritten.size());
CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size());
response = "<body xmlns='http://jabber.org/protocol/httpbind' sid='743da605-4c2e-4de1-afac-ac040dd4a940' xmlns:stream = 'http://etherx.jabber.org/streams'><challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>cj1mYTlhOWQ4Yi1mZjA3LTRhOGMtODdhNy04OGFkYjQ0MWRlMGJhZmZlMWNhMy1mMDJkLTQ5NzEtYjkyNS0yM2NlNWQ2MDQyMjYscz1OVGd5WkdWaFptTXRaVE15WXkwMFpXUmhMV0ZqTURRdFpqYzRNbUppWmpGa1pqWXgsaT00MDk2</challenge></body>";
readResponse(response, connectionFactory->connections[0]);
eventLoop->processEvents();
CPPUNIT_ASSERT_EQUAL(st(2), boshDataWritten.size());
CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size());
send = "<response xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">Yz1iaXdzLHI9ZmE5YTlkOGItZmYwNy00YThjLTg3YTctODhhZGI0NDFkZTBiYWZmZTFjYTMtZjAyZC00OTcxLWI5MjUtMjNjZTVkNjA0MjI2LHA9aU11NWt3dDN2VWplU2RqL01Jb3VIRldkZjBnPQ==</response>";
testling->write(createSafeByteArray(send));
eventLoop->processEvents();
CPPUNIT_ASSERT_EQUAL(st(3), boshDataWritten.size());
CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size());
response = "<body xmlns='http://jabber.org/protocol/httpbind' sid='743da605-4c2e-4de1-afac-ac040dd4a940' xmlns:stream = 'http://etherx.jabber.org/streams'><success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>dj1YNmNBY3BBOWxHNjNOOXF2bVQ5S0FacERrVm89</success></body>";
readResponse(response, connectionFactory->connections[0]);
eventLoop->processEvents();
CPPUNIT_ASSERT_EQUAL(st(3), boshDataWritten.size());
CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size());
}
void testWrite_Empty() {
boost::shared_ptr<MockConnection> c0;
PoolRef testling = createTestling();
- c0 = connectionFactory->connections[0];
CPPUNIT_ASSERT_EQUAL(st(1), connectionFactory->connections.size());
- eventLoop->processEvents();
+ c0 = connectionFactory->connections[0];
readResponse(initial, c0);
+ eventLoop->processEvents();
CPPUNIT_ASSERT_EQUAL(st(1), boshDataWritten.size()); /* Shouldn't have sent anything extra */
+ eventLoop->processEvents();
testling->restartStream();
+ eventLoop->processEvents();
CPPUNIT_ASSERT_EQUAL(st(2), boshDataWritten.size());
readResponse("<body></body>", c0);
+ eventLoop->processEvents();
CPPUNIT_ASSERT_EQUAL(st(3), boshDataWritten.size());
std::string fullBody = "<body rid='" + boost::lexical_cast<std::string>(initialRID + 2) + "' sid='" + sid + "' xmlns='http://jabber.org/protocol/httpbind'></body>";
std::string response = boshDataWritten[2];
size_t bodyPosition = response.find("\r\n\r\n");
CPPUNIT_ASSERT_EQUAL(fullBody, response.substr(bodyPosition+4));
}
private:
PoolRef createTestling() {
- PoolRef pool = boost::make_shared<BOSHConnectionPool>(boshURL, connectionFactory, &parserFactory, static_cast<TLSContextFactory*>(NULL), to, initialRID, URL(), "", "");
+ BOSHConnectionPool* a = new BOSHConnectionPool(boshURL, resolver, connectionFactory, &parserFactory, static_cast<TLSContextFactory*>(NULL), timerFactory, eventLoop, to, initialRID, URL(), SafeString(""), SafeString(""));
+ PoolRef pool(a);
+ //FIXME: Remko - why does the above work, but the below fail?
+ //PoolRef pool = boost::make_shared<BOSHConnectionPool>(boshURL, resolver, connectionFactory, &parserFactory, static_cast<TLSContextFactory*>(NULL), timerFactory, eventLoop, to, initialRID, URL(), SafeString(""), SafeString(""));
pool->onXMPPDataRead.connect(boost::bind(&BOSHConnectionPoolTest::handleXMPPDataRead, this, _1));
pool->onBOSHDataRead.connect(boost::bind(&BOSHConnectionPoolTest::handleBOSHDataRead, this, _1));
pool->onBOSHDataWritten.connect(boost::bind(&BOSHConnectionPoolTest::handleBOSHDataWritten, this, _1));
pool->onSessionStarted.connect(boost::bind(&BOSHConnectionPoolTest::handleSessionStarted, this));
pool->onSessionTerminated.connect(boost::bind(&BOSHConnectionPoolTest::handleSessionTerminated, this));
+ eventLoop->processEvents();
+ eventLoop->processEvents();
return pool;
}
std::string lastBody() {
std::string response = boshDataWritten[boshDataWritten.size() - 1];
size_t bodyPosition = response.find("\r\n\r\n");
return response.substr(bodyPosition+4);
}
size_t st(int val) {
return static_cast<size_t>(val);
}
void handleXMPPDataRead(const SafeByteArray& d) {
xmppDataRead.push_back(safeByteArrayToString(d));
}
void handleBOSHDataRead(const SafeByteArray& d) {
boshDataRead.push_back(safeByteArrayToString(d));
}
void handleBOSHDataWritten(const SafeByteArray& d) {
boshDataWritten.push_back(safeByteArrayToString(d));
}
void handleSessionStarted() {
sessionStarted++;
}
void handleSessionTerminated() {
sessionTerminated++;
}
struct MockConnection : public Connection {
@@ -374,48 +404,50 @@ class BOSHConnectionPoolTest : public CppUnit::TestFixture {
void readResponse(const std::string& response, boost::shared_ptr<MockConnection> connection) {
connection->pending = false;
boost::shared_ptr<SafeByteArray> data1 = boost::make_shared<SafeByteArray>(createSafeByteArray(
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/xml; charset=utf-8\r\n"
"Access-Control-Allow-Origin: *\r\n"
"Access-Control-Allow-Headers: Content-Type\r\n"
"Content-Length: "));
connection->onDataRead(data1);
boost::shared_ptr<SafeByteArray> data2 = boost::make_shared<SafeByteArray>(createSafeByteArray(boost::lexical_cast<std::string>(response.size())));
connection->onDataRead(data2);
boost::shared_ptr<SafeByteArray> data3 = boost::make_shared<SafeByteArray>(createSafeByteArray("\r\n\r\n"));
connection->onDataRead(data3);
boost::shared_ptr<SafeByteArray> data4 = boost::make_shared<SafeByteArray>(createSafeByteArray(response));
connection->onDataRead(data4);
}
std::string fullRequestFor(const std::string& data) {
std::string body = data;
std::string result = "POST /" + path + " HTTP/1.1\r\n"
+ "Host: " + to + ":" + port + "\r\n"
+ "Content-Type: text/xml; charset=utf-8\r\n"
+ "Content-Length: " + boost::lexical_cast<std::string>(body.size()) + "\r\n\r\n"
+ body;
return result;
}
private:
URL boshURL;
DummyEventLoop* eventLoop;
MockConnectionFactory* connectionFactory;
std::vector<std::string> xmppDataRead;
std::vector<std::string> boshDataRead;
std::vector<std::string> boshDataWritten;
PlatformXMLParserFactory parserFactory;
+ StaticDomainNameResolver* resolver;
+ TimerFactory* timerFactory;
std::string to;
std::string path;
std::string port;
std::string sid;
std::string initial;
long initialRID;
int sessionStarted;
int sessionTerminated;
};
CPPUNIT_TEST_SUITE_REGISTRATION(BOSHConnectionPoolTest);
diff --git a/Swiften/Network/UnitTest/BOSHConnectionTest.cpp b/Swiften/Network/UnitTest/BOSHConnectionTest.cpp
index 8062bea..e0dc0bf 100644
--- a/Swiften/Network/UnitTest/BOSHConnectionTest.cpp
+++ b/Swiften/Network/UnitTest/BOSHConnectionTest.cpp
@@ -1,90 +1,96 @@
/*
* Copyright (c) 2011 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#include <QA/Checker/IO.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <boost/optional.hpp>
#include <boost/bind.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/lexical_cast.hpp>
#include <Swiften/Base/Algorithm.h>
#include <Swiften/Network/Connection.h>
#include <Swiften/Network/ConnectionFactory.h>
#include <Swiften/Network/BOSHConnection.h>
#include <Swiften/Network/HostAddressPort.h>
+#include <Swiften/Network/StaticDomainNameResolver.h>
+#include <Swiften/Network/DummyTimerFactory.h>
#include <Swiften/EventLoop/DummyEventLoop.h>
#include <Swiften/Parser/PlatformXMLParserFactory.h>
using namespace Swift;
class BOSHConnectionTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(BOSHConnectionTest);
CPPUNIT_TEST(testHeader);
CPPUNIT_TEST(testReadiness_ok);
CPPUNIT_TEST(testReadiness_pending);
CPPUNIT_TEST(testReadiness_disconnect);
CPPUNIT_TEST(testReadiness_noSID);
CPPUNIT_TEST(testWrite_Receive);
CPPUNIT_TEST(testWrite_ReceiveTwice);
CPPUNIT_TEST(testRead_Fragment);
CPPUNIT_TEST(testHTTPRequest);
CPPUNIT_TEST(testHTTPRequest_Empty);
CPPUNIT_TEST_SUITE_END();
public:
void setUp() {
eventLoop = new DummyEventLoop();
connectionFactory = new MockConnectionFactory(eventLoop);
+ resolver = new StaticDomainNameResolver(eventLoop);
+ timerFactory = new DummyTimerFactory();
connectFinished = false;
disconnected = false;
disconnectedError = false;
dataRead.clear();
}
void tearDown() {
eventLoop->processEvents();
delete connectionFactory;
- delete eventLoop;
+ delete resolver;
+ delete timerFactory;
+ delete eventLoop;
}
void testHeader() {
BOSHConnection::ref testling = createTestling();
testling->connect();
eventLoop->processEvents();
testling->startStream("wonderland.lit", 1);
std::string initial("<body wait='60' "
"inactivity='30' "
"polling='5' "
"requests='2' "
"hold='1' "
"maxpause='120' "
"sid='MyShinySID' "
"ver='1.6' "
"from='wonderland.lit' "
"xmlns='http://jabber.org/protocol/httpbind'/>");
readResponse(initial, connectionFactory->connections[0]);
CPPUNIT_ASSERT_EQUAL(std::string("MyShinySID"), sid);
CPPUNIT_ASSERT(testling->isReadyToSend());
}
void testReadiness_ok() {
BOSHConnection::ref testling = createTestling();
testling->connect();
eventLoop->processEvents();
testling->setSID("blahhhhh");
CPPUNIT_ASSERT(testling->isReadyToSend());
}
void testReadiness_pending() {
BOSHConnection::ref testling = createTestling();
testling->connect();
eventLoop->processEvents();
testling->setSID("mySID");
@@ -153,71 +159,73 @@ class BOSHConnectionTest : public CppUnit::TestFixture {
boost::shared_ptr<SafeByteArray> data2 = boost::make_shared<SafeByteArray>(createSafeByteArray(
"\r\n<body xmlns='http://jabber.org/protocol/httpbind'>"
"<bl"));
boost::shared_ptr<SafeByteArray> data3 = boost::make_shared<SafeByteArray>(createSafeByteArray(
"ah/>"
"</body>"));
connection->onDataRead(data1);
connection->onDataRead(data2);
CPPUNIT_ASSERT(dataRead.empty());
connection->onDataRead(data3);
CPPUNIT_ASSERT_EQUAL(std::string("<blah/>"), byteArrayToString(dataRead));
}
void testHTTPRequest() {
std::string data = "<blah/>";
std::string sid = "wigglebloom";
std::string fullBody = "<body xmlns='http://jabber.org/protocol/httpbind' sid='" + sid + "' rid='20'>" + data + "</body>";
std::pair<SafeByteArray, size_t> http = BOSHConnection::createHTTPRequest(createSafeByteArray(data), false, false, 20, sid, URL());
CPPUNIT_ASSERT_EQUAL(fullBody.size(), http.second);
}
void testHTTPRequest_Empty() {
std::string data = "";
std::string sid = "wigglebloomsickle";
std::string fullBody = "<body rid='42' sid='" + sid + "' xmlns='http://jabber.org/protocol/httpbind'>" + data + "</body>";
std::pair<SafeByteArray, size_t> http = BOSHConnection::createHTTPRequest(createSafeByteArray(data), false, false, 42, sid, URL());
CPPUNIT_ASSERT_EQUAL(fullBody.size(), http.second);
std::string response = safeByteArrayToString(http.first);
size_t bodyPosition = response.find("\r\n\r\n");
CPPUNIT_ASSERT_EQUAL(fullBody, response.substr(bodyPosition+4));
}
private:
BOSHConnection::ref createTestling() {
- BOSHConnection::ref c = BOSHConnection::create(URL("http", "wonderland.lit", 5280, "http-bind"), connectionFactory, &parserFactory, static_cast<TLSContextFactory*>(NULL));
+ resolver->addAddress("wonderland.lit", HostAddress("127.0.0.1"));
+ Connector::ref connector = Connector::create("wonderland.lit", resolver, connectionFactory, timerFactory, 5280);
+ BOSHConnection::ref c = BOSHConnection::create(URL("http", "wonderland.lit", 5280, "http-bind"), connector, &parserFactory);
c->onConnectFinished.connect(boost::bind(&BOSHConnectionTest::handleConnectFinished, this, _1));
c->onDisconnected.connect(boost::bind(&BOSHConnectionTest::handleDisconnected, this, _1));
c->onXMPPDataRead.connect(boost::bind(&BOSHConnectionTest::handleDataRead, this, _1));
c->onSessionStarted.connect(boost::bind(&BOSHConnectionTest::handleSID, this, _1));
c->setRID(42);
return c;
}
void handleConnectFinished(bool error) {
connectFinished = true;
connectFinishedWithError = error;
}
void handleDisconnected(bool e) {
disconnected = true;
disconnectedError = e;
}
void handleDataRead(const SafeByteArray& d) {
append(dataRead, d);
}
void handleSID(const std::string& s) {
sid = s;
}
struct MockConnection : public Connection {
public:
MockConnection(const std::vector<HostAddressPort>& failingPorts, EventLoop* eventLoop) : eventLoop(eventLoop), failingPorts(failingPorts), disconnected(false) {
}
void listen() { assert(false); }
void connect(const HostAddressPort& address) {
hostAddressPort = address;
@@ -252,41 +260,43 @@ class BOSHConnectionTest : public CppUnit::TestFixture {
connections.push_back(connection);
return connection;
}
EventLoop* eventLoop;
std::vector< boost::shared_ptr<MockConnection> > connections;
std::vector<HostAddressPort> failingPorts;
};
void readResponse(const std::string& response, boost::shared_ptr<MockConnection> connection) {
boost::shared_ptr<SafeByteArray> data1 = boost::make_shared<SafeByteArray>(createSafeByteArray(
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/xml; charset=utf-8\r\n"
"Access-Control-Allow-Origin: *\r\n"
"Access-Control-Allow-Headers: Content-Type\r\n"
"Content-Length: "));
connection->onDataRead(data1);
boost::shared_ptr<SafeByteArray> data2 = boost::make_shared<SafeByteArray>(createSafeByteArray(boost::lexical_cast<std::string>(response.size())));
connection->onDataRead(data2);
boost::shared_ptr<SafeByteArray> data3 = boost::make_shared<SafeByteArray>(createSafeByteArray("\r\n\r\n"));
connection->onDataRead(data3);
boost::shared_ptr<SafeByteArray> data4 = boost::make_shared<SafeByteArray>(createSafeByteArray(response));
connection->onDataRead(data4);
}
private:
DummyEventLoop* eventLoop;
MockConnectionFactory* connectionFactory;
bool connectFinished;
bool connectFinishedWithError;
bool disconnected;
bool disconnectedError;
ByteArray dataRead;
PlatformXMLParserFactory parserFactory;
+ StaticDomainNameResolver* resolver;
+ TimerFactory* timerFactory;
std::string sid;
};
CPPUNIT_TEST_SUITE_REGISTRATION(BOSHConnectionTest);
diff --git a/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp b/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp
index c0252d4..347a145 100644
--- a/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp
+++ b/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp
@@ -1,246 +1,257 @@
/*
* Copyright (c) 2010 Remko Tronçon
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#include <QA/Checker/IO.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <boost/optional.hpp>
#include <boost/bind.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <Swiften/Base/Algorithm.h>
#include <Swiften/Network/Connection.h>
#include <Swiften/Network/ConnectionFactory.h>
#include <Swiften/Network/HTTPConnectProxiedConnection.h>
#include <Swiften/Network/HostAddressPort.h>
+#include <Swiften/Network/StaticDomainNameResolver.h>
+#include <Swiften/Network/DummyTimerFactory.h>
#include <Swiften/EventLoop/DummyEventLoop.h>
using namespace Swift;
class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(HTTPConnectProxiedConnectionTest);
CPPUNIT_TEST(testConnect_CreatesConnectionToProxy);
CPPUNIT_TEST(testConnect_SendsConnectRequest);
CPPUNIT_TEST(testConnect_ReceiveConnectResponse);
CPPUNIT_TEST(testConnect_ReceiveMalformedConnectResponse);
CPPUNIT_TEST(testConnect_ReceiveErrorConnectResponse);
CPPUNIT_TEST(testConnect_ReceiveDataAfterConnect);
CPPUNIT_TEST(testWrite_AfterConnect);
CPPUNIT_TEST(testDisconnect_AfterConnectRequest);
CPPUNIT_TEST(testDisconnect_AfterConnect);
CPPUNIT_TEST_SUITE_END();
public:
void setUp() {
- proxyHost = HostAddressPort(HostAddress("1.1.1.1"), 1234);
+ proxyHost = "doo.bah";
+ proxyPort = 1234;
+ proxyHostAddress = HostAddressPort(HostAddress("1.1.1.1"), proxyPort);
host = HostAddressPort(HostAddress("2.2.2.2"), 2345);
eventLoop = new DummyEventLoop();
+ resolver = new StaticDomainNameResolver(eventLoop);
+ resolver->addAddress(proxyHost, proxyHostAddress.getAddress());
+ timerFactory = new DummyTimerFactory();
connectionFactory = new MockConnectionFactory(eventLoop);
connectFinished = false;
disconnected = false;
}
void tearDown() {
+ delete timerFactory;
delete connectionFactory;
+ delete resolver;
delete eventLoop;
}
+ void connect(HTTPConnectProxiedConnection::ref connection, const HostAddressPort& to) {
+ connection->connect(to);
+ eventLoop->processEvents();
+ eventLoop->processEvents();
+ eventLoop->processEvents();
+ }
+
void testConnect_CreatesConnectionToProxy() {
HTTPConnectProxiedConnection::ref testling(createTestling());
- testling->connect(host);
- eventLoop->processEvents();
+ connect(testling, host);
CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connectionFactory->connections.size()));
CPPUNIT_ASSERT(connectionFactory->connections[0]->hostAddressPort);
- CPPUNIT_ASSERT(proxyHost == *connectionFactory->connections[0]->hostAddressPort);
+ CPPUNIT_ASSERT(proxyHostAddress == *connectionFactory->connections[0]->hostAddressPort);
CPPUNIT_ASSERT(!connectFinished);
}
void testConnect_SendsConnectRequest() {
HTTPConnectProxiedConnection::ref testling(createTestling());
- testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345));
- eventLoop->processEvents();
+ connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345));
CPPUNIT_ASSERT_EQUAL(createByteArray("CONNECT 2.2.2.2:2345 HTTP/1.1\r\n\r\n"), connectionFactory->connections[0]->dataWritten);
}
void testConnect_ReceiveConnectResponse() {
HTTPConnectProxiedConnection::ref testling(createTestling());
- testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345));
- eventLoop->processEvents();
+ connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345));
connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef("HTTP/1.0 200 Connection established\r\n\r\n"));
eventLoop->processEvents();
CPPUNIT_ASSERT(connectFinished);
CPPUNIT_ASSERT(!connectFinishedWithError);
CPPUNIT_ASSERT(dataRead.empty());
}
void testConnect_ReceiveMalformedConnectResponse() {
HTTPConnectProxiedConnection::ref testling(createTestling());
- testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345));
- eventLoop->processEvents();
+ connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345));
connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef("FLOOP"));
eventLoop->processEvents();
CPPUNIT_ASSERT(connectFinished);
CPPUNIT_ASSERT(connectFinishedWithError);
CPPUNIT_ASSERT(connectionFactory->connections[0]->disconnected);
}
void testConnect_ReceiveErrorConnectResponse() {
HTTPConnectProxiedConnection::ref testling(createTestling());
- testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345));
- eventLoop->processEvents();
+ connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345));
connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef("HTTP/1.0 401 Unauthorized\r\n\r\n"));
eventLoop->processEvents();
CPPUNIT_ASSERT(connectFinished);
CPPUNIT_ASSERT(connectFinishedWithError);
CPPUNIT_ASSERT(connectionFactory->connections[0]->disconnected);
}
void testConnect_ReceiveDataAfterConnect() {
HTTPConnectProxiedConnection::ref testling(createTestling());
- testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345));
- eventLoop->processEvents();
+ connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345));
connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef("HTTP/1.0 200 Connection established\r\n\r\n"));
eventLoop->processEvents();
connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef("abcdef"));
CPPUNIT_ASSERT_EQUAL(createByteArray("abcdef"), dataRead);
}
void testWrite_AfterConnect() {
HTTPConnectProxiedConnection::ref testling(createTestling());
- testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345));
- eventLoop->processEvents();
+ connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345));
connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef("HTTP/1.0 200 Connection established\r\n\r\n"));
eventLoop->processEvents();
connectionFactory->connections[0]->dataWritten.clear();
testling->write(createSafeByteArray("abcdef"));
CPPUNIT_ASSERT_EQUAL(createByteArray("abcdef"), connectionFactory->connections[0]->dataWritten);
}
void testDisconnect_AfterConnectRequest() {
HTTPConnectProxiedConnection::ref testling(createTestling());
- testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345));
- eventLoop->processEvents();
+ connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345));
testling->disconnect();
CPPUNIT_ASSERT(connectionFactory->connections[0]->disconnected);
CPPUNIT_ASSERT(disconnected);
CPPUNIT_ASSERT(!disconnectedError);
}
void testDisconnect_AfterConnect() {
HTTPConnectProxiedConnection::ref testling(createTestling());
- testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345));
- eventLoop->processEvents();
+ connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345));
connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef("HTTP/1.0 200 Connection established\r\n\r\n"));
eventLoop->processEvents();
testling->disconnect();
CPPUNIT_ASSERT(connectionFactory->connections[0]->disconnected);
CPPUNIT_ASSERT(disconnected);
CPPUNIT_ASSERT(!disconnectedError);
}
private:
HTTPConnectProxiedConnection::ref createTestling() {
- boost::shared_ptr<HTTPConnectProxiedConnection> c = HTTPConnectProxiedConnection::create(connectionFactory, proxyHost, "", "");
+ boost::shared_ptr<HTTPConnectProxiedConnection> c = HTTPConnectProxiedConnection::create(resolver, connectionFactory, timerFactory, eventLoop, proxyHost, proxyPort, "", "");
c->onConnectFinished.connect(boost::bind(&HTTPConnectProxiedConnectionTest::handleConnectFinished, this, _1));
c->onDisconnected.connect(boost::bind(&HTTPConnectProxiedConnectionTest::handleDisconnected, this, _1));
c->onDataRead.connect(boost::bind(&HTTPConnectProxiedConnectionTest::handleDataRead, this, _1));
return c;
}
void handleConnectFinished(bool error) {
connectFinished = true;
connectFinishedWithError = error;
}
void handleDisconnected(const boost::optional<Connection::Error>& e) {
disconnected = true;
disconnectedError = e;
}
void handleDataRead(boost::shared_ptr<SafeByteArray> d) {
append(dataRead, *d);
}
struct MockConnection : public Connection {
public:
MockConnection(const std::vector<HostAddressPort>& failingPorts, EventLoop* eventLoop) : eventLoop(eventLoop), failingPorts(failingPorts), disconnected(false) {
}
void listen() { assert(false); }
void connect(const HostAddressPort& address) {
hostAddressPort = address;
bool fail = std::find(failingPorts.begin(), failingPorts.end(), address) != failingPorts.end();
eventLoop->postEvent(boost::bind(boost::ref(onConnectFinished), fail));
}
HostAddressPort getLocalAddress() const { return HostAddressPort(); }
void disconnect() {
disconnected = true;
onDisconnected(boost::optional<Connection::Error>());
}
void write(const SafeByteArray& d) {
append(dataWritten, d);
}
EventLoop* eventLoop;
boost::optional<HostAddressPort> hostAddressPort;
std::vector<HostAddressPort> failingPorts;
ByteArray dataWritten;
bool disconnected;
};
struct MockConnectionFactory : public ConnectionFactory {
MockConnectionFactory(EventLoop* eventLoop) : eventLoop(eventLoop) {
}
boost::shared_ptr<Connection> createConnection() {
boost::shared_ptr<MockConnection> connection = boost::make_shared<MockConnection>(failingPorts, eventLoop);
connections.push_back(connection);
return connection;
}
EventLoop* eventLoop;
std::vector< boost::shared_ptr<MockConnection> > connections;
std::vector<HostAddressPort> failingPorts;
};
private:
- HostAddressPort proxyHost;
+ std::string proxyHost;
+ HostAddressPort proxyHostAddress;
+ int proxyPort;
HostAddressPort host;
DummyEventLoop* eventLoop;
+ StaticDomainNameResolver* resolver;
MockConnectionFactory* connectionFactory;
+ TimerFactory* timerFactory;
std::vector< boost::shared_ptr<MockConnection> > connections;
bool connectFinished;
bool connectFinishedWithError;
bool disconnected;
boost::optional<Connection::Error> disconnectedError;
ByteArray dataRead;
};
CPPUNIT_TEST_SUITE_REGISTRATION(HTTPConnectProxiedConnectionTest);