From cd193389d65c4abd51c557f942348f36356f4788 Mon Sep 17 00:00:00 2001
From: Tobias Markmann <tm@ayena.de>
Date: Tue, 17 Mar 2015 13:55:12 +0100
Subject: Fix for HTTPConnectProxiedConnection to support responses in pieces

This fix lets HTTPConnectProxiedConnection buffer response data in
pieces until the end of the HTTP header is reached. Only then it will
try to parse the HTTP header.

This is *not* the HTTP chunked transfer encoding.

Test-Information:

Adjusted one test to respond in pieces and added a new test case that
verifies that response data is buffered.

Change-Id: Icfb987bdf2fc5771401a8a9c6979fa9ad1eebdca

diff --git a/Swiften/Network/HTTPConnectProxiedConnection.cpp b/Swiften/Network/HTTPConnectProxiedConnection.cpp
index fc5a6c6..942361e 100644
--- a/Swiften/Network/HTTPConnectProxiedConnection.cpp
+++ b/Swiften/Network/HTTPConnectProxiedConnection.cpp
@@ -83,7 +83,7 @@ void HTTPConnectProxiedConnection::parseHTTPHeader(const std::string& data, std:
 	}
 }
 
-void HTTPConnectProxiedConnection::sendHTTPRequest(const std::string& statusLine, std::vector<std::pair<std::string, std::string> >& headerFields) {
+void HTTPConnectProxiedConnection::sendHTTPRequest(const std::string& statusLine, const std::vector<std::pair<std::string, std::string> >& headerFields) {
 	typedef std::pair<std::string, std::string> HTTPHeaderField;
 	std::stringstream request;
 
@@ -98,13 +98,20 @@ void HTTPConnectProxiedConnection::sendHTTPRequest(const std::string& statusLine
 void HTTPConnectProxiedConnection::handleProxyInitializeData(boost::shared_ptr<SafeByteArray> data) {
 	std::string dataString = byteArrayToString(ByteArray(data->begin(), data->end()));
 	SWIFT_LOG(debug) << data << std::endl;
+	httpResponseBuffer_.append(dataString);
 
 	std::string statusLine;
 	std::vector<std::pair<std::string, std::string> > headerFields;
 
-	std::string::size_type headerEnd = dataString.find("\r\n\r\n", 0);
+	std::string::size_type headerEnd = httpResponseBuffer_.find("\r\n\r\n", 0);
+	if (headerEnd == std::string::npos) {
+		if ((httpResponseBuffer_.size() > 4) && (httpResponseBuffer_.substr(0, 4) != "HTTP")) {
+			setProxyInitializeFinished(false);
+		}
+		return;
+	}
 
-	parseHTTPHeader(dataString.substr(0, headerEnd), statusLine, headerFields);
+	parseHTTPHeader(httpResponseBuffer_.substr(0, headerEnd), statusLine, headerFields);
 
 	if (trafficFilter_) {
 		std::vector<std::pair<std::string, std::string> > newHeaderFields = trafficFilter_->filterHTTPResponseHeader(headerFields);
@@ -112,7 +119,6 @@ void HTTPConnectProxiedConnection::handleProxyInitializeData(boost::shared_ptr<S
 			std::stringstream statusLine;
 			statusLine << "CONNECT " << getServer().getAddress().toString() << ":" << getServer().getPort();
 			sendHTTPRequest(statusLine.str(), newHeaderFields);
-			SWIFT_LOG(debug) << "send HTTP request from traffic filter" << std::endl;
 			return;
 		}
 	}
@@ -126,7 +132,7 @@ void HTTPConnectProxiedConnection::handleProxyInitializeData(boost::shared_ptr<S
 				setProxyInitializeFinished(true);
 			}
 			else {
-				SWIFT_LOG(debug) << "HTTP Proxy returned an error: " << byteArrayToString(ByteArray(data->begin(), data->end())) << std::endl;
+				SWIFT_LOG(debug) << "HTTP Proxy returned an error: " << httpResponseBuffer_ << std::endl;
 				setProxyInitializeFinished(false);
 			}
 		}
@@ -138,4 +144,5 @@ void HTTPConnectProxiedConnection::handleProxyInitializeData(boost::shared_ptr<S
 	else {
 		setProxyInitializeFinished(false);
 	}
+	httpResponseBuffer_.clear();
 }
diff --git a/Swiften/Network/HTTPConnectProxiedConnection.h b/Swiften/Network/HTTPConnectProxiedConnection.h
index 7d83863..11431bf 100644
--- a/Swiften/Network/HTTPConnectProxiedConnection.h
+++ b/Swiften/Network/HTTPConnectProxiedConnection.h
@@ -41,12 +41,13 @@ namespace Swift {
 			virtual void initializeProxy();
 			virtual void handleProxyInitializeData(boost::shared_ptr<SafeByteArray> data);
 
-			void sendHTTPRequest(const std::string& statusLine, std::vector<std::pair<std::string, std::string> >& headerFields);
+			void sendHTTPRequest(const std::string& statusLine, const std::vector<std::pair<std::string, std::string> >& headerFields);
 			void parseHTTPHeader(const std::string& data, std::string& statusLine, std::vector<std::pair<std::string, std::string> >& headerFields);
 
 		private:
 			SafeByteArray authID_;
 			SafeByteArray authPassword_;
 			boost::shared_ptr<HTTPTrafficFilter> trafficFilter_;
+			std::string httpResponseBuffer_;
 	};
 }
diff --git a/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp b/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp
index fb6914e..d3db79d 100644
--- a/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp
+++ b/Swiften/Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp
@@ -49,6 +49,7 @@ class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture {
 		CPPUNIT_TEST(testConnect_CreatesConnectionToProxy);
 		CPPUNIT_TEST(testConnect_SendsConnectRequest);
 		CPPUNIT_TEST(testConnect_ReceiveConnectResponse);
+		CPPUNIT_TEST(testConnect_ReceiveConnectChunkedResponse);
 		CPPUNIT_TEST(testConnect_ReceiveMalformedConnectResponse);
 		CPPUNIT_TEST(testConnect_ReceiveErrorConnectResponse);
 		CPPUNIT_TEST(testConnect_ReceiveDataAfterConnect);
@@ -118,6 +119,21 @@ class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture {
 			CPPUNIT_ASSERT(dataRead.empty());
 		}
 
+		void testConnect_ReceiveConnectChunkedResponse() {
+			HTTPConnectProxiedConnection::ref testling(createTestling());
+			connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345));
+
+			connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef("HTTP/1.0 "));
+			eventLoop->processEvents();
+			connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef("200 Connection established\r\n\r\n"));
+			eventLoop->processEvents();
+
+			CPPUNIT_ASSERT(connectFinished);
+			CPPUNIT_ASSERT(!connectFinishedWithError);
+			CPPUNIT_ASSERT(dataRead.empty());
+		}
+
+
 		void testConnect_ReceiveMalformedConnectResponse() {
 			HTTPConnectProxiedConnection::ref testling(createTestling());
 			connect(testling, HostAddressPort(HostAddress("2.2.2.2"), 2345));
@@ -203,12 +219,16 @@ class HTTPConnectProxiedConnectionTest : public CppUnit::TestFixture {
 
 			connectionFactory->connections[0]->dataWritten.clear();
 
+			// test chunked response
+			connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef(
+				"HTTP/1.0 401 Unauthorized\r\n"));
+			eventLoop->processEvents();
 			connectionFactory->connections[0]->onDataRead(createSafeByteArrayRef(
-				"HTTP/1.0 401 Unauthorized\r\n"
 				"WWW-Authenticate: Negotiate\r\n"
 				"\r\n"));
 			eventLoop->processEvents();
 
+
 			// verify that the traffic filter got called and answered with its response
 			CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), httpTrafficFilter->filterResponses.size());
 			CPPUNIT_ASSERT_EQUAL(std::string("WWW-Authenticate"), httpTrafficFilter->filterResponses[0][0].first);
-- 
cgit v0.10.2-6-g49f6