summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften/StreamStack')
-rw-r--r--Swiften/StreamStack/CompressionLayer.h48
-rw-r--r--Swiften/StreamStack/ConnectionLayer.h23
-rw-r--r--Swiften/StreamStack/HighLayer.cpp8
-rw-r--r--Swiften/StreamStack/HighLayer.h19
-rw-r--r--Swiften/StreamStack/LowLayer.cpp8
-rw-r--r--Swiften/StreamStack/LowLayer.h19
-rw-r--r--Swiften/StreamStack/OpenSSLLayer.cpp28
-rw-r--r--Swiften/StreamStack/OpenSSLLayer.h27
-rw-r--r--Swiften/StreamStack/PlatformTLSLayerFactory.cpp31
-rw-r--r--Swiften/StreamStack/PlatformTLSLayerFactory.h13
-rw-r--r--Swiften/StreamStack/SConscript21
-rw-r--r--Swiften/StreamStack/StreamLayer.h16
-rw-r--r--Swiften/StreamStack/StreamStack.cpp40
-rw-r--r--Swiften/StreamStack/StreamStack.h44
-rw-r--r--Swiften/StreamStack/TLSLayer.h19
-rw-r--r--Swiften/StreamStack/TLSLayerFactory.cpp8
-rw-r--r--Swiften/StreamStack/TLSLayerFactory.h15
-rw-r--r--Swiften/StreamStack/UnitTest/StreamStackTest.cpp165
-rw-r--r--Swiften/StreamStack/UnitTest/XMPPLayerTest.cpp119
-rw-r--r--Swiften/StreamStack/WhitespacePingLayer.cpp39
-rw-r--r--Swiften/StreamStack/WhitespacePingLayer.h32
-rw-r--r--Swiften/StreamStack/XMPPLayer.cpp80
-rw-r--r--Swiften/StreamStack/XMPPLayer.h55
23 files changed, 877 insertions, 0 deletions
diff --git a/Swiften/StreamStack/CompressionLayer.h b/Swiften/StreamStack/CompressionLayer.h
new file mode 100644
index 0000000..6a8f2b3
--- /dev/null
+++ b/Swiften/StreamStack/CompressionLayer.h
@@ -0,0 +1,48 @@
+#ifndef SWIFTEN_COMPRESSIONLAYER_H
+#define SWIFTEN_COMPRESSIONLAYER_H
+
+#include <boost/noncopyable.hpp>
+#include <boost/signal.hpp>
+
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/StreamStack/StreamLayer.h"
+#include "Swiften/Compress/ZLibException.h"
+#include "Swiften/Compress/ZLibCompressor.h"
+#include "Swiften/Compress/ZLibDecompressor.h"
+
+namespace Swift {
+ class ZLibCompressor;
+ class ZLibDecompressor;
+
+ class CompressionLayer : public StreamLayer, boost::noncopyable {
+ public:
+ CompressionLayer() {}
+
+ virtual void writeData(const ByteArray& data) {
+ try {
+ onWriteData(compressor_.process(data));
+ }
+ catch (const ZLibException& e) {
+ onError();
+ }
+ }
+
+ virtual void handleDataRead(const ByteArray& data) {
+ try {
+ onDataRead(decompressor_.process(data));
+ }
+ catch (const ZLibException& e) {
+ onError();
+ }
+ }
+
+ public:
+ boost::signal<void ()> onError;
+
+ private:
+ ZLibCompressor compressor_;
+ ZLibDecompressor decompressor_;
+ };
+}
+
+#endif
diff --git a/Swiften/StreamStack/ConnectionLayer.h b/Swiften/StreamStack/ConnectionLayer.h
new file mode 100644
index 0000000..7688f78
--- /dev/null
+++ b/Swiften/StreamStack/ConnectionLayer.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/StreamStack/LowLayer.h"
+#include "Swiften/Network/Connection.h"
+
+namespace Swift {
+ class ConnectionLayer : public LowLayer {
+ public:
+ ConnectionLayer(boost::shared_ptr<Connection> connection) : connection(connection) {
+ connection->onDataRead.connect(onDataRead);
+ }
+
+ void writeData(const ByteArray& data) {
+ connection->write(data);
+ }
+
+ private:
+ boost::shared_ptr<Connection> connection;
+ };
+}
diff --git a/Swiften/StreamStack/HighLayer.cpp b/Swiften/StreamStack/HighLayer.cpp
new file mode 100644
index 0000000..2f5e1df
--- /dev/null
+++ b/Swiften/StreamStack/HighLayer.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/StreamStack/HighLayer.h"
+
+namespace Swift {
+
+HighLayer::~HighLayer() {
+}
+
+}
diff --git a/Swiften/StreamStack/HighLayer.h b/Swiften/StreamStack/HighLayer.h
new file mode 100644
index 0000000..bd6c6e6
--- /dev/null
+++ b/Swiften/StreamStack/HighLayer.h
@@ -0,0 +1,19 @@
+#ifndef SWIFTEN_HIGHLAYER_H
+#define SWIFTEN_HIGHLAYER_H
+
+#include <boost/signal.hpp>
+
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+ class HighLayer {
+ public:
+ virtual ~HighLayer();
+
+ virtual void handleDataRead(const ByteArray& data) = 0;
+
+ boost::signal<void (const ByteArray&)> onWriteData;
+ };
+}
+
+#endif
diff --git a/Swiften/StreamStack/LowLayer.cpp b/Swiften/StreamStack/LowLayer.cpp
new file mode 100644
index 0000000..24aa7a2
--- /dev/null
+++ b/Swiften/StreamStack/LowLayer.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/StreamStack/LowLayer.h"
+
+namespace Swift {
+
+LowLayer::~LowLayer() {
+}
+
+}
diff --git a/Swiften/StreamStack/LowLayer.h b/Swiften/StreamStack/LowLayer.h
new file mode 100644
index 0000000..763cfc4
--- /dev/null
+++ b/Swiften/StreamStack/LowLayer.h
@@ -0,0 +1,19 @@
+#ifndef SWIFTEN_LOWLAYER_H
+#define SWIFTEN_LOWLAYER_H
+
+#include <boost/signal.hpp>
+
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+ class LowLayer {
+ public:
+ virtual ~LowLayer();
+
+ virtual void writeData(const ByteArray& data) = 0;
+
+ boost::signal<void (const ByteArray&)> onDataRead;
+ };
+}
+
+#endif
diff --git a/Swiften/StreamStack/OpenSSLLayer.cpp b/Swiften/StreamStack/OpenSSLLayer.cpp
new file mode 100644
index 0000000..da93e4e
--- /dev/null
+++ b/Swiften/StreamStack/OpenSSLLayer.cpp
@@ -0,0 +1,28 @@
+#include "Swiften/StreamStack/OpenSSLLayer.h"
+
+namespace Swift {
+
+OpenSSLLayer::OpenSSLLayer() {
+ context_.onDataForNetwork.connect(onWriteData);
+ context_.onDataForApplication.connect(onDataRead);
+ context_.onConnected.connect(onConnected);
+ context_.onError.connect(onError);
+}
+
+void OpenSSLLayer::connect() {
+ context_.connect();
+}
+
+void OpenSSLLayer::writeData(const ByteArray& data) {
+ context_.handleDataFromApplication(data);
+}
+
+void OpenSSLLayer::handleDataRead(const ByteArray& data) {
+ context_.handleDataFromNetwork(data);
+}
+
+bool OpenSSLLayer::setClientCertificate(const PKCS12Certificate& certificate) {
+ return context_.setClientCertificate(certificate);
+}
+
+}
diff --git a/Swiften/StreamStack/OpenSSLLayer.h b/Swiften/StreamStack/OpenSSLLayer.h
new file mode 100644
index 0000000..615c75e
--- /dev/null
+++ b/Swiften/StreamStack/OpenSSLLayer.h
@@ -0,0 +1,27 @@
+#ifndef SWIFTEN_OpenSSLLayer_H
+#define SWIFTEN_OpenSSLLayer_H
+
+#include <boost/noncopyable.hpp>
+#include <boost/signal.hpp>
+
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/StreamStack/TLSLayer.h"
+#include "Swiften/TLS/OpenSSL/OpenSSLContext.h"
+
+namespace Swift {
+ class OpenSSLLayer : public TLSLayer, boost::noncopyable {
+ public:
+ OpenSSLLayer();
+
+ virtual void connect();
+ virtual bool setClientCertificate(const PKCS12Certificate&);
+
+ virtual void writeData(const ByteArray& data);
+ virtual void handleDataRead(const ByteArray& data);
+
+ private:
+ OpenSSLContext context_;
+ };
+}
+
+#endif
diff --git a/Swiften/StreamStack/PlatformTLSLayerFactory.cpp b/Swiften/StreamStack/PlatformTLSLayerFactory.cpp
new file mode 100644
index 0000000..cdcb8a7
--- /dev/null
+++ b/Swiften/StreamStack/PlatformTLSLayerFactory.cpp
@@ -0,0 +1,31 @@
+#include "Swiften/StreamStack/PlatformTLSLayerFactory.h"
+
+#include <cassert>
+
+#ifdef HAVE_OPENSSL
+#include "Swiften/StreamStack/OpenSSLLayer.h"
+#endif
+
+namespace Swift {
+
+PlatformTLSLayerFactory::PlatformTLSLayerFactory() {
+}
+
+bool PlatformTLSLayerFactory::canCreate() const {
+#ifdef HAVE_OPENSSL
+ return true;
+#else
+ return false;
+#endif
+}
+
+boost::shared_ptr<TLSLayer> PlatformTLSLayerFactory::createTLSLayer() {
+#ifdef HAVE_OPENSSL
+ return boost::shared_ptr<TLSLayer>(new OpenSSLLayer());
+#else
+ assert(false);
+ return boost::shared_ptr<TLSLayer>();
+#endif
+}
+
+}
diff --git a/Swiften/StreamStack/PlatformTLSLayerFactory.h b/Swiften/StreamStack/PlatformTLSLayerFactory.h
new file mode 100644
index 0000000..6c343b0
--- /dev/null
+++ b/Swiften/StreamStack/PlatformTLSLayerFactory.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "Swiften/StreamStack/TLSLayerFactory.h"
+
+namespace Swift {
+ class PlatformTLSLayerFactory : public TLSLayerFactory {
+ public:
+ PlatformTLSLayerFactory();
+
+ bool canCreate() const;
+ virtual boost::shared_ptr<TLSLayer> createTLSLayer();
+ };
+}
diff --git a/Swiften/StreamStack/SConscript b/Swiften/StreamStack/SConscript
new file mode 100644
index 0000000..449a39b
--- /dev/null
+++ b/Swiften/StreamStack/SConscript
@@ -0,0 +1,21 @@
+Import("swiften_env")
+
+myenv = swiften_env.Clone()
+myenv.MergeFlags(swiften_env["OPENSSL_FLAGS"])
+
+sources = [
+ "HighLayer.cpp",
+ "LowLayer.cpp",
+ "PlatformTLSLayerFactory.cpp",
+ "StreamStack.cpp",
+ "TLSLayerFactory.cpp",
+ "WhitespacePingLayer.cpp",
+ "XMPPLayer.cpp",
+ ]
+
+if myenv.get("HAVE_OPENSSL", 0) :
+ myenv.Append(CPPDEFINES = "HAVE_OPENSSL")
+ sources += ["OpenSSLLayer.cpp"]
+
+objects = myenv.StaticObject(sources)
+swiften_env.Append(SWIFTEN_OBJECTS = [objects])
diff --git a/Swiften/StreamStack/StreamLayer.h b/Swiften/StreamStack/StreamLayer.h
new file mode 100644
index 0000000..6ea2051
--- /dev/null
+++ b/Swiften/StreamStack/StreamLayer.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_STREAMLAYER_H
+#define SWIFTEN_STREAMLAYER_H
+
+#include <boost/signal.hpp>
+
+#include "Swiften/StreamStack/LowLayer.h"
+#include "Swiften/StreamStack/HighLayer.h"
+
+namespace Swift {
+ class StreamLayer : public LowLayer, public HighLayer {
+ public:
+ StreamLayer() {}
+ };
+}
+
+#endif
diff --git a/Swiften/StreamStack/StreamStack.cpp b/Swiften/StreamStack/StreamStack.cpp
new file mode 100644
index 0000000..9eb9b4e
--- /dev/null
+++ b/Swiften/StreamStack/StreamStack.cpp
@@ -0,0 +1,40 @@
+#include "Swiften/StreamStack/StreamStack.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Base/foreach.h"
+#include "Swiften/StreamStack/XMPPLayer.h"
+#include "Swiften/StreamStack/LowLayer.h"
+#include "Swiften/StreamStack/StreamLayer.h"
+
+namespace Swift {
+
+StreamStack::StreamStack(boost::shared_ptr<XMPPLayer> xmppLayer, boost::shared_ptr<LowLayer> physicalLayer) : xmppLayer_(xmppLayer), physicalLayer_(physicalLayer) {
+ xmppReadSlotConnection_ = physicalLayer_->onDataRead.connect(boost::bind(&XMPPLayer::parseData, xmppLayer_, _1));
+ xmppWriteSignalConnection_ = xmppLayer_->onWriteData.connect(boost::bind(&LowLayer::writeData, physicalLayer_, _1));
+}
+
+StreamStack::~StreamStack() {
+ // Disconnect the write signal connections to break cyclic signal
+ // dependencies. The read signal connections have
+ // to remain, since these can be reached from the main event loop.
+ xmppWriteSignalConnection_.disconnect();
+ foreach(const boost::bsignals::connection& connection, writeSignalConnections_) {
+ connection.disconnect();
+ }
+}
+
+void StreamStack::addLayer(boost::shared_ptr<StreamLayer> newLayer) {
+ xmppReadSlotConnection_.disconnect();
+ xmppWriteSignalConnection_.disconnect();
+
+ boost::shared_ptr<LowLayer> lowLayer = (layers_.empty() ? physicalLayer_ : *layers_.rbegin());
+
+ lowLayer->onDataRead.connect(boost::bind(&HighLayer::handleDataRead, newLayer, _1), boost::bsignals::at_front);
+ writeSignalConnections_.push_back(newLayer->onWriteData.connect(boost::bind(&LowLayer::writeData, lowLayer, _1), boost::bsignals::at_front));
+ xmppWriteSignalConnection_ = xmppLayer_->onWriteData.connect(boost::bind(&LowLayer::writeData, newLayer, _1), boost::bsignals::at_front);
+ xmppReadSlotConnection_ = newLayer->onDataRead.connect(boost::bind(&XMPPLayer::parseData, xmppLayer_, _1), boost::bsignals::at_front);
+ layers_.push_back(newLayer);
+}
+
+}
diff --git a/Swiften/StreamStack/StreamStack.h b/Swiften/StreamStack/StreamStack.h
new file mode 100644
index 0000000..87c522d
--- /dev/null
+++ b/Swiften/StreamStack/StreamStack.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/signal.hpp>
+#include <vector>
+
+#include "Swiften/Elements/Stanza.h"
+#include "Swiften/Base/foreach.h"
+
+namespace Swift {
+ class XMPPLayer;
+ class LowLayer;
+ class StreamLayer;
+
+ class StreamStack {
+ public:
+ StreamStack(boost::shared_ptr<XMPPLayer> xmppLayer, boost::shared_ptr<LowLayer> physicalLayer);
+ ~StreamStack();
+
+ void addLayer(boost::shared_ptr<StreamLayer>);
+
+ boost::shared_ptr<XMPPLayer> getXMPPLayer() const {
+ return xmppLayer_;
+ }
+
+ template<typename T> boost::shared_ptr<T> getLayer() {
+ foreach(const boost::shared_ptr<StreamLayer>& streamLayer, layers_) {
+ boost::shared_ptr<T> layer = boost::dynamic_pointer_cast<T>(streamLayer);
+ if (layer) {
+ return layer;
+ }
+ }
+ return boost::shared_ptr<T>();
+ }
+
+ private:
+ boost::shared_ptr<XMPPLayer> xmppLayer_;
+ boost::shared_ptr<LowLayer> physicalLayer_;
+ std::vector< boost::shared_ptr<StreamLayer> > layers_;
+ boost::bsignals::connection xmppReadSlotConnection_;
+ boost::bsignals::connection xmppWriteSignalConnection_;
+ std::vector< boost::bsignals::connection > writeSignalConnections_;
+ };
+}
diff --git a/Swiften/StreamStack/TLSLayer.h b/Swiften/StreamStack/TLSLayer.h
new file mode 100644
index 0000000..490e16c
--- /dev/null
+++ b/Swiften/StreamStack/TLSLayer.h
@@ -0,0 +1,19 @@
+#ifndef SWIFTEN_TLSLayer_H
+#define SWIFTEN_TLSLayer_H
+
+#include "Swiften/StreamStack/StreamLayer.h"
+#include "Swiften/TLS/PKCS12Certificate.h"
+
+namespace Swift {
+ class TLSLayer : public StreamLayer {
+ public:
+ virtual void connect() = 0;
+ virtual bool setClientCertificate(const PKCS12Certificate&) = 0;
+
+ public:
+ boost::signal<void ()> onError;
+ boost::signal<void ()> onConnected;
+ };
+}
+
+#endif
diff --git a/Swiften/StreamStack/TLSLayerFactory.cpp b/Swiften/StreamStack/TLSLayerFactory.cpp
new file mode 100644
index 0000000..15de455
--- /dev/null
+++ b/Swiften/StreamStack/TLSLayerFactory.cpp
@@ -0,0 +1,8 @@
+#include "Swiften/StreamStack/TLSLayerFactory.h"
+
+namespace Swift {
+
+TLSLayerFactory::~TLSLayerFactory() {
+}
+
+}
diff --git a/Swiften/StreamStack/TLSLayerFactory.h b/Swiften/StreamStack/TLSLayerFactory.h
new file mode 100644
index 0000000..1b0bfc1
--- /dev/null
+++ b/Swiften/StreamStack/TLSLayerFactory.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+ class TLSLayer;
+
+ class TLSLayerFactory {
+ public:
+ virtual ~TLSLayerFactory();
+ virtual bool canCreate() const = 0;
+
+ virtual boost::shared_ptr<TLSLayer> createTLSLayer() = 0;
+ };
+}
diff --git a/Swiften/StreamStack/UnitTest/StreamStackTest.cpp b/Swiften/StreamStack/UnitTest/StreamStackTest.cpp
new file mode 100644
index 0000000..6b97c0d
--- /dev/null
+++ b/Swiften/StreamStack/UnitTest/StreamStackTest.cpp
@@ -0,0 +1,165 @@
+#include <vector>
+#include <boost/bind.hpp>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/StreamStack/StreamStack.h"
+#include "Swiften/StreamStack/LowLayer.h"
+#include "Swiften/StreamStack/XMPPLayer.h"
+#include "Swiften/StreamStack/StreamLayer.h"
+#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h"
+#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h"
+
+using namespace Swift;
+
+class StreamStackTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(StreamStackTest);
+ CPPUNIT_TEST(testWriteData_NoIntermediateStreamStack);
+ CPPUNIT_TEST(testWriteData_OneIntermediateStream);
+ CPPUNIT_TEST(testWriteData_TwoIntermediateStreamStack);
+ CPPUNIT_TEST(testReadData_NoIntermediateStreamStack);
+ CPPUNIT_TEST(testReadData_OneIntermediateStream);
+ CPPUNIT_TEST(testReadData_TwoIntermediateStreamStack);
+ CPPUNIT_TEST(testAddLayer_ExistingOnWriteDataSlot);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ StreamStackTest() {}
+
+ void setUp() {
+ physicalStream_ = boost::shared_ptr<TestLowLayer>(new TestLowLayer());
+ xmppStream_ = boost::shared_ptr<XMPPLayer>(new XMPPLayer(&parserFactories_, &serializers_));
+ elementsReceived_ = 0;
+ dataWriteReceived_ = 0;
+ }
+
+ void tearDown() {
+ }
+
+ void testWriteData_NoIntermediateStreamStack() {
+ StreamStack testling(xmppStream_, physicalStream_);
+
+ xmppStream_->writeData("foo");
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), physicalStream_->data_.size());
+ CPPUNIT_ASSERT_EQUAL(ByteArray("foo"), physicalStream_->data_[0]);
+ }
+
+ void testWriteData_OneIntermediateStream() {
+ StreamStack testling(xmppStream_, physicalStream_);
+ boost::shared_ptr<MyStreamLayer> xStream(new MyStreamLayer("X"));
+ testling.addLayer(xStream);
+
+ xmppStream_->writeData("foo");
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), physicalStream_->data_.size());
+ CPPUNIT_ASSERT_EQUAL(ByteArray("Xfoo"), physicalStream_->data_[0]);
+ }
+
+ void testWriteData_TwoIntermediateStreamStack() {
+ StreamStack testling(xmppStream_, physicalStream_);
+ boost::shared_ptr<MyStreamLayer> xStream(new MyStreamLayer("X"));
+ boost::shared_ptr<MyStreamLayer> yStream(new MyStreamLayer("Y"));
+ testling.addLayer(xStream);
+ testling.addLayer(yStream);
+
+ xmppStream_->writeData("foo");
+
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), physicalStream_->data_.size());
+ CPPUNIT_ASSERT_EQUAL(ByteArray("XYfoo"), physicalStream_->data_[0]);
+ }
+
+ void testReadData_NoIntermediateStreamStack() {
+ StreamStack testling(xmppStream_, physicalStream_);
+ xmppStream_->onElement.connect(boost::bind(&StreamStackTest::handleElement, this, _1));
+
+ physicalStream_->onDataRead(ByteArray("<stream:stream xmlns:stream='http://etherx.jabber.org/streams'><presence/>"));
+
+ CPPUNIT_ASSERT_EQUAL(1, elementsReceived_);
+ }
+
+ void testReadData_OneIntermediateStream() {
+ StreamStack testling(xmppStream_, physicalStream_);
+ xmppStream_->onElement.connect(boost::bind(&StreamStackTest::handleElement, this, _1));
+ boost::shared_ptr<MyStreamLayer> xStream(new MyStreamLayer("<"));
+ testling.addLayer(xStream);
+
+ physicalStream_->onDataRead(ByteArray("stream:stream xmlns:stream='http://etherx.jabber.org/streams'><presence/>"));
+
+ CPPUNIT_ASSERT_EQUAL(1, elementsReceived_);
+ }
+
+ void testReadData_TwoIntermediateStreamStack() {
+ StreamStack testling(xmppStream_, physicalStream_);
+ xmppStream_->onElement.connect(boost::bind(&StreamStackTest::handleElement, this, _1));
+ boost::shared_ptr<MyStreamLayer> xStream(new MyStreamLayer("s"));
+ boost::shared_ptr<MyStreamLayer> yStream(new MyStreamLayer("<"));
+ testling.addLayer(xStream);
+ testling.addLayer(yStream);
+
+ physicalStream_->onDataRead(ByteArray("tream:stream xmlns:stream='http://etherx.jabber.org/streams'><presence/>"));
+
+ CPPUNIT_ASSERT_EQUAL(1, elementsReceived_);
+ }
+
+ void testAddLayer_ExistingOnWriteDataSlot() {
+ StreamStack testling(xmppStream_, physicalStream_);
+ xmppStream_->onWriteData.connect(boost::bind(&StreamStackTest::handleWriteData, this, _1));
+ boost::shared_ptr<MyStreamLayer> xStream(new MyStreamLayer("X"));
+ testling.addLayer(xStream);
+
+ xmppStream_->writeData("foo");
+
+ CPPUNIT_ASSERT_EQUAL(1, dataWriteReceived_);
+ }
+
+ void handleElement(boost::shared_ptr<Element>) {
+ ++elementsReceived_;
+ }
+
+ void handleWriteData(ByteArray) {
+ ++dataWriteReceived_;
+ }
+
+ private:
+ class MyStreamLayer : public StreamLayer {
+ public:
+ MyStreamLayer(const String& prepend) : prepend_(prepend) {
+ }
+
+ virtual void writeData(const ByteArray& data) {
+ onWriteData(ByteArray(prepend_) + data);
+ }
+
+ virtual void handleDataRead(const ByteArray& data) {
+ onDataRead(ByteArray(prepend_) + data);
+ }
+
+ private:
+ String prepend_;
+ };
+
+ class TestLowLayer : public LowLayer {
+ public:
+ TestLowLayer() {
+ }
+
+ virtual void writeData(const ByteArray& data) {
+ data_.push_back(data);
+ }
+
+ std::vector<ByteArray> data_;
+ };
+
+ private:
+ FullPayloadParserFactoryCollection parserFactories_;
+ FullPayloadSerializerCollection serializers_;
+ boost::shared_ptr<TestLowLayer> physicalStream_;
+ boost::shared_ptr<XMPPLayer> xmppStream_;
+ int elementsReceived_;
+ int dataWriteReceived_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(StreamStackTest);
diff --git a/Swiften/StreamStack/UnitTest/XMPPLayerTest.cpp b/Swiften/StreamStack/UnitTest/XMPPLayerTest.cpp
new file mode 100644
index 0000000..e284ba9
--- /dev/null
+++ b/Swiften/StreamStack/UnitTest/XMPPLayerTest.cpp
@@ -0,0 +1,119 @@
+#include <vector>
+#include <boost/bind.hpp>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include "Swiften/Elements/ProtocolHeader.h"
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/StreamStack/XMPPLayer.h"
+#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h"
+#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h"
+
+using namespace Swift;
+
+class XMPPLayerTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(XMPPLayerTest);
+ CPPUNIT_TEST(testParseData_Error);
+ CPPUNIT_TEST(testResetParser);
+ CPPUNIT_TEST(testResetParser_FromSlot);
+ CPPUNIT_TEST(testWriteHeader);
+ CPPUNIT_TEST(testWriteElement);
+ CPPUNIT_TEST(testWriteFooter);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ XMPPLayerTest() {}
+
+ void setUp() {
+ testling_ = new XMPPLayer(&parserFactories_, &serializers_);
+ elementsReceived_ = 0;
+ dataReceived_ = "";
+ errorReceived_ = 0;
+ }
+
+ void tearDown() {
+ delete testling_;
+ }
+
+ void testParseData_Error() {
+ testling_->onError.connect(boost::bind(&XMPPLayerTest::handleError, this));
+
+ testling_->parseData("<iq>");
+
+ CPPUNIT_ASSERT_EQUAL(1, errorReceived_);
+ }
+
+ void testResetParser() {
+ testling_->onElement.connect(boost::bind(&XMPPLayerTest::handleElement, this, _1));
+ testling_->onError.connect(boost::bind(&XMPPLayerTest::handleError, this));
+
+ testling_->parseData("<stream:stream to=\"example.com\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" >");
+ testling_->resetParser();
+ testling_->parseData("<stream:stream to=\"example.com\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" >");
+ testling_->parseData("<presence/>");
+
+ CPPUNIT_ASSERT_EQUAL(1, elementsReceived_);
+ CPPUNIT_ASSERT_EQUAL(0, errorReceived_);
+ }
+
+ void testResetParser_FromSlot() {
+ testling_->onElement.connect(boost::bind(&XMPPLayerTest::handleElementAndReset, this, _1));
+ testling_->parseData("<stream:stream to=\"example.com\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" ><presence/>");
+ testling_->parseData("<stream:stream to=\"example.com\" xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" ><presence/>");
+
+ CPPUNIT_ASSERT_EQUAL(2, elementsReceived_);
+ CPPUNIT_ASSERT_EQUAL(0, errorReceived_);
+ }
+
+ void testWriteHeader() {
+ testling_->onWriteData.connect(boost::bind(&XMPPLayerTest::handleWriteData, this, _1));
+ ProtocolHeader header;
+ header.setTo("example.com");
+ testling_->writeHeader(header);
+
+ CPPUNIT_ASSERT_EQUAL(String("<?xml version=\"1.0\"?><stream:stream xmlns=\"jabber:client\" xmlns:stream=\"http://etherx.jabber.org/streams\" to=\"example.com\" version=\"1.0\">"), dataReceived_);
+ }
+
+ void testWriteElement() {
+ testling_->onWriteData.connect(boost::bind(&XMPPLayerTest::handleWriteData, this, _1));
+ testling_->writeElement(boost::shared_ptr<Presence>(new Presence()));
+
+ CPPUNIT_ASSERT_EQUAL(String("<presence/>"), dataReceived_);
+ }
+
+ void testWriteFooter() {
+ testling_->onWriteData.connect(boost::bind(&XMPPLayerTest::handleWriteData, this, _1));
+ testling_->writeFooter();
+
+ CPPUNIT_ASSERT_EQUAL(String("</stream:stream>"), dataReceived_);
+ }
+
+ void handleElement(boost::shared_ptr<Element>) {
+ ++elementsReceived_;
+ }
+
+ void handleElementAndReset(boost::shared_ptr<Element>) {
+ ++elementsReceived_;
+ testling_->resetParser();
+ }
+
+ void handleWriteData(ByteArray ba) {
+ dataReceived_ += std::string(ba.getData(), ba.getSize());
+ }
+
+ void handleError() {
+ ++errorReceived_;
+ }
+
+ private:
+ FullPayloadParserFactoryCollection parserFactories_;
+ FullPayloadSerializerCollection serializers_;
+ XMPPLayer* testling_;
+ int elementsReceived_;
+ String dataReceived_;
+ int errorReceived_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(XMPPLayerTest);
diff --git a/Swiften/StreamStack/WhitespacePingLayer.cpp b/Swiften/StreamStack/WhitespacePingLayer.cpp
new file mode 100644
index 0000000..5c1eb00
--- /dev/null
+++ b/Swiften/StreamStack/WhitespacePingLayer.cpp
@@ -0,0 +1,39 @@
+#include "Swiften/StreamStack/WhitespacePingLayer.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Network/TimerFactory.h"
+#include "Swiften/Network/Timer.h"
+
+namespace Swift {
+
+static const int TIMEOUT_MILLISECONDS = 60000;
+
+WhitespacePingLayer::WhitespacePingLayer(TimerFactory* timerFactory) : isActive(false) {
+ timer = timerFactory->createTimer(TIMEOUT_MILLISECONDS);
+ timer->onTick.connect(boost::bind(&WhitespacePingLayer::handleTimerTick, this));
+}
+
+void WhitespacePingLayer::writeData(const ByteArray& data) {
+ onWriteData(data);
+}
+
+void WhitespacePingLayer::handleDataRead(const ByteArray& data) {
+ onDataRead(data);
+}
+
+void WhitespacePingLayer::handleTimerTick() {
+ onWriteData(" ");
+}
+
+void WhitespacePingLayer::setActive() {
+ isActive = true;
+ timer->start();
+}
+
+void WhitespacePingLayer::setInactive() {
+ timer->stop();
+ isActive = false;
+}
+
+}
diff --git a/Swiften/StreamStack/WhitespacePingLayer.h b/Swiften/StreamStack/WhitespacePingLayer.h
new file mode 100644
index 0000000..18ea39a
--- /dev/null
+++ b/Swiften/StreamStack/WhitespacePingLayer.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/StreamStack/StreamLayer.h"
+
+namespace Swift {
+ class Timer;
+ class TimerFactory;
+
+ class WhitespacePingLayer : public StreamLayer, boost::noncopyable {
+ public:
+ WhitespacePingLayer(TimerFactory* timerFactory);
+
+ void setActive();
+ void setInactive();
+ void writeData(const ByteArray& data);
+ void handleDataRead(const ByteArray& data);
+
+ bool getIsActive() const {
+ return isActive;
+ }
+
+ private:
+ void handleTimerTick();
+
+ private:
+ bool isActive;
+ boost::shared_ptr<Timer> timer;
+ };
+}
diff --git a/Swiften/StreamStack/XMPPLayer.cpp b/Swiften/StreamStack/XMPPLayer.cpp
new file mode 100644
index 0000000..b87cb4a
--- /dev/null
+++ b/Swiften/StreamStack/XMPPLayer.cpp
@@ -0,0 +1,80 @@
+#include "Swiften/StreamStack/XMPPLayer.h"
+#include "Swiften/Parser/XMPPParser.h"
+#include "Swiften/Serializer/XMPPSerializer.h"
+#include "Swiften/Elements/ProtocolHeader.h"
+
+namespace Swift {
+
+XMPPLayer::XMPPLayer(
+ PayloadParserFactoryCollection* payloadParserFactories,
+ PayloadSerializerCollection* payloadSerializers) :
+ payloadParserFactories_(payloadParserFactories),
+ payloadSerializers_(payloadSerializers),
+ resetParserAfterParse_(false),
+ inParser_(false) {
+ xmppParser_ = new XMPPParser(this, payloadParserFactories_);
+ xmppSerializer_ = new XMPPSerializer(payloadSerializers_);
+}
+
+XMPPLayer::~XMPPLayer() {
+ delete xmppSerializer_;
+ delete xmppParser_;
+}
+
+void XMPPLayer::writeHeader(const ProtocolHeader& header) {
+ onWriteData(ByteArray(xmppSerializer_->serializeHeader(header)));
+}
+
+void XMPPLayer::writeFooter() {
+ onWriteData(ByteArray(xmppSerializer_->serializeFooter()));
+}
+
+void XMPPLayer::writeElement(boost::shared_ptr<Element> element) {
+ onWriteData(ByteArray(xmppSerializer_->serializeElement(element)));
+}
+
+void XMPPLayer::writeData(const String& data) {
+ onWriteData(ByteArray(data));
+}
+
+void XMPPLayer::parseData(ByteArray data) {
+ onDataRead(data);
+ inParser_ = true;
+ if (!xmppParser_->parse(String(data.getData(), data.getSize()))) {
+ inParser_ = false;
+ onError();
+ return;
+ }
+ inParser_ = false;
+ if (resetParserAfterParse_) {
+ doResetParser();
+ }
+}
+
+void XMPPLayer::doResetParser() {
+ delete xmppParser_;
+ xmppParser_ = new XMPPParser(this, payloadParserFactories_);
+ resetParserAfterParse_ = false;
+}
+
+void XMPPLayer::handleStreamStart(const ProtocolHeader& header) {
+ onStreamStart(header);
+}
+
+void XMPPLayer::handleElement(boost::shared_ptr<Element> stanza) {
+ onElement(stanza);
+}
+
+void XMPPLayer::handleStreamEnd() {
+}
+
+void XMPPLayer::resetParser() {
+ if (inParser_) {
+ resetParserAfterParse_ = true;
+ }
+ else {
+ doResetParser();
+ }
+}
+
+}
diff --git a/Swiften/StreamStack/XMPPLayer.h b/Swiften/StreamStack/XMPPLayer.h
new file mode 100644
index 0000000..7974811
--- /dev/null
+++ b/Swiften/StreamStack/XMPPLayer.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/signal.hpp>
+#include <boost/noncopyable.hpp>
+
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/Elements/Element.h"
+#include "Swiften/Parser/XMPPParserClient.h"
+
+namespace Swift {
+ class ProtocolHeader;
+ class XMPPParser;
+ class PayloadParserFactoryCollection;
+ class XMPPSerializer;
+ class PayloadSerializerCollection;
+
+ class XMPPLayer : public XMPPParserClient, boost::noncopyable {
+ public:
+ XMPPLayer(
+ PayloadParserFactoryCollection* payloadParserFactories,
+ PayloadSerializerCollection* payloadSerializers);
+ ~XMPPLayer();
+
+ void writeHeader(const ProtocolHeader& header);
+ void writeFooter();
+ void writeElement(boost::shared_ptr<Element>);
+ void writeData(const String& data);
+
+ void parseData(ByteArray data);
+ void resetParser();
+
+ public:
+ boost::signal<void (const ProtocolHeader&)> onStreamStart;
+ boost::signal<void (boost::shared_ptr<Element>)> onElement;
+ boost::signal<void (const ByteArray&)> onWriteData;
+ boost::signal<void (const ByteArray&)> onDataRead;
+ boost::signal<void ()> onError;
+
+ private:
+ void handleStreamStart(const ProtocolHeader&);
+ void handleElement(boost::shared_ptr<Element>);
+ void handleStreamEnd();
+
+ void doResetParser();
+
+ private:
+ PayloadParserFactoryCollection* payloadParserFactories_;
+ XMPPParser* xmppParser_;
+ PayloadSerializerCollection* payloadSerializers_;
+ XMPPSerializer* xmppSerializer_;
+ bool resetParserAfterParse_;
+ bool inParser_;
+ };
+}