summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRemko Tronçon <git@el-tramo.be>2009-06-01 08:48:42 (GMT)
committerRemko Tronçon <git@el-tramo.be>2009-06-01 09:24:28 (GMT)
commit2812bddd81f8a1b804c7460f4e14cd0aa393d129 (patch)
treed46294f35150c4f0f43deaf2d31fceaf945ae715 /Swiften/StreamStack
downloadswift-2812bddd81f8a1b804c7460f4e14cd0aa393d129.zip
swift-2812bddd81f8a1b804c7460f4e14cd0aa393d129.tar.bz2
Import.
Diffstat (limited to 'Swiften/StreamStack')
-rw-r--r--Swiften/StreamStack/CompressionLayer.h48
-rw-r--r--Swiften/StreamStack/ConnectionLayer.h25
-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/Makefile.inc15
-rw-r--r--Swiften/StreamStack/OpenSSLLayer.cpp28
-rw-r--r--Swiften/StreamStack/OpenSSLLayer.h27
-rw-r--r--Swiften/StreamStack/PlatformTLSLayerFactory.cpp34
-rw-r--r--Swiften/StreamStack/PlatformTLSLayerFactory.h16
-rw-r--r--Swiften/StreamStack/StreamLayer.h16
-rw-r--r--Swiften/StreamStack/StreamStack.cpp29
-rw-r--r--Swiften/StreamStack/StreamStack.h45
-rw-r--r--Swiften/StreamStack/TLSLayer.h19
-rw-r--r--Swiften/StreamStack/TLSLayerFactory.cpp8
-rw-r--r--Swiften/StreamStack/TLSLayerFactory.h16
-rw-r--r--Swiften/StreamStack/UnitTest/Makefile.inc3
-rw-r--r--Swiften/StreamStack/UnitTest/StreamStackTest.cpp167
-rw-r--r--Swiften/StreamStack/UnitTest/XMPPLayerTest.cpp116
-rw-r--r--Swiften/StreamStack/WhitespacePingLayer.cpp30
-rw-r--r--Swiften/StreamStack/WhitespacePingLayer.h27
-rw-r--r--Swiften/StreamStack/XMPPLayer.cpp79
-rw-r--r--Swiften/StreamStack/XMPPLayer.h57
24 files changed, 859 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..99873a0
--- /dev/null
+++ b/Swiften/StreamStack/ConnectionLayer.h
@@ -0,0 +1,25 @@
+#ifndef SWIFTEN_CONNECTIONLAYER_H
+#define SWIFTEN_CONNECTIONLAYER_H
+
+#include <boost/signal.hpp>
+
+#include "Swiften/StreamStack/LowLayer.h"
+#include "Swiften/Network/Connection.h"
+
+namespace Swift {
+ class ConnectionLayer : public LowLayer {
+ public:
+ ConnectionLayer(Connection* connection) : connection_(connection) {
+ connection_->onDataRead.connect(onDataRead);
+ }
+
+ void writeData(const ByteArray& data) {
+ connection_->write(data);
+ }
+
+ private:
+ Connection* connection_;
+ };
+}
+
+#endif
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/Makefile.inc b/Swiften/StreamStack/Makefile.inc
new file mode 100644
index 0000000..51f7b68
--- /dev/null
+++ b/Swiften/StreamStack/Makefile.inc
@@ -0,0 +1,15 @@
+SWIFTEN_SOURCES += \
+ Swiften/StreamStack/XMPPLayer.cpp \
+ Swiften/StreamStack/StreamStack.cpp \
+ Swiften/StreamStack/LowLayer.cpp \
+ Swiften/StreamStack/HighLayer.cpp \
+ Swiften/StreamStack/WhitespacePingLayer.cpp \
+ Swiften/StreamStack/TLSLayerFactory.cpp \
+ Swiften/StreamStack/PlatformTLSLayerFactory.cpp
+
+ifeq ($(HAVE_OPENSSL),yes)
+SWIFTEN_SOURCES += \
+ Swiften/StreamStack/OpenSSLLayer.cpp
+endif
+
+include Swiften/StreamStack/UnitTest/Makefile.inc
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..bb1abf1
--- /dev/null
+++ b/Swiften/StreamStack/PlatformTLSLayerFactory.cpp
@@ -0,0 +1,34 @@
+#include "Swiften/StreamStack/PlatformTLSLayerFactory.h"
+
+#include <cassert>
+
+#ifdef HAVE_SWIFTEN_CONFIG_H
+#include "Swiften/config.h"
+#endif
+#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
+}
+
+TLSLayer* PlatformTLSLayerFactory::createTLSLayer() {
+#ifdef HAVE_OPENSSL
+ return new OpenSSLLayer();
+#else
+ assert(false);
+ return 0;
+#endif
+}
+
+}
diff --git a/Swiften/StreamStack/PlatformTLSLayerFactory.h b/Swiften/StreamStack/PlatformTLSLayerFactory.h
new file mode 100644
index 0000000..6ebee32
--- /dev/null
+++ b/Swiften/StreamStack/PlatformTLSLayerFactory.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_OpenSSLLayerFactory_H
+#define SWIFTEN_OpenSSLLayerFactory_H
+
+#include "Swiften/StreamStack/TLSLayerFactory.h"
+
+namespace Swift {
+ class PlatformTLSLayerFactory : public TLSLayerFactory {
+ public:
+ PlatformTLSLayerFactory();
+
+ bool canCreate() const;
+ virtual TLSLayer* createTLSLayer();
+ };
+}
+
+#endif
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..7dde858
--- /dev/null
+++ b/Swiften/StreamStack/StreamStack.cpp
@@ -0,0 +1,29 @@
+#include "Swiften/StreamStack/StreamStack.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/StreamStack/XMPPLayer.h"
+#include "Swiften/StreamStack/LowLayer.h"
+#include "Swiften/StreamStack/StreamLayer.h"
+
+namespace Swift {
+
+StreamStack::StreamStack(XMPPLayer* xmppLayer, 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));
+}
+
+void StreamStack::addLayer(StreamLayer* newLayer) {
+ xmppReadSlotConnection_.disconnect();
+ xmppWriteSignalConnection_.disconnect();
+
+ LowLayer* lowLayer = (layers_.empty() ? physicalLayer_ : *layers_.rbegin());
+
+ lowLayer->onDataRead.connect(boost::bind(&HighLayer::handleDataRead, newLayer, _1), boost::bsignals::at_front);
+ 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..bc2e89b
--- /dev/null
+++ b/Swiften/StreamStack/StreamStack.h
@@ -0,0 +1,45 @@
+#ifndef SWIFTEN_STREAMSTACK_H
+#define SWIFTEN_STREAMSTACK_H
+
+#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(XMPPLayer* xmppLayer, LowLayer* physicalLayer);
+
+ void addLayer(StreamLayer*);
+
+ XMPPLayer* getXMPPLayer() const {
+ return xmppLayer_;
+ }
+
+ template<typename T> T* getLayer() {
+ foreach(StreamLayer* streamLayer, layers_) {
+ T* layer = dynamic_cast<T*>(streamLayer);
+ if (layer) {
+ return layer;
+ }
+ }
+ return 0;
+ }
+
+ private:
+ XMPPLayer* xmppLayer_;
+ LowLayer* physicalLayer_;
+ std::vector<StreamLayer*> layers_;
+ boost::bsignals::connection xmppReadSlotConnection_;
+ boost::bsignals::connection xmppWriteSignalConnection_;
+ };
+}
+
+#endif
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..b4218a5
--- /dev/null
+++ b/Swiften/StreamStack/TLSLayerFactory.h
@@ -0,0 +1,16 @@
+#ifndef SWIFTEN_TLSLayerFactory_H
+#define SWIFTEN_TLSLayerFactory_H
+
+namespace Swift {
+ class TLSLayer;
+
+ class TLSLayerFactory {
+ public:
+ virtual ~TLSLayerFactory();
+ virtual bool canCreate() const = 0;
+
+ virtual TLSLayer* createTLSLayer() = 0;
+ };
+}
+
+#endif
diff --git a/Swiften/StreamStack/UnitTest/Makefile.inc b/Swiften/StreamStack/UnitTest/Makefile.inc
new file mode 100644
index 0000000..dba5f5a
--- /dev/null
+++ b/Swiften/StreamStack/UnitTest/Makefile.inc
@@ -0,0 +1,3 @@
+UNITTEST_SOURCES += \
+ Swiften/StreamStack/UnitTest/XMPPLayerTest.cpp \
+ Swiften/StreamStack/UnitTest/StreamStackTest.cpp
diff --git a/Swiften/StreamStack/UnitTest/StreamStackTest.cpp b/Swiften/StreamStack/UnitTest/StreamStackTest.cpp
new file mode 100644
index 0000000..1f6aad7
--- /dev/null
+++ b/Swiften/StreamStack/UnitTest/StreamStackTest.cpp
@@ -0,0 +1,167 @@
+#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_ = new TestLowLayer();
+ xmppStream_ = new XMPPLayer(&parserFactories_, &serializers_);
+ elementsReceived_ = 0;
+ dataWriteReceived_ = 0;
+ }
+
+ void tearDown() {
+ delete physicalStream_;
+ delete xmppStream_;
+ }
+
+ 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_);
+ std::auto_ptr<MyStreamLayer> xStream(new MyStreamLayer("X"));
+ testling.addLayer(xStream.get());
+
+ 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_);
+ std::auto_ptr<MyStreamLayer> xStream(new MyStreamLayer("X"));
+ std::auto_ptr<MyStreamLayer> yStream(new MyStreamLayer("Y"));
+ testling.addLayer(xStream.get());
+ testling.addLayer(yStream.get());
+
+ 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));
+ std::auto_ptr<MyStreamLayer> xStream(new MyStreamLayer("<"));
+ testling.addLayer(xStream.get());
+
+ 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));
+ std::auto_ptr<MyStreamLayer> xStream(new MyStreamLayer("s"));
+ std::auto_ptr<MyStreamLayer> yStream(new MyStreamLayer("<"));
+ testling.addLayer(xStream.get());
+ testling.addLayer(yStream.get());
+
+ 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));
+ std::auto_ptr<MyStreamLayer> xStream(new MyStreamLayer("X"));
+ testling.addLayer(xStream.get());
+
+ 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_;
+ TestLowLayer* physicalStream_;
+ 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..2150f4d
--- /dev/null
+++ b/Swiften/StreamStack/UnitTest/XMPPLayerTest.cpp
@@ -0,0 +1,116 @@
+#include <vector>
+#include <boost/bind.hpp>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.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));
+ testling_->writeHeader("example.com");
+
+ CPPUNIT_ASSERT_EQUAL(String("<?xml version='1.0'?><stream:stream to='example.com' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' 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..b9fbbd1
--- /dev/null
+++ b/Swiften/StreamStack/WhitespacePingLayer.cpp
@@ -0,0 +1,30 @@
+#include "Swiften/StreamStack/WhitespacePingLayer.h"
+#include "Swiften/Network/Timer.h"
+
+namespace Swift {
+
+static const int TIMEOUT_MILLISECONDS = 60000;
+
+WhitespacePingLayer::WhitespacePingLayer() {
+ timer_ = new Timer(TIMEOUT_MILLISECONDS);
+ timer_->onTick.connect(boost::bind(&WhitespacePingLayer::handleTimerTick, this));
+ timer_->start();
+}
+
+WhitespacePingLayer::~WhitespacePingLayer() {
+ delete timer_;
+}
+
+void WhitespacePingLayer::writeData(const ByteArray& data) {
+ onWriteData(data);
+}
+
+void WhitespacePingLayer::handleDataRead(const ByteArray& data) {
+ onDataRead(data);
+}
+
+void WhitespacePingLayer::handleTimerTick() {
+ onWriteData(" ");
+}
+
+}
diff --git a/Swiften/StreamStack/WhitespacePingLayer.h b/Swiften/StreamStack/WhitespacePingLayer.h
new file mode 100644
index 0000000..01fd3e3
--- /dev/null
+++ b/Swiften/StreamStack/WhitespacePingLayer.h
@@ -0,0 +1,27 @@
+#ifndef SWIFTEN_WhitespacePingLayer_H
+#define SWIFTEN_WhitespacePingLayer_H
+
+#include <boost/noncopyable.hpp>
+
+#include "Swiften/StreamStack/StreamLayer.h"
+
+namespace Swift {
+ class Timer;
+
+ class WhitespacePingLayer : public StreamLayer, boost::noncopyable {
+ public:
+ WhitespacePingLayer();
+ ~WhitespacePingLayer();
+
+ void writeData(const ByteArray& data);
+ void handleDataRead(const ByteArray& data);
+
+ private:
+ void handleTimerTick();
+
+ private:
+ Timer* timer_;
+ };
+}
+
+#endif
diff --git a/Swiften/StreamStack/XMPPLayer.cpp b/Swiften/StreamStack/XMPPLayer.cpp
new file mode 100644
index 0000000..7afa762
--- /dev/null
+++ b/Swiften/StreamStack/XMPPLayer.cpp
@@ -0,0 +1,79 @@
+#include "Swiften/StreamStack/XMPPLayer.h"
+#include "Swiften/Parser/XMPPParser.h"
+#include "Swiften/Serializer/XMPPSerializer.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 String& domain) {
+ onWriteData(ByteArray(xmppSerializer_->serializeHeader(domain)));
+}
+
+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() {
+ onStreamStart();
+}
+
+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..07b5b1c
--- /dev/null
+++ b/Swiften/StreamStack/XMPPLayer.h
@@ -0,0 +1,57 @@
+#ifndef SWIFTEN_XMPPLAYER_H
+#define SWIFTEN_XMPPLAYER_H
+
+#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 XMPPParser;
+ class PayloadParserFactoryCollection;
+ class XMPPSerializer;
+ class PayloadSerializerCollection;
+
+ class XMPPLayer : public XMPPParserClient, boost::noncopyable {
+ public:
+ XMPPLayer(
+ PayloadParserFactoryCollection* payloadParserFactories,
+ PayloadSerializerCollection* payloadSerializers);
+ ~XMPPLayer();
+
+ void writeHeader(const String& domain);
+ void writeFooter();
+ void writeElement(boost::shared_ptr<Element>);
+ void writeData(const String& data);
+
+ void parseData(ByteArray data);
+ void resetParser();
+
+ public:
+ boost::signal<void ()> 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();
+ void handleElement(boost::shared_ptr<Element>);
+ void handleStreamEnd();
+
+ void doResetParser();
+
+ private:
+ PayloadParserFactoryCollection* payloadParserFactories_;
+ XMPPParser* xmppParser_;
+ PayloadSerializerCollection* payloadSerializers_;
+ XMPPSerializer* xmppSerializer_;
+ bool resetParserAfterParse_;
+ bool inParser_;
+ };
+}
+
+#endif