diff options
author | Kevin Smith <git@kismith.co.uk> | 2011-11-12 16:56:21 (GMT) |
---|---|---|
committer | Kevin Smith <git@kismith.co.uk> | 2011-12-13 08:17:58 (GMT) |
commit | 81c09a0f6a3e87b078340d7f35d0dea4c03f3a6d (patch) | |
tree | 4371c5808ee26b2b5ed79ace9ccb439ff2988945 /Swiften/Client | |
parent | fd17fe0d239f97cedebe4ceffa234155bd299b68 (diff) | |
download | swift-contrib-81c09a0f6a3e87b078340d7f35d0dea4c03f3a6d.zip swift-contrib-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.h | 28 | ||||
-rw-r--r-- | Swiften/Client/ClientSession.cpp | 2 | ||||
-rw-r--r-- | Swiften/Client/ClientXMLTracer.cpp | 17 | ||||
-rw-r--r-- | Swiften/Client/ClientXMLTracer.h | 3 | ||||
-rw-r--r-- | Swiften/Client/CoreClient.cpp | 82 | ||||
-rw-r--r-- | Swiften/Client/CoreClient.h | 5 | ||||
-rw-r--r-- | Swiften/Client/UnitTest/ClientSessionTest.cpp | 4 |
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; } |