diff options
Diffstat (limited to 'Swiften/Network/BOSHConnection.cpp')
-rw-r--r-- | Swiften/Network/BOSHConnection.cpp | 538 |
1 files changed, 269 insertions, 269 deletions
diff --git a/Swiften/Network/BOSHConnection.cpp b/Swiften/Network/BOSHConnection.cpp index b04609f..1c468f1 100644 --- a/Swiften/Network/BOSHConnection.cpp +++ b/Swiften/Network/BOSHConnection.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2011-2015 Isode Limited. + * Copyright (c) 2011-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -13,10 +13,10 @@ #include <Swiften/Network/BOSHConnection.h> #include <string> +#include <thread> #include <boost/bind.hpp> #include <boost/lexical_cast.hpp> -#include <boost/thread.hpp> #include <Swiften/Base/ByteArray.h> #include <Swiften/Base/Concat.h> @@ -32,362 +32,362 @@ namespace Swift { BOSHConnection::BOSHConnection(const URL& boshURL, Connector::ref connector, XMLParserFactory* parserFactory, TLSContextFactory* tlsContextFactory, const TLSOptions& tlsOptions) - : boshURL_(boshURL), - connector_(connector), - parserFactory_(parserFactory), - sid_(), - waitingForStartResponse_(false), - rid_(~0ULL), - pending_(false), - connectionReady_(false) + : boshURL_(boshURL), + connector_(connector), + parserFactory_(parserFactory), + sid_(), + waitingForStartResponse_(false), + rid_(~0ULL), + pending_(false), + connectionReady_(false) { - if (boshURL_.getScheme() == "https") { - tlsLayer_ = boost::make_shared<TLSLayer>(tlsContextFactory, tlsOptions); - // The following dummyLayer_ is needed as the TLSLayer will pass the decrypted data to its parent layer. - // The dummyLayer_ will serve as the parent layer. - dummyLayer_ = boost::make_shared<DummyStreamLayer>(tlsLayer_.get()); - } + if (boshURL_.getScheme() == "https") { + tlsLayer_ = std::make_shared<TLSLayer>(tlsContextFactory, tlsOptions); + // The following dummyLayer_ is needed as the TLSLayer will pass the decrypted data to its parent layer. + // The dummyLayer_ will serve as the parent layer. + dummyLayer_ = std::make_shared<DummyStreamLayer>(tlsLayer_.get()); + } } BOSHConnection::~BOSHConnection() { - cancelConnector(); - if (connection_) { - connection_->onDataRead.disconnect(boost::bind(&BOSHConnection::handleDataRead, shared_from_this(), _1)); - connection_->onDisconnected.disconnect(boost::bind(&BOSHConnection::handleDisconnected, shared_from_this(), _1)); - } - BOSHConnection::disconnect(); + cancelConnector(); + if (connection_) { + connection_->onDataRead.disconnect(boost::bind(&BOSHConnection::handleDataRead, shared_from_this(), _1)); + connection_->onDisconnected.disconnect(boost::bind(&BOSHConnection::handleDisconnected, shared_from_this(), _1)); + } + BOSHConnection::disconnect(); } void BOSHConnection::connect() { - connector_->onConnectFinished.connect(boost::bind(&BOSHConnection::handleConnectFinished, shared_from_this(), _1)); - connector_->start(); + 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(); - } + if (connector_) { + connector_->onConnectFinished.disconnect(boost::bind(&BOSHConnection::handleConnectFinished, shared_from_this(), _1)); + connector_->stop(); + connector_.reset(); + } } void BOSHConnection::handleTLSConnected() { - SWIFT_LOG(debug) << std::endl; - onConnectFinished(false); + SWIFT_LOG(debug) << std::endl; + onConnectFinished(false); } void BOSHConnection::handleTLSApplicationDataRead(const SafeByteArray& data) { - SWIFT_LOG(debug) << std::endl; - handleDataRead(boost::make_shared<SafeByteArray>(data)); + SWIFT_LOG(debug) << std::endl; + handleDataRead(std::make_shared<SafeByteArray>(data)); } void BOSHConnection::handleTLSNetowrkDataWriteRequest(const SafeByteArray& data) { - SWIFT_LOG(debug) << std::endl; - connection_->write(data); + SWIFT_LOG(debug) << std::endl; + connection_->write(data); } -void BOSHConnection::handleRawDataRead(boost::shared_ptr<SafeByteArray> data) { - SWIFT_LOG(debug) << std::endl; - tlsLayer_->handleDataRead(*data.get()); +void BOSHConnection::handleRawDataRead(std::shared_ptr<SafeByteArray> data) { + SWIFT_LOG(debug) << std::endl; + tlsLayer_->handleDataRead(*data.get()); } -void BOSHConnection::handleTLSError(boost::shared_ptr<TLSError> /* error */) { +void BOSHConnection::handleTLSError(std::shared_ptr<TLSError> /* error */) { } void BOSHConnection::writeData(const SafeByteArray& data) { - if (tlsLayer_) { - tlsLayer_->writeData(data); - } - else { - connection_->write(data); - } + if (tlsLayer_) { + tlsLayer_->writeData(data); + } + else { + connection_->write(data); + } } void BOSHConnection::disconnect() { - if (connection_) { - connection_->disconnect(); - sid_ = ""; - } - else { - /* handleDisconnected takes care of the connector_ as well */ - handleDisconnected(boost::optional<Connection::Error>()); - } + if (connection_) { + connection_->disconnect(); + sid_ = ""; + } + else { + /* handleDisconnected takes care of the connector_ as well */ + handleDisconnected(boost::optional<Connection::Error>()); + } } void BOSHConnection::restartStream() { - write(createSafeByteArray(""), true, false); + write(createSafeByteArray(""), true, false); } bool BOSHConnection::setClientCertificate(CertificateWithKey::ref cert) { - if (tlsLayer_) { - SWIFT_LOG(debug) << "set client certificate" << std::endl; - return tlsLayer_->setClientCertificate(cert); - } - else { - return false; - } + if (tlsLayer_) { + SWIFT_LOG(debug) << "set client certificate" << std::endl; + return tlsLayer_->setClientCertificate(cert); + } + else { + return false; + } } Certificate::ref BOSHConnection::getPeerCertificate() const { - Certificate::ref peerCertificate; - if (tlsLayer_) { - peerCertificate = tlsLayer_->getPeerCertificate(); - } - return peerCertificate; + Certificate::ref peerCertificate; + if (tlsLayer_) { + peerCertificate = tlsLayer_->getPeerCertificate(); + } + return peerCertificate; } std::vector<Certificate::ref> BOSHConnection::getPeerCertificateChain() const { - std::vector<Certificate::ref> peerCertificateChain; - if (tlsLayer_) { - peerCertificateChain = tlsLayer_->getPeerCertificateChain(); - } - return peerCertificateChain; + std::vector<Certificate::ref> peerCertificateChain; + if (tlsLayer_) { + peerCertificateChain = tlsLayer_->getPeerCertificateChain(); + } + return peerCertificateChain; } CertificateVerificationError::ref BOSHConnection::getPeerCertificateVerificationError() const { - CertificateVerificationError::ref verificationError; - if (tlsLayer_) { - verificationError = tlsLayer_->getPeerCertificateVerificationError(); - } - return verificationError; + CertificateVerificationError::ref verificationError; + if (tlsLayer_) { + verificationError = tlsLayer_->getPeerCertificateVerificationError(); + } + return verificationError; } void BOSHConnection::terminateStream() { - write(createSafeByteArray(""), false, true); + write(createSafeByteArray(""), false, true); } void BOSHConnection::write(const SafeByteArray& data) { - write(data, false, false); + write(data, false, false); } 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(); - if (boshURL.getPort()) { - header << ":" << *boshURL.getPort(); - } - header << "\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); + 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(); + if (boshURL.getPort()) { + header << ":" << *boshURL.getPort(); + } + header << "\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()); + assert(connectionReady_); + assert(!sid_.empty()); - SafeByteArray safeHeader = createHTTPRequest(data, streamRestart, terminate, rid_, sid_, boshURL_).first; + SafeByteArray safeHeader = createHTTPRequest(data, streamRestart, terminate, rid_, sid_, boshURL_).first; - onBOSHDataWritten(safeHeader); - writeData(safeHeader); - pending_ = true; + onBOSHDataWritten(safeHeader); + writeData(safeHeader); + pending_ = true; - SWIFT_LOG(debug) << "write data: " << safeByteArrayToString(safeHeader) << std::endl; + SWIFT_LOG(debug) << "write data: " << safeByteArrayToString(safeHeader) << std::endl; } void BOSHConnection::handleConnectFinished(Connection::ref connection) { - cancelConnector(); - connectionReady_ = !!connection; - if (connectionReady_) { - connection_ = connection; - if (tlsLayer_) { - connection_->onDataRead.connect(boost::bind(&BOSHConnection::handleRawDataRead, shared_from_this(), _1)); - connection_->onDisconnected.connect(boost::bind(&BOSHConnection::handleDisconnected, shared_from_this(), _1)); - - tlsLayer_->getContext()->onDataForNetwork.connect(boost::bind(&BOSHConnection::handleTLSNetowrkDataWriteRequest, shared_from_this(), _1)); - tlsLayer_->getContext()->onDataForApplication.connect(boost::bind(&BOSHConnection::handleTLSApplicationDataRead, shared_from_this(), _1)); - tlsLayer_->onConnected.connect(boost::bind(&BOSHConnection::handleTLSConnected, shared_from_this())); - tlsLayer_->onError.connect(boost::bind(&BOSHConnection::handleTLSError, shared_from_this(), _1)); - tlsLayer_->connect(); - } - else { - connection_->onDataRead.connect(boost::bind(&BOSHConnection::handleDataRead, shared_from_this(), _1)); - connection_->onDisconnected.connect(boost::bind(&BOSHConnection::handleDisconnected, shared_from_this(), _1)); - } - } - - if (!connectionReady_ || !tlsLayer_) { - onConnectFinished(!connectionReady_); - } + cancelConnector(); + connectionReady_ = !!connection; + if (connectionReady_) { + connection_ = connection; + if (tlsLayer_) { + connection_->onDataRead.connect(boost::bind(&BOSHConnection::handleRawDataRead, shared_from_this(), _1)); + connection_->onDisconnected.connect(boost::bind(&BOSHConnection::handleDisconnected, shared_from_this(), _1)); + + tlsLayer_->getContext()->onDataForNetwork.connect(boost::bind(&BOSHConnection::handleTLSNetowrkDataWriteRequest, shared_from_this(), _1)); + tlsLayer_->getContext()->onDataForApplication.connect(boost::bind(&BOSHConnection::handleTLSApplicationDataRead, shared_from_this(), _1)); + tlsLayer_->onConnected.connect(boost::bind(&BOSHConnection::handleTLSConnected, shared_from_this())); + tlsLayer_->onError.connect(boost::bind(&BOSHConnection::handleTLSError, shared_from_this(), _1)); + tlsLayer_->connect(); + } + else { + connection_->onDataRead.connect(boost::bind(&BOSHConnection::handleDataRead, shared_from_this(), _1)); + connection_->onDisconnected.connect(boost::bind(&BOSHConnection::handleDisconnected, shared_from_this(), _1)); + } + } + + if (!connectionReady_ || !tlsLayer_) { + onConnectFinished(!connectionReady_); + } } 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(); - if (boshURL_.getPort()) { - header << ":" << *boshURL_.getPort(); - } - header << "\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); - writeData(safeHeader); - SWIFT_LOG(debug) << "write stream header: " << safeByteArrayToString(safeHeader) << std::endl; + 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(); + if (boshURL_.getPort()) { + header << ":" << *boshURL_.getPort(); + } + header << "\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); + writeData(safeHeader); + SWIFT_LOG(debug) << "write stream header: " << safeByteArrayToString(safeHeader) << std::endl; } -void BOSHConnection::handleDataRead(boost::shared_ptr<SafeByteArray> data) { - onBOSHDataRead(*data); - buffer_ = concat(buffer_, *data); - std::string response = safeByteArrayToString(buffer_); - if (response.find("\r\n\r\n") == std::string::npos) { - onBOSHDataRead(createSafeByteArray("[[Previous read incomplete, pending]]")); - return; - } - - std::string httpCode = response.substr(response.find(" ") + 1, 3); - if (httpCode != "200") { - onHTTPError(httpCode); - return; - } - - BOSHBodyExtractor parser(parserFactory_, createByteArray(response.substr(response.find("\r\n\r\n") + 4))); - if (parser.getBody()) { - if (parser.getBody()->attributes.getAttribute("type") == "terminate") { - BOSHError::Type errorType = parseTerminationCondition(parser.getBody()->attributes.getAttribute("condition")); - onSessionTerminated(errorType == BOSHError::NoError ? boost::shared_ptr<BOSHError>() : boost::make_shared<BOSHError>(errorType)); - } - buffer_.clear(); - if (waitingForStartResponse_) { - waitingForStartResponse_ = false; - sid_ = parser.getBody()->attributes.getAttribute("sid"); - std::string requestsString = parser.getBody()->attributes.getAttribute("requests"); - size_t requests = 2; - if (!requestsString.empty()) { - try { - requests = boost::lexical_cast<size_t>(requestsString); - } - catch (const boost::bad_lexical_cast&) { - } - } - onSessionStarted(sid_, requests); - } - SafeByteArray payload = createSafeByteArray(parser.getBody()->content); - /* Say we're good to go again, so don't add anything after here in the method */ - pending_ = false; - onXMPPDataRead(payload); - } +void BOSHConnection::handleDataRead(std::shared_ptr<SafeByteArray> data) { + onBOSHDataRead(*data); + buffer_ = concat(buffer_, *data); + std::string response = safeByteArrayToString(buffer_); + if (response.find("\r\n\r\n") == std::string::npos) { + onBOSHDataRead(createSafeByteArray("[[Previous read incomplete, pending]]")); + return; + } + + std::string httpCode = response.substr(response.find(" ") + 1, 3); + if (httpCode != "200") { + onHTTPError(httpCode); + return; + } + + BOSHBodyExtractor parser(parserFactory_, createByteArray(response.substr(response.find("\r\n\r\n") + 4))); + if (parser.getBody()) { + if (parser.getBody()->attributes.getAttribute("type") == "terminate") { + BOSHError::Type errorType = parseTerminationCondition(parser.getBody()->attributes.getAttribute("condition")); + onSessionTerminated(errorType == BOSHError::NoError ? std::shared_ptr<BOSHError>() : std::make_shared<BOSHError>(errorType)); + } + buffer_.clear(); + if (waitingForStartResponse_) { + waitingForStartResponse_ = false; + sid_ = parser.getBody()->attributes.getAttribute("sid"); + std::string requestsString = parser.getBody()->attributes.getAttribute("requests"); + size_t requests = 2; + if (!requestsString.empty()) { + try { + requests = boost::lexical_cast<size_t>(requestsString); + } + catch (const boost::bad_lexical_cast&) { + } + } + onSessionStarted(sid_, requests); + } + SafeByteArray payload = createSafeByteArray(parser.getBody()->content); + /* Say we're good to go again, so don't add anything after here in the method */ + pending_ = false; + onXMPPDataRead(payload); + } } BOSHError::Type BOSHConnection::parseTerminationCondition(const std::string& text) { - BOSHError::Type condition = BOSHError::UndefinedCondition; - if (text == "bad-request") { - condition = BOSHError::BadRequest; - } - else if (text == "host-gone") { - condition = BOSHError::HostGone; - } - else if (text == "host-unknown") { - condition = BOSHError::HostUnknown; - } - else if (text == "improper-addressing") { - condition = BOSHError::ImproperAddressing; - } - else if (text == "internal-server-error") { - condition = BOSHError::InternalServerError; - } - else if (text == "item-not-found") { - condition = BOSHError::ItemNotFound; - } - else if (text == "other-request") { - condition = BOSHError::OtherRequest; - } - 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; + BOSHError::Type condition = BOSHError::UndefinedCondition; + if (text == "bad-request") { + condition = BOSHError::BadRequest; + } + else if (text == "host-gone") { + condition = BOSHError::HostGone; + } + else if (text == "host-unknown") { + condition = BOSHError::HostUnknown; + } + else if (text == "improper-addressing") { + condition = BOSHError::ImproperAddressing; + } + else if (text == "internal-server-error") { + condition = BOSHError::InternalServerError; + } + else if (text == "item-not-found") { + condition = BOSHError::ItemNotFound; + } + else if (text == "other-request") { + condition = BOSHError::OtherRequest; + } + 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_; + return sid_; } void BOSHConnection::setRID(unsigned long long rid) { - rid_ = rid; + rid_ = rid; } void BOSHConnection::setSID(const std::string& sid) { - sid_ = sid; + sid_ = sid; } void BOSHConnection::handleDisconnected(const boost::optional<Connection::Error>& error) { - cancelConnector(); - onDisconnected(error ? true : false); - sid_ = ""; - connectionReady_ = false; + cancelConnector(); + onDisconnected(error ? true : false); + 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(); + /* 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(); } } |