summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Smith <git@kismith.co.uk>2011-11-12 16:56:21 (GMT)
committerKevin Smith <git@kismith.co.uk>2011-12-13 08:17:58 (GMT)
commit81c09a0f6a3e87b078340d7f35d0dea4c03f3a6d (patch)
tree4371c5808ee26b2b5ed79ace9ccb439ff2988945 /Swiften/Client
parentfd17fe0d239f97cedebe4ceffa234155bd299b68 (diff)
downloadswift-81c09a0f6a3e87b078340d7f35d0dea4c03f3a6d.zip
swift-81c09a0f6a3e87b078340d7f35d0dea4c03f3a6d.tar.bz2
BOSH Support for Swiften
This adds support for BOSH to Swiften. It does not expose it to Swift. Release-Notes: Swiften now allows connects over BOSH, if used appropriately.
Diffstat (limited to 'Swiften/Client')
-rw-r--r--Swiften/Client/ClientOptions.h28
-rw-r--r--Swiften/Client/ClientSession.cpp2
-rw-r--r--Swiften/Client/ClientXMLTracer.cpp17
-rw-r--r--Swiften/Client/ClientXMLTracer.h3
-rw-r--r--Swiften/Client/CoreClient.cpp82
-rw-r--r--Swiften/Client/CoreClient.h5
-rw-r--r--Swiften/Client/UnitTest/ClientSessionTest.cpp4
7 files changed, 105 insertions, 36 deletions
diff --git a/Swiften/Client/ClientOptions.h b/Swiften/Client/ClientOptions.h
index 3b51a87..06bf947 100644
--- a/Swiften/Client/ClientOptions.h
+++ b/Swiften/Client/ClientOptions.h
@@ -6,6 +6,9 @@
#pragma once
+#include <Swiften/Base/URL.h>
+#include <Swiften/Base/SafeString.h>
+
namespace Swift {
struct ClientOptions {
enum UseTLS {
@@ -14,7 +17,7 @@ namespace Swift {
RequireTLS
};
- ClientOptions() : useStreamCompression(true), useTLS(UseTLSWhenAvailable), allowPLAINWithoutTLS(false), useStreamResumption(false), forgetPassword(false), useAcks(true) {
+ ClientOptions() : useStreamCompression(true), useTLS(UseTLSWhenAvailable), allowPLAINWithoutTLS(false), useStreamResumption(false), forgetPassword(false), useAcks(true), boshHTTPConnectProxyAuthID(""), boshHTTPConnectProxyAuthPassword("") {
}
/**
@@ -61,5 +64,28 @@ namespace Swift {
* Default: true
*/
bool useAcks;
+
+ /**
+ * If non-empty, use BOSH instead of direct TCP, with the given URL.
+ * The host currently needs to be specified by IP, rather than hostname.
+ * Default: empty (no BOSH)
+ */
+ URL boshURL;
+
+ /**
+ * If non-empty, BOSH connections will try to connect over this HTTP CONNECT
+ * proxy instead of directly.
+ * Must be specified by IP, rather than hostname.
+ * Default: empty (no proxy)
+ */
+ URL boshHTTPConnectProxyURL;
+
+ /**
+ * If this and matching Password are non-empty, BOSH connections over
+ * HTTP CONNECT proxies will use these credentials for proxy access.
+ * Default: empty (no authentication needed by the proxy)
+ */
+ SafeString boshHTTPConnectProxyAuthID;
+ SafeString boshHTTPConnectProxyAuthPassword;
};
}
diff --git a/Swiften/Client/ClientSession.cpp b/Swiften/Client/ClientSession.cpp
index 55e0bc2..bfc9313 100644
--- a/Swiften/Client/ClientSession.cpp
+++ b/Swiften/Client/ClientSession.cpp
@@ -181,7 +181,7 @@ void ClientSession::handleElement(boost::shared_ptr<Element> element) {
else if (useTLS == RequireTLS && !stream->isTLSEncrypted()) {
finishSession(Error::NoSupportedAuthMechanismsError);
}
- else if (useStreamCompression && streamFeatures->hasCompressionMethod("zlib")) {
+ else if (useStreamCompression && stream->supportsZLibCompression() && streamFeatures->hasCompressionMethod("zlib")) {
state = Compressing;
stream->writeElement(boost::make_shared<CompressRequest>("zlib"));
}
diff --git a/Swiften/Client/ClientXMLTracer.cpp b/Swiften/Client/ClientXMLTracer.cpp
index c1093eb..405e3d1 100644
--- a/Swiften/Client/ClientXMLTracer.cpp
+++ b/Swiften/Client/ClientXMLTracer.cpp
@@ -11,7 +11,7 @@
namespace Swift {
-ClientXMLTracer::ClientXMLTracer(CoreClient* client) {
+ClientXMLTracer::ClientXMLTracer(CoreClient* client, bool bosh) : bosh(bosh) {
beautifier = new XMLBeautifier(true, true);
client->onDataRead.connect(boost::bind(&ClientXMLTracer::printData, this, '<', _1));
client->onDataWritten.connect(boost::bind(&ClientXMLTracer::printData, this, '>', _1));
@@ -23,7 +23,20 @@ ClientXMLTracer::~ClientXMLTracer() {
void ClientXMLTracer::printData(char direction, const SafeByteArray& data) {
printLine(direction);
- std::cerr << beautifier->beautify(byteArrayToString(ByteArray(data.begin(), data.end()))) << std::endl;
+ if (bosh) {
+ std::string line = byteArrayToString(ByteArray(data.begin(), data.end()));
+ size_t endOfHTTP = line.find("\r\n\r\n");
+ if (false && endOfHTTP != std::string::npos) {
+ /* Disabled because it swallows bits of XML (namespaces, if I recall) */
+ std::cerr << line.substr(0, endOfHTTP) << std::endl << beautifier->beautify(line.substr(endOfHTTP)) << std::endl;
+ }
+ else {
+ std::cerr << line << std::endl;
+ }
+ }
+ else {
+ std::cerr << beautifier->beautify(byteArrayToString(ByteArray(data.begin(), data.end()))) << std::endl;
+ }
}
void ClientXMLTracer::printLine(char c) {
diff --git a/Swiften/Client/ClientXMLTracer.h b/Swiften/Client/ClientXMLTracer.h
index 0752faa..67040c4 100644
--- a/Swiften/Client/ClientXMLTracer.h
+++ b/Swiften/Client/ClientXMLTracer.h
@@ -13,7 +13,7 @@
namespace Swift {
class ClientXMLTracer {
public:
- ClientXMLTracer(CoreClient* client);
+ ClientXMLTracer(CoreClient* client, bool bosh = false);
~ClientXMLTracer();
private:
void printData(char direction, const SafeByteArray& data);
@@ -21,5 +21,6 @@ namespace Swift {
private:
XMLBeautifier *beautifier;
+ bool bosh;
};
}
diff --git a/Swiften/Client/CoreClient.cpp b/Swiften/Client/CoreClient.cpp
index 08f31a0..cef2b24 100644
--- a/Swiften/Client/CoreClient.cpp
+++ b/Swiften/Client/CoreClient.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010 Remko Tronçon
+ * Copyright (c) 2010-2011 Remko Tronçon
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
@@ -20,6 +20,7 @@
#include <Swiften/Network/ProxyProvider.h>
#include <Swiften/TLS/PKCS12Certificate.h>
#include <Swiften/Session/BasicSessionStream.h>
+#include <Swiften/Session/BOSHSessionStream.h>
#include <Swiften/Queries/IQRouter.h>
#include <Swiften/Client/ClientSessionStanzaChannel.h>
#include <Swiften/Network/SOCKS5ProxiedConnectionFactory.h>
@@ -70,15 +71,52 @@ void CoreClient::connect(const std::string& host) {
proxyConnectionFactories.push_back(new HTTPConnectProxiedConnectionFactory(networkFactories->getConnectionFactory(), networkFactories->getProxyProvider()->getHTTPConnectProxy()));
}
std::vector<ConnectionFactory*> connectionFactories(proxyConnectionFactories);
- // connectionFactories.push_back(networkFactories->getConnectionFactory());
- connectionFactories.push_back(new BOSHConnectionFactory(networkFactories->getConnectionFactory()));
+ if (options.boshURL.empty()) {
+ connectionFactories.push_back(networkFactories->getConnectionFactory());
+ connector_ = boost::make_shared<ChainedConnector>(host, networkFactories->getDomainNameResolver(), connectionFactories, networkFactories->getTimerFactory());
+ connector_->onConnectFinished.connect(boost::bind(&CoreClient::handleConnectorFinished, this, _1));
+ connector_->setTimeoutMilliseconds(60*1000);
+ connector_->start();
+ }
+ else {
+ /* Autodiscovery of which proxy works is largely ok with a TCP session, because this is a one-off. With BOSH
+ * it would be quite painful given that potentially every stanza could be sent on a new connection.
+ */
+ //sessionStream_ = boost::make_shared<BOSHSessionStream>(boost::make_shared<BOSHConnectionFactory>(options.boshURL, networkFactories->getConnectionFactory(), networkFactories->getXMLParserFactory(), networkFactories->getTLSContextFactory()), getPayloadParserFactories(), getPayloadSerializers(), networkFactories->getTLSContextFactory(), networkFactories->getTimerFactory(), networkFactories->getXMLParserFactory(), networkFactories->getEventLoop(), host, options.boshHTTPConnectProxyURL, options.boshHTTPConnectProxyAuthID, options.boshHTTPConnectProxyAuthPassword);
+ sessionStream_ = boost::shared_ptr<BOSHSessionStream>(new BOSHSessionStream(boost::make_shared<BOSHConnectionFactory>(options.boshURL, networkFactories->getConnectionFactory(), networkFactories->getXMLParserFactory(), networkFactories->getTLSContextFactory()), getPayloadParserFactories(), getPayloadSerializers(), networkFactories->getTLSContextFactory(), networkFactories->getTimerFactory(), networkFactories->getXMLParserFactory(), networkFactories->getEventLoop(), host, options.boshHTTPConnectProxyURL, options.boshHTTPConnectProxyAuthID, options.boshHTTPConnectProxyAuthPassword));
+ sessionStream_->onDataRead.connect(boost::bind(&CoreClient::handleDataRead, this, _1));
+ sessionStream_->onDataWritten.connect(boost::bind(&CoreClient::handleDataWritten, this, _1));
+ bindSessionToStream();
+ }
+
+}
- connector_ = boost::make_shared<ChainedConnector>(host, networkFactories->getDomainNameResolver(), connectionFactories, networkFactories->getTimerFactory());
- connector_->onConnectFinished.connect(boost::bind(&CoreClient::handleConnectorFinished, this, _1));
- connector_->setTimeoutMilliseconds(60*1000);
- connector_->start();
+void CoreClient::bindSessionToStream() {
+ session_ = ClientSession::create(jid_, sessionStream_);
+ session_->setCertificateTrustChecker(certificateTrustChecker);
+ session_->setUseStreamCompression(options.useStreamCompression);
+ session_->setAllowPLAINOverNonTLS(options.allowPLAINWithoutTLS);
+ switch(options.useTLS) {
+ case ClientOptions::UseTLSWhenAvailable:
+ session_->setUseTLS(ClientSession::UseTLSWhenAvailable);
+ break;
+ case ClientOptions::NeverUseTLS:
+ session_->setUseTLS(ClientSession::NeverUseTLS);
+ break;
+ case ClientOptions::RequireTLS:
+ session_->setUseTLS(ClientSession::RequireTLS);
+ break;
+ }
+ session_->setUseAcks(options.useAcks);
+ stanzaChannel_->setSession(session_);
+ session_->onFinished.connect(boost::bind(&CoreClient::handleSessionFinished, this, _1));
+ session_->onNeedCredentials.connect(boost::bind(&CoreClient::handleNeedCredentials, this));
+ session_->start();
}
+/**
+ * Only called for TCP sessions. BOSH is handled inside the BOSHSessionStream.
+ */
void CoreClient::handleConnectorFinished(boost::shared_ptr<Connection> connection) {
resetConnector();
if (!connection) {
@@ -99,26 +137,7 @@ void CoreClient::handleConnectorFinished(boost::shared_ptr<Connection> connectio
sessionStream_->onDataRead.connect(boost::bind(&CoreClient::handleDataRead, this, _1));
sessionStream_->onDataWritten.connect(boost::bind(&CoreClient::handleDataWritten, this, _1));
- session_ = ClientSession::create(jid_, sessionStream_);
- session_->setCertificateTrustChecker(certificateTrustChecker);
- session_->setUseStreamCompression(options.useStreamCompression);
- session_->setAllowPLAINOverNonTLS(options.allowPLAINWithoutTLS);
- switch(options.useTLS) {
- case ClientOptions::UseTLSWhenAvailable:
- session_->setUseTLS(ClientSession::UseTLSWhenAvailable);
- break;
- case ClientOptions::NeverUseTLS:
- session_->setUseTLS(ClientSession::NeverUseTLS);
- break;
- case ClientOptions::RequireTLS:
- session_->setUseTLS(ClientSession::RequireTLS);
- break;
- }
- session_->setUseAcks(options.useAcks);
- stanzaChannel_->setSession(session_);
- session_->onFinished.connect(boost::bind(&CoreClient::handleSessionFinished, this, _1));
- session_->onNeedCredentials.connect(boost::bind(&CoreClient::handleNeedCredentials, this));
- session_->start();
+ bindSessionToStream();
}
}
@@ -339,9 +358,14 @@ void CoreClient::resetSession() {
sessionStream_->onDataRead.disconnect(boost::bind(&CoreClient::handleDataRead, this, _1));
sessionStream_->onDataWritten.disconnect(boost::bind(&CoreClient::handleDataWritten, this, _1));
- sessionStream_.reset();
- connection_->disconnect();
+ if (connection_) {
+ connection_->disconnect();
+ }
+ else if (boost::dynamic_pointer_cast<BOSHSessionStream>(sessionStream_)) {
+ sessionStream_->close();
+ }
+ sessionStream_.reset();
connection_.reset();
}
diff --git a/Swiften/Client/CoreClient.h b/Swiften/Client/CoreClient.h
index 3c089c1..c231fdc 100644
--- a/Swiften/Client/CoreClient.h
+++ b/Swiften/Client/CoreClient.h
@@ -29,7 +29,7 @@ namespace Swift {
class ClientSession;
class StanzaChannel;
class Stanza;
- class BasicSessionStream;
+ class SessionStream;
class CertificateTrustChecker;
class NetworkFactories;
class ClientSessionStanzaChannel;
@@ -207,6 +207,7 @@ namespace Swift {
void handleMessageReceived(boost::shared_ptr<Message>);
void handleStanzaAcked(boost::shared_ptr<Stanza>);
void purgePassword();
+ void bindSessionToStream();
void resetConnector();
void resetSession();
@@ -222,7 +223,7 @@ namespace Swift {
boost::shared_ptr<ChainedConnector> connector_;
std::vector<ConnectionFactory*> proxyConnectionFactories;
boost::shared_ptr<Connection> connection_;
- boost::shared_ptr<BasicSessionStream> sessionStream_;
+ boost::shared_ptr<SessionStream> sessionStream_;
boost::shared_ptr<ClientSession> session_;
std::string certificate_;
bool disconnectRequested_;
diff --git a/Swiften/Client/UnitTest/ClientSessionTest.cpp b/Swiften/Client/UnitTest/ClientSessionTest.cpp
index e9d1b21..22db8fc 100644
--- a/Swiften/Client/UnitTest/ClientSessionTest.cpp
+++ b/Swiften/Client/UnitTest/ClientSessionTest.cpp
@@ -403,6 +403,10 @@ class ClientSessionTest : public CppUnit::TestFixture {
return boost::shared_ptr<CertificateVerificationError>();
}
+ virtual bool supportsZLibCompression() {
+ return true;
+ }
+
virtual void addZLibCompression() {
compressed = true;
}