summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften/Network/BOSHConnectionPool.cpp')
-rw-r--r--Swiften/Network/BOSHConnectionPool.cpp429
1 files changed, 214 insertions, 215 deletions
diff --git a/Swiften/Network/BOSHConnectionPool.cpp b/Swiften/Network/BOSHConnectionPool.cpp
index 57c1bcc..e4ca471 100644
--- a/Swiften/Network/BOSHConnectionPool.cpp
+++ b/Swiften/Network/BOSHConnectionPool.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011-2015 Isode Limited.
+ * Copyright (c) 2011-2016 Isode Limited.
* All rights reserved.
* See the COPYING file for more information.
*/
@@ -12,290 +12,289 @@
#include <Swiften/Base/Log.h>
#include <Swiften/Base/SafeString.h>
-#include <Swiften/Base/foreach.h>
#include <Swiften/Network/CachingDomainNameResolver.h>
#include <Swiften/Network/HTTPConnectProxiedConnectionFactory.h>
namespace Swift {
-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, const TLSOptions& tlsOptions, boost::shared_ptr<HTTPTrafficFilter> trafficFilter) :
- boshURL(boshURL),
- connectionFactory(connectionFactoryParameter),
- xmlParserFactory(parserFactory),
- timerFactory(timerFactory),
- rid(initialRID),
- pendingTerminate(false),
- to(to),
- requestLimit(2),
- restartCount(0),
- pendingRestart(false),
- tlsContextFactory_(tlsFactory),
- tlsOptions_(tlsOptions) {
-
- if (!boshHTTPConnectProxyURL.isEmpty()) {
- connectionFactory = new HTTPConnectProxiedConnectionFactory(realResolver, connectionFactory, timerFactory, boshHTTPConnectProxyURL.getHost(), URL::getPortOrDefaultPort(boshHTTPConnectProxyURL), boshHTTPConnectProxyAuthID, boshHTTPConnectProxyAuthPassword, trafficFilter);
- }
- resolver = new CachingDomainNameResolver(realResolver, eventLoop);
+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, const TLSOptions& tlsOptions, std::shared_ptr<HTTPTrafficFilter> trafficFilter) :
+ boshURL(boshURL),
+ connectionFactory(connectionFactoryParameter),
+ xmlParserFactory(parserFactory),
+ timerFactory(timerFactory),
+ rid(initialRID),
+ pendingTerminate(false),
+ to(to),
+ requestLimit(2),
+ restartCount(0),
+ pendingRestart(false),
+ tlsContextFactory_(tlsFactory),
+ tlsOptions_(tlsOptions) {
+
+ if (!boshHTTPConnectProxyURL.isEmpty()) {
+ connectionFactory = new HTTPConnectProxiedConnectionFactory(realResolver, connectionFactory, timerFactory, boshHTTPConnectProxyURL.getHost(), URL::getPortOrDefaultPort(boshHTTPConnectProxyURL), boshHTTPConnectProxyAuthID, boshHTTPConnectProxyAuthPassword, trafficFilter);
+ }
+ resolver = new CachingDomainNameResolver(realResolver, eventLoop);
}
BOSHConnectionPool::~BOSHConnectionPool() {
- /* Don't do a normal close here. Instead kill things forcibly, as close() or writeFooter() will already have been called */
- std::vector<BOSHConnection::ref> connectionCopies = connections;
- foreach (BOSHConnection::ref connection, connectionCopies) {
- if (connection) {
- destroyConnection(connection);
- connection->disconnect();
- }
- }
- foreach (ConnectionFactory* factory, myConnectionFactories) {
- delete factory;
- }
- delete resolver;
+ /* Don't do a normal close here. Instead kill things forcibly, as close() or writeFooter() will already have been called */
+ std::vector<BOSHConnection::ref> connectionCopies = connections;
+ for (auto&& connection : connectionCopies) {
+ if (connection) {
+ destroyConnection(connection);
+ connection->disconnect();
+ }
+ }
+ for (auto factory : myConnectionFactories) {
+ delete factory;
+ }
+ delete resolver;
}
void BOSHConnectionPool::write(const SafeByteArray& data) {
- dataQueue.push_back(data);
- tryToSendQueuedData();
+ dataQueue.push_back(data);
+ tryToSendQueuedData();
}
void BOSHConnectionPool::handleDataRead(const SafeByteArray& data) {
- onXMPPDataRead(data);
- tryToSendQueuedData(); /* Will rebalance the connections */
+ 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;
- }
+ BOSHConnection::ref connection = getSuitableConnection();
+ if (connection) {
+ pendingRestart = false;
+ rid++;
+ connection->setRID(rid);
+ connection->restartStream();
+ restartCount++;
+ }
+ else {
+ pendingRestart = true;
+ }
}
void BOSHConnectionPool::setTLSCertificate(CertificateWithKey::ref certWithKey) {
- clientCertificate = certWithKey;
+ clientCertificate = certWithKey;
}
bool BOSHConnectionPool::isTLSEncrypted() const {
- return !pinnedCertificateChain_.empty();
+ return !pinnedCertificateChain_.empty();
}
Certificate::ref BOSHConnectionPool::getPeerCertificate() const {
- Certificate::ref peerCertificate;
- if (!pinnedCertificateChain_.empty()) {
- peerCertificate = pinnedCertificateChain_[0];
- }
- return peerCertificate;
+ Certificate::ref peerCertificate;
+ if (!pinnedCertificateChain_.empty()) {
+ peerCertificate = pinnedCertificateChain_[0];
+ }
+ return peerCertificate;
}
std::vector<Certificate::ref> BOSHConnectionPool::getPeerCertificateChain() const {
- return pinnedCertificateChain_;
+ return pinnedCertificateChain_;
}
-boost::shared_ptr<CertificateVerificationError> BOSHConnectionPool::getPeerCertificateVerificationError() const {
- return lastVerificationError_;
+std::shared_ptr<CertificateVerificationError> BOSHConnectionPool::getPeerCertificateVerificationError() const {
+ return lastVerificationError_;
}
void BOSHConnectionPool::writeFooter() {
- pendingTerminate = true;
- tryToSendQueuedData();
+ pendingTerminate = true;
+ tryToSendQueuedData();
}
void BOSHConnectionPool::open() {
- createConnection();
+ createConnection();
}
void BOSHConnectionPool::close() {
- if (!sid.empty()) {
- writeFooter();
- }
- else {
- pendingTerminate = true;
- std::vector<BOSHConnection::ref> connectionCopies = connections;
- foreach (BOSHConnection::ref connection, connectionCopies) {
- if (connection) {
- connection->disconnect();
- }
- }
- }
+ if (!sid.empty()) {
+ writeFooter();
+ }
+ else {
+ pendingTerminate = true;
+ std::vector<BOSHConnection::ref> connectionCopies = connections;
+ for (auto&& connection : connectionCopies) {
+ if (connection) {
+ connection->disconnect();
+ }
+ }
+ }
}
void BOSHConnectionPool::handleSessionStarted(const std::string& sessionID, size_t requests) {
- sid = sessionID;
- requestLimit = requests;
- onSessionStarted();
+ sid = sessionID;
+ requestLimit = requests;
+ onSessionStarted();
}
void BOSHConnectionPool::handleConnectFinished(bool error, BOSHConnection::ref connection) {
- if (error) {
- onSessionTerminated(boost::make_shared<BOSHError>(BOSHError::UndefinedCondition));
- /*TODO: We can probably manage to not terminate the stream here and use the rid/ack retry
- * logic to just swallow the error and try again (some number of tries).
- */
- }
- else {
- if (connection->getPeerCertificate() && pinnedCertificateChain_.empty()) {
- pinnedCertificateChain_ = connection->getPeerCertificateChain();
- }
- if (!pinnedCertificateChain_.empty()) {
- lastVerificationError_ = connection->getPeerCertificateVerificationError();
- }
-
- if (sid.empty()) {
- connection->startStream(to, rid);
- }
- if (pendingRestart) {
- restartStream();
- }
- tryToSendQueuedData();
- }
+ if (error) {
+ onSessionTerminated(std::make_shared<BOSHError>(BOSHError::UndefinedCondition));
+ /*TODO: We can probably manage to not terminate the stream here and use the rid/ack retry
+ * logic to just swallow the error and try again (some number of tries).
+ */
+ }
+ else {
+ if (connection->getPeerCertificate() && pinnedCertificateChain_.empty()) {
+ pinnedCertificateChain_ = connection->getPeerCertificateChain();
+ }
+ if (!pinnedCertificateChain_.empty()) {
+ lastVerificationError_ = connection->getPeerCertificateVerificationError();
+ }
+
+ if (sid.empty()) {
+ connection->startStream(to, rid);
+ }
+ if (pendingRestart) {
+ restartStream();
+ }
+ tryToSendQueuedData();
+ }
}
BOSHConnection::ref BOSHConnectionPool::getSuitableConnection() {
- BOSHConnection::ref suitableConnection;
- foreach (BOSHConnection::ref connection, connections) {
- if (connection->isReadyToSend()) {
- suitableConnection = connection;
- break;
- }
- }
-
- if (!suitableConnection && connections.size() < requestLimit) {
- /* This is not a suitable connection because it won't have yet connected and added TLS if needed. */
- BOSHConnection::ref newConnection = createConnection();
- newConnection->setSID(sid);
- }
- assert(connections.size() <= requestLimit);
- assert((!suitableConnection) || suitableConnection->isReadyToSend());
- return suitableConnection;
+ BOSHConnection::ref suitableConnection;
+ for (auto&& connection : connections) {
+ if (connection->isReadyToSend()) {
+ suitableConnection = connection;
+ break;
+ }
+ }
+
+ if (!suitableConnection && connections.size() < requestLimit) {
+ /* This is not a suitable connection because it won't have yet connected and added TLS if needed. */
+ BOSHConnection::ref newConnection = createConnection();
+ newConnection->setSID(sid);
+ }
+ assert(connections.size() <= requestLimit);
+ assert((!suitableConnection) || suitableConnection->isReadyToSend());
+ return suitableConnection;
}
void BOSHConnectionPool::tryToSendQueuedData() {
- if (sid.empty()) {
- /* If we've not got as far as stream start yet, pend */
- return;
- }
-
- BOSHConnection::ref suitableConnection = getSuitableConnection();
- bool toSend = !dataQueue.empty();
- if (suitableConnection) {
- if (toSend) {
- rid++;
- suitableConnection->setRID(rid);
- SafeByteArray data;
- foreach (const SafeByteArray& datum, dataQueue) {
- data.insert(data.end(), datum.begin(), datum.end());
- }
- suitableConnection->write(data);
- dataQueue.clear();
- }
- else if (pendingTerminate) {
- rid++;
- suitableConnection->setRID(rid);
- suitableConnection->terminateStream();
- sid = "";
- close();
- }
- }
- if (!pendingTerminate) {
- /* Ensure there's always a session waiting to read data for us */
- bool pending = false;
- foreach (BOSHConnection::ref connection, connections) {
- if (connection && !connection->isReadyToSend()) {
- pending = true;
- }
- }
- if (!pending) {
- if (restartCount >= 1) {
- /* Don't open a second connection until we've restarted the stream twice - i.e. we've authed and resource bound.*/
- if (suitableConnection) {
- rid++;
- suitableConnection->setRID(rid);
- 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. */
- }
- }
- }
- }
+ if (sid.empty()) {
+ /* If we've not got as far as stream start yet, pend */
+ return;
+ }
+
+ BOSHConnection::ref suitableConnection = getSuitableConnection();
+ bool toSend = !dataQueue.empty();
+ if (suitableConnection) {
+ if (toSend) {
+ rid++;
+ suitableConnection->setRID(rid);
+ SafeByteArray data;
+ for (const auto& datum : dataQueue) {
+ data.insert(data.end(), datum.begin(), datum.end());
+ }
+ suitableConnection->write(data);
+ dataQueue.clear();
+ }
+ else if (pendingTerminate) {
+ rid++;
+ suitableConnection->setRID(rid);
+ suitableConnection->terminateStream();
+ sid = "";
+ close();
+ }
+ }
+ if (!pendingTerminate) {
+ /* Ensure there's always a session waiting to read data for us */
+ bool pending = false;
+ for (auto&& connection : connections) {
+ if (connection && !connection->isReadyToSend()) {
+ pending = true;
+ }
+ }
+ if (!pending) {
+ if (restartCount >= 1) {
+ /* Don't open a second connection until we've restarted the stream twice - i.e. we've authed and resource bound.*/
+ if (suitableConnection) {
+ rid++;
+ suitableConnection->setRID(rid);
+ 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));
+ handleSessionTerminated(std::make_shared<BOSHError>(BOSHError::UndefinedCondition));
}
void BOSHConnectionPool::handleConnectionDisconnected(bool/* error*/, BOSHConnection::ref connection) {
- destroyConnection(connection);
- if (pendingTerminate && sid.empty() && connections.empty()) {
- handleSessionTerminated(BOSHError::ref());
- }
- //else if (error) {
- // handleSessionTerminated(boost::make_shared<BOSHError>(BOSHError::UndefinedCondition));
- //}
- else {
- /* We might have just freed up a connection slot to send with */
- tryToSendQueuedData();
- }
+ destroyConnection(connection);
+ if (pendingTerminate && sid.empty() && connections.empty()) {
+ handleSessionTerminated(BOSHError::ref());
+ }
+ //else if (error) {
+ // handleSessionTerminated(std::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() {
- Connector::ref connector = Connector::create(boshURL.getHost(), URL::getPortOrDefaultPort(boshURL), boost::optional<std::string>(), resolver, connectionFactory, timerFactory);
- BOSHConnection::ref connection = BOSHConnection::create(boshURL, connector, xmlParserFactory, tlsContextFactory_, tlsOptions_);
- 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));
-
- if (boshURL.getScheme() == "https") {
- bool success = connection->setClientCertificate(clientCertificate);
- SWIFT_LOG(debug) << "setClientCertificate, success: " << success << std::endl;
- }
-
- connection->connect();
- connections.push_back(connection);
- return connection;
+std::shared_ptr<BOSHConnection> BOSHConnectionPool::createConnection() {
+ Connector::ref connector = Connector::create(boshURL.getHost(), URL::getPortOrDefaultPort(boshURL), boost::optional<std::string>(), resolver, connectionFactory, timerFactory);
+ BOSHConnection::ref connection = BOSHConnection::create(boshURL, connector, xmlParserFactory, tlsContextFactory_, tlsOptions_);
+ 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));
+
+ if (boshURL.getScheme() == "https") {
+ bool success = connection->setClientCertificate(clientCertificate);
+ SWIFT_LOG(debug) << "setClientCertificate, success: " << success << std::endl;
+ }
+
+ 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::destroyConnection(std::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);
+ onSessionTerminated(error);
}
void BOSHConnectionPool::handleBOSHDataRead(const SafeByteArray& data) {
- onBOSHDataRead(data);
+ onBOSHDataRead(data);
}
void BOSHConnectionPool::handleBOSHDataWritten(const SafeByteArray& data) {
- onBOSHDataWritten(data);
+ onBOSHDataWritten(data);
}
}