diff options
author | Tobias Markmann <tm@ayena.de> | 2015-09-30 14:45:28 (GMT) |
---|---|---|
committer | Kevin Smith <kevin.smith@isode.com> | 2015-10-30 18:00:01 (GMT) |
commit | 87ff0cf5d0efcda674bbeb0aec306e92c7527ac5 (patch) | |
tree | 232a1d7ff34ed4479ce5c302b112cabc21d95305 /Swiften/Network/UnitTest | |
parent | 4a6950af0f324091553f7ab7271de45721b8667f (diff) | |
download | swift-87ff0cf5d0efcda674bbeb0aec306e92c7527ac5.zip swift-87ff0cf5d0efcda674bbeb0aec306e92c7527ac5.tar.bz2 |
Fix issues with HTTPTrafficFilter for HTTP proxy / BOSH usage
This patch fixes the code to handle HTTP/1.0 proxies which
do not keep the connection alive after a single request.
If a HTTPTrafficFilter returns a new header reply, the HTTP
CONNECT proxy code will issue the request over a new connection.
The final connection is kept alive, as it used for the
persistent connection forwarding.
In addition, the response status line is now passed to the
HTTPTrafficFilter handling method to provide ability to act
upon the response status code.
Missing passthrough of the HTTPTrafficFilter object the down
the stack to the HTTPConnectProxiedConnection is added.
Test-Information:
Added a unit test following an NTLM HTTP proxy authentication
with a static HTTPTrafficFilter. This and other unit tests
still pass.
Change-Id: Ida0d1aec08a60f10c1480e1eecaecbd3f87b0dca
Diffstat (limited to 'Swiften/Network/UnitTest')
-rw-r--r-- | Swiften/Network/UnitTest/BOSHConnectionTest.cpp | 22 | ||||
-rw-r--r-- | Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp | 129 |
2 files changed, 127 insertions, 24 deletions
diff --git a/Swiften/Network/UnitTest/BOSHConnectionTest.cpp b/Swiften/Network/UnitTest/BOSHConnectionTest.cpp index a115f9b..c8bf1f0 100644 --- a/Swiften/Network/UnitTest/BOSHConnectionTest.cpp +++ b/Swiften/Network/UnitTest/BOSHConnectionTest.cpp @@ -1,28 +1,28 @@ /* - * Copyright (c) 2011 Isode Limited. + * Copyright (c) 2011-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ +#include <boost/bind.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/optional.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/smart_ptr/make_shared.hpp> + #include <QA/Checker/IO.h> #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> -#include <boost/optional.hpp> -#include <boost/bind.hpp> -#include <boost/smart_ptr/make_shared.hpp> -#include <boost/shared_ptr.hpp> -#include <boost/lexical_cast.hpp> - #include <Swiften/Base/Algorithm.h> +#include <Swiften/EventLoop/DummyEventLoop.h> +#include <Swiften/Network/BOSHConnection.h> #include <Swiften/Network/Connection.h> #include <Swiften/Network/ConnectionFactory.h> -#include <Swiften/Network/BOSHConnection.h> +#include <Swiften/Network/DummyTimerFactory.h> #include <Swiften/Network/HostAddressPort.h> #include <Swiften/Network/StaticDomainNameResolver.h> -#include <Swiften/Network/DummyTimerFactory.h> -#include <Swiften/EventLoop/DummyEventLoop.h> #include <Swiften/Parser/PlatformXMLParserFactory.h> using namespace Swift; @@ -39,7 +39,7 @@ class BOSHConnectionTest : public CppUnit::TestFixture { CPPUNIT_TEST(testRead_Fragment); CPPUNIT_TEST(testHTTPRequest); CPPUNIT_TEST(testHTTPRequest_Empty); - CPPUNIT_TEST_SUITE_END(); + CPPUNIT_TEST_SUITE_END(); public: void setUp() { diff --git a/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp b/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp index d3db79d..56ace5c 100644 --- a/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp +++ b/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp @@ -3,26 +3,30 @@ * All rights reserved. * See the COPYING file for more information. */ + +#include <boost/algorithm/string.hpp> +#include <boost/bind.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/optional.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/smart_ptr/make_shared.hpp> + #include <QA/Checker/IO.h> #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> -#include <boost/optional.hpp> -#include <boost/bind.hpp> -#include <boost/smart_ptr/make_shared.hpp> -#include <boost/shared_ptr.hpp> - #include <Swiften/Base/Algorithm.h> +#include <Swiften/Base/Log.h> +#include <Swiften/Base/foreach.h> +#include <Swiften/EventLoop/DummyEventLoop.h> #include <Swiften/Network/Connection.h> #include <Swiften/Network/ConnectionFactory.h> +#include <Swiften/Network/DummyTimerFactory.h> #include <Swiften/Network/HTTPConnectProxiedConnection.h> #include <Swiften/Network/HTTPTrafficFilter.h> #include <Swiften/Network/HostAddressPort.h> #include <Swiften/Network/StaticDomainNameResolver.h> -#include <Swiften/Network/DummyTimerFactory.h> -#include <Swiften/EventLoop/DummyEventLoop.h> -#include <Swiften/Base/Log.h> using namespace Swift; @@ -32,7 +36,7 @@ namespace { ExampleHTTPTrafficFilter() {} virtual ~ExampleHTTPTrafficFilter() {} - virtual std::vector<std::pair<std::string, std::string> > filterHTTPResponseHeader(const std::vector<std::pair<std::string, std::string> >& response) { + virtual std::vector<std::pair<std::string, std::string> > filterHTTPResponseHeader(const std::string& /* statusLine */, const std::vector<std::pair<std::string, std::string> >& response) { filterResponses.push_back(response); SWIFT_LOG(debug) << std::endl; return filterResponseReturn; @@ -42,6 +46,46 @@ namespace { std::vector<std::pair<std::string, std::string> > filterResponseReturn; }; + + class ProxyAuthenticationHTTPTrafficFilter : public HTTPTrafficFilter { + static std::string to_lower(const std::string& str) { + std::string lower = str; + boost::algorithm::to_lower(lower); + return lower; + } + + public: + ProxyAuthenticationHTTPTrafficFilter() {} + virtual ~ProxyAuthenticationHTTPTrafficFilter() {} + + virtual std::vector<std::pair<std::string, std::string> > filterHTTPResponseHeader(const std::string& statusLine, const std::vector<std::pair<std::string, std::string> >& response) { + std::vector<std::pair<std::string, std::string> > filterResponseReturn; + std::vector<std::string> statusLineFields; + boost::split(statusLineFields, statusLine, boost::is_any_of(" "), boost::token_compress_on); + + int statusCode = boost::lexical_cast<int>(statusLineFields[1]); + if (statusCode == 407) { + typedef std::pair<std::string, std::string> StrPair; + foreach (const StrPair& field, response) { + if (to_lower(field.first) == to_lower("Proxy-Authenticate")) { + if (field.second.size() >= 6 && field.second.substr(0, 6) == " NTLM ") { + filterResponseReturn.push_back(std::pair<std::string, std::string>("Proxy-Authorization", "NTLM TlRMTVNTUAADAAAAGAAYAHIAAAAYABgAigAAABIAEgBIAAAABgAGAFoAAAASABIVNTUAADAAYAAAABAAEACiAAAANYKI4gUBKAoAAAAPTABBAEIAUwBNAE8ASwBFADMAXwBxAGEATABBAEIAUwBNAE8ASwBFADMA0NKq8HYYhj8AAAAAAAAAAAAAAAAAAAAAOIiih3mR+AkyM4r99sy1mdFonCu2ILODro1WTTrJ4b4JcXEzUBA2Ig==")); + return filterResponseReturn; + } + else if (field.second.size() >= 5 && field.second.substr(0, 5) == " NTLM") { + filterResponseReturn.push_back(std::pair<std::string, std::string>("Proxy-Authorization", "NTLM TlRMTVNTUAABAAAAt7II4gkACQAxAAAACQAJACgAAAVNTUAADAAFASgKAAAAD0xBQlNNT0tFM1dPUktHUk9VUA==")); + return filterResponseReturn; + } + } + } + + return filterResponseReturn; + } + else { + return std::vector<std::pair<std::string, std::string> >(); + } + } + }; } class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture { @@ -57,6 +101,7 @@ class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture { CPPUNIT_TEST(testDisconnect_AfterConnectRequest); CPPUNIT_TEST(testDisconnect_AfterConnect); CPPUNIT_TEST(testTrafficFilter); + CPPUNIT_TEST(testTrafficFilterNoConnectionReuse); CPPUNIT_TEST_SUITE_END(); public: @@ -71,6 +116,7 @@ class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture { timerFactory = new DummyTimerFactory(); connectionFactory = new MockConnectionFactory(eventLoop); connectFinished = false; + connectFinishedWithError = false; disconnected = false; } @@ -238,12 +284,69 @@ class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture { eventLoop->processEvents(); // verify that the traffic filter answer is send over the wire - CPPUNIT_ASSERT_EQUAL(createByteArray("CONNECT 2.2.2.2:2345\r\nAuthorization:Negotiate a87421000492aa874209af8bc028\r\n\r\n"), connectionFactory->connections[0]->dataWritten); + CPPUNIT_ASSERT_EQUAL(createByteArray("CONNECT 2.2.2.2:2345 HTTP/1.1\r\nAuthorization: Negotiate a87421000492aa874209af8bc028\r\n\r\n"), connectionFactory->connections[1]->dataWritten); // verify that after without the default response, the traffic filter is skipped, authentication proceeds and traffic goes right through - connectionFactory->connections[0]->dataWritten.clear(); + connectionFactory->connections[1]->dataWritten.clear(); testling->write(createSafeByteArray("abcdef")); - CPPUNIT_ASSERT_EQUAL(createByteArray("abcdef"), connectionFactory->connections[0]->dataWritten); + CPPUNIT_ASSERT_EQUAL(createByteArray("abcdef"), connectionFactory->connections[1]->dataWritten); + } + + void testTrafficFilterNoConnectionReuse() { + HTTPConnectProxiedConnection::ref testling = createTestling(); + + boost::shared_ptr<ProxyAuthenticationHTTPTrafficFilter> httpTrafficFilter = boost::make_shared<ProxyAuthenticationHTTPTrafficFilter>(); + testling->setHTTPTrafficFilter(httpTrafficFilter); + + connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345)); + + // First HTTP CONNECT request assumes the proxy will work. + CPPUNIT_ASSERT_EQUAL(createByteArray("CONNECT 2.2.2.2:2345 HTTP/1.1\r\n" + "\r\n"), connectionFactory->connections[0]->dataWritten); + + // First reply presents initiator with authentication options. + connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef( + "HTTP/1.0 407 ProxyAuthentication Required\r\n" + "proxy-Authenticate: Negotiate\r\n" + "Proxy-Authenticate: Kerberos\r\n" + "proxy-Authenticate: NTLM\r\n" + "\r\n")); + eventLoop->processEvents(); + CPPUNIT_ASSERT_EQUAL(false, connectFinished); + CPPUNIT_ASSERT_EQUAL(false, connectFinishedWithError); + + // The HTTP proxy responds with code 407, so the traffic filter should inject the authentication response on a new connection. + CPPUNIT_ASSERT_EQUAL(createByteArray("CONNECT 2.2.2.2:2345 HTTP/1.1\r\n" + "Proxy-Authorization: NTLM TlRMTVNTUAABAAAAt7II4gkACQAxAAAACQAJACgAAAVNTUAADAAFASgKAAAAD0xBQlNNT0tFM1dPUktHUk9VUA==\r\n" + "\r\n"), connectionFactory->connections[1]->dataWritten); + + // The proxy responds with another authentication step. + connectionFactory->connections[1]->onDataRead(createSafeByteArrayRef( + "HTTP/1.0 407 ProxyAuthentication Required\r\n" + "Proxy-Authenticate: NTLM TlRMTVNTUAACAAAAEAAQADgAAAA1goriluCDYHcYI/sAAAAAAAAAAFQAVABIAAAABQLODgAAAA9TAFAASQBSAEkAVAAxAEIAAgAQAFMAUABJAFIASQBUADEAQgABABAAUwBQAEkAUgBJAFQAMQBCAAQAEABzAHAAaQByAGkAdAAxAGIAAwAQAHMAcABpAHIAaQB0ADEAYgAAAAAA\r\n" + "\r\n")); + eventLoop->processEvents(); + CPPUNIT_ASSERT_EQUAL(false, connectFinished); + CPPUNIT_ASSERT_EQUAL(false, connectFinishedWithError); + + // Last HTTP request that should succeed. Further traffic will go over the connection of this request. + CPPUNIT_ASSERT_EQUAL(createByteArray("CONNECT 2.2.2.2:2345 HTTP/1.1\r\n" + "Proxy-Authorization: NTLM TlRMTVNTUAADAAAAGAAYAHIAAAAYABgAigAAABIAEgBIAAAABgAGAFoAAAASABIVNTUAADAAYAAAABAAEACiAAAANYKI4gUBKAoAAAAPTABBAEIAUwBNAE8ASwBFADMAXwBxAGEATABBAEIAUwBNAE8ASwBFADMA0NKq8HYYhj8AAAAAAAAAAAAAAAAAAAAAOIiih3mR+AkyM4r99sy1mdFonCu2ILODro1WTTrJ4b4JcXEzUBA2Ig==\r\n" + "\r\n"), connectionFactory->connections[2]->dataWritten); + + connectionFactory->connections[2]->onDataRead(createSafeByteArrayRef( + "HTTP/1.0 200 OK\r\n" + "\r\n")); + eventLoop->processEvents(); + + // The HTTP CONNECT proxy initialization finished without error. + CPPUNIT_ASSERT_EQUAL(true, connectFinished); + CPPUNIT_ASSERT_EQUAL(false, connectFinishedWithError); + + // Further traffic is written directly, without interception of the filter. + connectionFactory->connections[2]->dataWritten.clear(); + testling->write(createSafeByteArray("This is some basic data traffic.")); + CPPUNIT_ASSERT_EQUAL(createByteArray("This is some basic data traffic."), connectionFactory->connections[2]->dataWritten); } private: @@ -307,6 +410,7 @@ class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture { boost::shared_ptr<Connection> createConnection() { boost::shared_ptr<MockConnection> connection = boost::make_shared<MockConnection>(failingPorts, eventLoop); connections.push_back(connection); + SWIFT_LOG(debug) << "new connection created" << std::endl; return connection; } @@ -324,7 +428,6 @@ class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture { StaticDomainNameResolver* resolver; MockConnectionFactory* connectionFactory; TimerFactory* timerFactory; - std::vector< boost::shared_ptr<MockConnection> > connections; bool connectFinished; bool connectFinishedWithError; bool disconnected; |