소스 검색

Merge pull request #827 from web2098/proxy

Websocket Proxy Support
Paul-Louis Ageneau 2 년 전
부모
커밋
820b113441

+ 2 - 0
CMakeLists.txt

@@ -125,6 +125,7 @@ set(LIBDATACHANNEL_IMPL_SOURCES
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/sha.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/pollinterrupter.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/pollservice.cpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/httpproxytransport.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/tcpserver.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/tcptransport.cpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/tlstransport.cpp
@@ -157,6 +158,7 @@ set(LIBDATACHANNEL_IMPL_HEADERS
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/sha.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/pollinterrupter.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/pollservice.hpp
+	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/httpproxytransport.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/tcpserver.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/tcptransport.hpp
 	${CMAKE_CURRENT_SOURCE_DIR}/src/impl/tlstransport.hpp

+ 1 - 1
include/rtc/rtc.h

@@ -374,7 +374,7 @@ int rtcSetSsrcForType(const char *mediaType, const char *sdp, char *buffer, cons
 
 typedef struct {
 	bool disableTlsVerification; // if true, don't verify the TLS certificate
-	const char *proxyServer;     // unsupported for now
+	const char *proxyServer;
 	const char **protocols;
 	int protocolsCount;
 	int pingInterval;        // in milliseconds, 0 means default, < 0 means disabled

+ 1 - 1
include/rtc/websocket.hpp

@@ -34,7 +34,7 @@ public:
 
 	struct Configuration {
 		bool disableTlsVerification = false; // if true, don't verify the TLS certificate
-		optional<ProxyServer> proxyServer;   // unsupported for now
+		optional<ProxyServer> proxyServer;
 		std::vector<string> protocols;
 		optional<std::chrono::milliseconds> pingInterval; // zero to disable
 		optional<int> maxOutstandingPings;

+ 144 - 0
src/impl/httpproxytransport.cpp

@@ -0,0 +1,144 @@
+/**
+ * Copyright (c) 2020-2021 Paul-Louis Ageneau
+ * Copyright (c) 2023 Eric Gressman
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#include "httpproxytransport.hpp"
+#include "tcptransport.hpp"
+#include "utils.hpp"
+
+#if RTC_ENABLE_WEBSOCKET
+
+namespace rtc::impl {
+
+using std::to_string;
+using std::chrono::system_clock;
+
+HttpProxyTransport::HttpProxyTransport(shared_ptr<TcpTransport> lower, std::string hostname, std::string service, state_callback stateCallback)
+    : Transport(lower, std::move(stateCallback))
+	, mHostname( std::move(hostname) )
+	, mService( std::move(service) )
+{
+	if (!lower->isActive())
+		throw std::logic_error("Http proxy creation failed, expects lower transport to be active");
+
+	PLOG_DEBUG << "Initializing http Proxy transport";
+}
+
+HttpProxyTransport::~HttpProxyTransport() { unregisterIncoming(); }
+
+void HttpProxyTransport::start() {
+	registerIncoming();
+
+	changeState(State::Connecting);
+	sendHttpRequest();
+}
+
+void HttpProxyTransport::stop() {
+	unregisterIncoming();
+}
+
+bool HttpProxyTransport::send(message_ptr message) {
+	std::lock_guard lock(mSendMutex);
+
+	if (state() != State::Connected)
+		throw std::runtime_error("Http proxy connection is not open");
+
+	PLOG_VERBOSE << "Send size=" << message->size();
+	return outgoing(message);
+}
+
+bool HttpProxyTransport::isActive() const { return true; }
+
+void HttpProxyTransport::incoming(message_ptr message) {
+	auto s = state();
+	if (s != State::Connecting && s != State::Connected)
+		return; // Drop
+
+	if (message) {
+		PLOG_VERBOSE << "Incoming size=" << message->size();
+
+		try {
+			if (state() == State::Connecting) {
+				mBuffer.insert(mBuffer.end(), message->begin(), message->end());
+				if (size_t len = parseHttpResponse(mBuffer.data(), mBuffer.size())) {
+					PLOG_INFO << "Http proxy connection open";
+					changeState(State::Connected);
+					mBuffer.erase(mBuffer.begin(), mBuffer.begin() + len);
+
+					if( !mBuffer.empty() )
+					{
+						recv(make_message(mBuffer));
+						mBuffer.clear();
+					}
+				}
+			}
+			else if (state() == State::Connected)
+			{
+				recv(std::move(message));
+			}
+
+			return;
+		} catch (const std::exception &e) {
+			PLOG_ERROR << e.what();
+		}
+	}
+
+	if (state() == State::Connected) {
+		PLOG_INFO << "Http Proxy disconnected";
+		changeState(State::Disconnected);
+		recv(nullptr);
+	} else {
+		PLOG_ERROR << "Http Proxy failed";
+		changeState(State::Failed);
+	}
+}
+
+bool HttpProxyTransport::sendHttpRequest() {
+	PLOG_DEBUG << "Sending proxy http request";
+
+	const string request = generateHttpRequest();
+	auto data = reinterpret_cast<const byte *>(request.data());
+	return outgoing(make_message(data, data + request.size()));
+}
+
+std::string HttpProxyTransport::generateHttpRequest()
+{
+	std::string out =
+		"CONNECT " +
+		mHostname + ":" + mService +
+		" HTTP/1.1\r\nHost: " +
+		mHostname + "\r\n\r\n";
+	return out;
+}
+
+size_t HttpProxyTransport::parseHttpResponse( std::byte* buffer, size_t size )
+{
+	std::list<string> lines;
+	size_t length = utils::parseHttpLines(buffer, size, lines);
+	if (length == 0)
+		return 0;
+
+	if (lines.empty())
+		throw std::runtime_error("Invalid http request for proxy");
+
+	std::istringstream status(std::move(lines.front()));
+	lines.pop_front();
+
+	string protocol;
+	unsigned int code = 0;
+	status >> protocol >> code;
+
+	if (code != 200)
+		throw std::runtime_error("Unexpected response code " + to_string(code) + " for proxy");
+
+	return length;
+}
+
+} // namespace rtc::impl
+
+#endif

+ 50 - 0
src/impl/httpproxytransport.hpp

@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2020-2021 Paul-Louis Ageneau
+ * Copyright (c) 2023 Eric Gressman
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef RTC_IMPL_TCP_PROXY_TRANSPORT_H
+#define RTC_IMPL_TCP_PROXY_TRANSPORT_H
+
+#include "common.hpp"
+#include "transport.hpp"
+
+#if RTC_ENABLE_WEBSOCKET
+
+namespace rtc::impl {
+
+class TcpTransport;
+
+class HttpProxyTransport final : public Transport, public std::enable_shared_from_this<HttpProxyTransport> {
+public:
+	HttpProxyTransport(shared_ptr<TcpTransport> lower, std::string hostname, std::string service,
+				state_callback stateCallback);
+	~HttpProxyTransport();
+
+	void start() override;
+	void stop() override;
+	bool send(message_ptr message) override;
+
+	bool isActive() const;
+
+private:
+	void incoming(message_ptr message) override;
+	bool sendHttpRequest();
+	std::string generateHttpRequest();
+	size_t parseHttpResponse( std::byte* buffer, size_t size );
+
+	std::string mHostname;
+	std::string mService;
+	binary mBuffer;
+	std::mutex mSendMutex;
+};
+
+} // namespace rtc::impl
+
+#endif
+
+#endif

+ 11 - 6
src/impl/tlstransport.cpp

@@ -8,6 +8,7 @@
 
 #include "tlstransport.hpp"
 #include "tcptransport.hpp"
+#include "httpproxytransport.hpp"
 #include "threadpool.hpp"
 
 #if RTC_ENABLE_WEBSOCKET
@@ -58,10 +59,12 @@ void TlsTransport::Cleanup() {
 	// Nothing to do
 }
 
-TlsTransport::TlsTransport(shared_ptr<TcpTransport> lower, optional<string> host,
+TlsTransport::TlsTransport(variant<shared_ptr<TcpTransport>, shared_ptr<HttpProxyTransport>> lower, optional<string> host,
                            certificate_ptr certificate, state_callback callback)
-    : Transport(lower, std::move(callback)), mHost(std::move(host)), mIsClient(lower->isActive()),
-      mIncomingQueue(RECV_QUEUE_LIMIT, message_size_func) {
+    : Transport(std::visit([](auto l) { return std::static_pointer_cast<Transport>(l); }, lower),
+			    std::move(callback)), mHost(std::move(host))
+	, mIsClient(std::visit([](auto l) { return l->isActive(); }, lower))
+	, mIncomingQueue(RECV_QUEUE_LIMIT, message_size_func) {
 
 	PLOG_DEBUG << "Initializing TLS transport (GnuTLS)";
 
@@ -308,10 +311,12 @@ void TlsTransport::Cleanup() {
 	// Nothing to do
 }
 
-TlsTransport::TlsTransport(shared_ptr<TcpTransport> lower, optional<string> host,
+TlsTransport::TlsTransport(variant<shared_ptr<TcpTransport>, shared_ptr<HttpProxyTransport>> lower, optional<string> host,
                            certificate_ptr certificate, state_callback callback)
-    : Transport(lower, std::move(callback)), mHost(std::move(host)), mIsClient(lower->isActive()),
-      mIncomingQueue(RECV_QUEUE_LIMIT, message_size_func) {
+    : Transport(std::visit([](auto l) { return std::static_pointer_cast<Transport>(l); }, lower),
+				std::move(callback)), mHost(std::move(host))
+	, mIsClient(std::visit([](auto l) { return l->isActive(); }, lower))
+	, mIncomingQueue(RECV_QUEUE_LIMIT, message_size_func) {
 
 	PLOG_DEBUG << "Initializing TLS transport (OpenSSL)";
 

+ 2 - 1
src/impl/tlstransport.hpp

@@ -23,13 +23,14 @@
 namespace rtc::impl {
 
 class TcpTransport;
+class HttpProxyTransport;
 
 class TlsTransport : public Transport, public std::enable_shared_from_this<TlsTransport> {
 public:
 	static void Init();
 	static void Cleanup();
 
-	TlsTransport(shared_ptr<TcpTransport> lower, optional<string> host, certificate_ptr certificate,
+	TlsTransport(variant<shared_ptr<TcpTransport>, shared_ptr<HttpProxyTransport>> lower, optional<string> host, certificate_ptr certificate,
 	             state_callback callback);
 	virtual ~TlsTransport();
 

+ 40 - 0
src/impl/utils.cpp

@@ -128,4 +128,44 @@ std::seed_seq random_seed() {
 	return std::seed_seq(seed.begin(), seed.end());
 }
 
+size_t parseHttpLines(const byte *buffer, size_t size, std::list<string> &lines) {
+	lines.clear();
+	auto begin = reinterpret_cast<const char *>(buffer);
+	auto end = begin + size;
+	auto cur = begin;
+	while (true) {
+		auto last = cur;
+		cur = std::find(cur, end, '\n');
+		if (cur == end)
+			return 0;
+		string line(last, cur != begin && *std::prev(cur) == '\r' ? std::prev(cur++) : cur++);
+		if (line.empty())
+			break;
+		lines.emplace_back(std::move(line));
+	}
+
+	return cur - begin;
+}
+
+std::multimap<string, string> parseHttpHeaders(const std::list<string> &lines) {
+	std::multimap<string, string> headers;
+	for (const auto &line : lines) {
+		if (size_t pos = line.find_first_of(':'); pos != string::npos) {
+			string key = line.substr(0, pos);
+			string value = "";
+			if (size_t subPos = line.find_first_not_of(' ', pos + 1); subPos != string::npos )
+			{
+				value = line.substr(subPos);
+			}
+			std::transform(key.begin(), key.end(), key.begin(),
+			               [](char c) { return std::tolower(c); });
+			headers.emplace(std::move(key), std::move(value));
+		} else {
+			headers.emplace(line, "");
+		}
+	}
+
+	return headers;
+}
+
 } // namespace rtc::impl::utils

+ 8 - 0
src/impl/utils.hpp

@@ -13,6 +13,8 @@
 
 #include <climits>
 #include <limits>
+#include <list>
+#include <map>
 #include <random>
 #include <vector>
 
@@ -32,6 +34,12 @@ string base64_encode(const binary &data);
 // Return a random seed sequence
 std::seed_seq random_seed();
 
+// Parse an http message into lines
+size_t parseHttpLines(const byte *buffer, size_t size, std::list<string> &lines);
+
+// Parse headers of a http message
+std::multimap<string, string> parseHttpHeaders(const std::list<string> &lines);
+
 template <typename Generator, typename Result = typename Generator::result_type>
 struct random_engine_wrapper {
 	Generator &engine;

+ 1 - 1
src/impl/verifiedtlstransport.cpp

@@ -13,7 +13,7 @@
 
 namespace rtc::impl {
 
-VerifiedTlsTransport::VerifiedTlsTransport(shared_ptr<TcpTransport> lower, string host,
+VerifiedTlsTransport::VerifiedTlsTransport(variant<shared_ptr<TcpTransport>, shared_ptr<HttpProxyTransport>> lower, string host,
                                            certificate_ptr certificate, state_callback callback)
     : TlsTransport(std::move(lower), std::move(host), std::move(certificate), std::move(callback)) {
 

+ 1 - 1
src/impl/verifiedtlstransport.hpp

@@ -17,7 +17,7 @@ namespace rtc::impl {
 
 class VerifiedTlsTransport final : public TlsTransport {
 public:
-	VerifiedTlsTransport(shared_ptr<TcpTransport> lower, string host, certificate_ptr certificate,
+	VerifiedTlsTransport(variant<shared_ptr<TcpTransport>, shared_ptr<HttpProxyTransport>> lower, string host, certificate_ptr certificate,
 	                     state_callback callback);
 	~VerifiedTlsTransport();
 };

+ 83 - 11
src/impl/websocket.cpp

@@ -15,6 +15,7 @@
 #include "utils.hpp"
 
 #include "tcptransport.hpp"
+#include "httpproxytransport.hpp"
 #include "tlstransport.hpp"
 #include "verifiedtlstransport.hpp"
 #include "wstransport.hpp"
@@ -47,10 +48,6 @@ void WebSocket::open(const string &url) {
 	if (state != State::Closed)
 		throw std::logic_error("WebSocket must be closed before opening");
 
-	if (config.proxyServer) {
-		PLOG_WARNING << "Proxy server support for WebSocket is not implemented";
-	}
-
 	// Modified regex from RFC 3986, see https://www.rfc-editor.org/rfc/rfc3986.html#appendix-B
 	static const char *rs =
 	    R"(^(([^:.@/?#]+):)?(/{0,2}((([^:@]*)(:([^@]*))?)@)?(([^:/?#]*)(:([^/?#]*))?))?([^?#]*)(\?([^#]*))?(#(.*))?)";
@@ -101,11 +98,20 @@ void WebSocket::open(const string &url) {
 	if (string query = m[15]; !query.empty())
 		path += "?" + query;
 
-	mHostname = hostname; // for TLS SNI
+	mHostname = hostname; // for TLS SNI and Proxy
+	mService = service; //For proxy
 	std::atomic_store(&mWsHandshake, std::make_shared<WsHandshake>(host, path, config.protocols));
 
 	changeState(State::Connecting);
-	setTcpTransport(std::make_shared<TcpTransport>(hostname, service, nullptr));
+
+	if (config.proxyServer)
+	{
+		setTcpTransport(std::make_shared<TcpTransport>(config.proxyServer->hostname, std::to_string(config.proxyServer->port), nullptr));
+	}
+	else
+	{
+		setTcpTransport(std::make_shared<TcpTransport>(hostname, service, nullptr));
+	}
 }
 
 void WebSocket::close() {
@@ -218,7 +224,9 @@ shared_ptr<TcpTransport> WebSocket::setTcpTransport(shared_ptr<TcpTransport> tra
 				return;
 			switch (transportState) {
 			case State::Connected:
-				if (mIsSecure)
+				if (config.proxyServer)
+					initProxyTransport();
+				else if (mIsSecure)
 					initTlsTransport();
 				else
 					initWsTransport();
@@ -250,6 +258,52 @@ shared_ptr<TcpTransport> WebSocket::setTcpTransport(shared_ptr<TcpTransport> tra
 	}
 }
 
+shared_ptr<HttpProxyTransport> WebSocket::initProxyTransport() {
+	PLOG_VERBOSE << "Starting Tcp Proxy transport";
+	using State = HttpProxyTransport::State;
+	try {
+		if (auto transport = std::atomic_load(&mProxyTransport))
+			return transport;
+
+		auto lower = std::atomic_load(&mTcpTransport);
+		if (!lower)
+			throw std::logic_error("No underlying TCP transport for Proxy transport");
+
+		auto stateChangeCallback = [this, weak_this = weak_from_this()](State transportState) {
+			auto shared_this = weak_this.lock();
+			if (!shared_this)
+				return;
+			switch (transportState) {
+			case State::Connected:
+				if (mIsSecure)
+					initTlsTransport();
+				else
+					initWsTransport();
+				break;
+			case State::Failed:
+				triggerError("Proxy connection failed");
+				remoteClose();
+				break;
+			case State::Disconnected:
+				remoteClose();
+				break;
+			default:
+				// Ignore
+				break;
+			}
+		};
+
+		auto transport = std::make_shared<HttpProxyTransport>( lower, mHostname.value(), mService.value(), stateChangeCallback );
+
+		return emplaceTransport(this, &mProxyTransport, std::move(transport));
+
+	} catch (const std::exception &e) {
+		PLOG_ERROR << e.what();
+		remoteClose();
+		throw std::runtime_error("Tcp Proxy transport initialization failed");
+	}
+}
+
 shared_ptr<TlsTransport> WebSocket::initTlsTransport() {
 	PLOG_VERBOSE << "Starting TLS transport";
 	using State = TlsTransport::State;
@@ -257,9 +311,20 @@ shared_ptr<TlsTransport> WebSocket::initTlsTransport() {
 		if (auto transport = std::atomic_load(&mTlsTransport))
 			return transport;
 
-		auto lower = std::atomic_load(&mTcpTransport);
-		if (!lower)
-			throw std::logic_error("No underlying TCP transport for TLS transport");
+		variant<shared_ptr<TcpTransport>, shared_ptr<HttpProxyTransport>> lower;
+		if (config.proxyServer) {
+			auto transport = std::atomic_load(&mProxyTransport);
+			if (!transport)
+				throw std::logic_error("No underlying proxy transport for TLS transport");
+
+			lower = transport;
+		} else {
+			auto transport = std::atomic_load(&mTcpTransport);
+			if (!transport)
+				throw std::logic_error("No underlying TCP transport for TLS transport");
+
+			lower = transport;
+		}
 
 		auto stateChangeCallback = [this, weak_this = weak_from_this()](State transportState) {
 			auto shared_this = weak_this.lock();
@@ -314,12 +379,19 @@ shared_ptr<WsTransport> WebSocket::initWsTransport() {
 		if (auto transport = std::atomic_load(&mWsTransport))
 			return transport;
 
-		variant<shared_ptr<TcpTransport>, shared_ptr<TlsTransport>> lower;
+		variant<shared_ptr<TcpTransport>, shared_ptr<HttpProxyTransport>, shared_ptr<TlsTransport>> lower;
 		if (mIsSecure) {
 			auto transport = std::atomic_load(&mTlsTransport);
 			if (!transport)
 				throw std::logic_error("No underlying TLS transport for WebSocket transport");
 
+			lower = transport;
+		} 
+		else if (config.proxyServer) {
+			auto transport = std::atomic_load(&mProxyTransport);
+			if (!transport)
+				throw std::logic_error("No underlying proxy transport for WebSocket transport");
+
 			lower = transport;
 		} else {
 			auto transport = std::atomic_load(&mTcpTransport);

+ 5 - 1
src/impl/websocket.hpp

@@ -17,6 +17,7 @@
 #include "message.hpp"
 #include "queue.hpp"
 #include "tcptransport.hpp"
+#include "httpproxytransport.hpp"
 #include "tlstransport.hpp"
 #include "wstransport.hpp"
 
@@ -51,6 +52,7 @@ struct WebSocket final : public Channel, public std::enable_shared_from_this<Web
 	bool changeState(State state);
 
 	shared_ptr<TcpTransport> setTcpTransport(shared_ptr<TcpTransport> transport);
+	shared_ptr<HttpProxyTransport> initProxyTransport();
 	shared_ptr<TlsTransport> initTlsTransport();
 	shared_ptr<WsTransport> initWsTransport();
 	shared_ptr<TcpTransport> getTcpTransport() const;
@@ -70,9 +72,11 @@ private:
 	const certificate_ptr mCertificate;
 	bool mIsSecure;
 
-	optional<string> mHostname; // for TLS SNI
+	optional<string> mHostname; // for TLS SNI and Proxy
+	optional<string> mService; // for Proxy
 
 	shared_ptr<TcpTransport> mTcpTransport;
+	shared_ptr<HttpProxyTransport> mProxyTransport;
 	shared_ptr<TlsTransport> mTlsTransport;
 	shared_ptr<WsTransport> mWsTransport;
 	shared_ptr<WsHandshake> mWsHandshake;

+ 4 - 44
src/impl/wshandshake.cpp

@@ -133,7 +133,7 @@ string WsHandshake::generateHttpError(int responseCode) {
 size_t WsHandshake::parseHttpRequest(const byte *buffer, size_t size) {
 	std::unique_lock lock(mMutex);
 	std::list<string> lines;
-	size_t length = parseHttpLines(buffer, size, lines);
+	size_t length = utils::parseHttpLines(buffer, size, lines);
 	if (length == 0)
 		return 0;
 
@@ -151,7 +151,7 @@ size_t WsHandshake::parseHttpRequest(const byte *buffer, size_t size) {
 
 	mPath = std::move(path);
 
-	auto headers = parseHttpHeaders(lines);
+	auto headers = utils::parseHttpHeaders(lines);
 
 	auto h = headers.find("host");
 	if (h == headers.end())
@@ -185,7 +185,7 @@ size_t WsHandshake::parseHttpRequest(const byte *buffer, size_t size) {
 size_t WsHandshake::parseHttpResponse(const byte *buffer, size_t size) {
 	std::unique_lock lock(mMutex);
 	std::list<string> lines;
-	size_t length = parseHttpLines(buffer, size, lines);
+	size_t length = utils::parseHttpLines(buffer, size, lines);
 	if (length == 0)
 		return 0;
 
@@ -202,7 +202,7 @@ size_t WsHandshake::parseHttpResponse(const byte *buffer, size_t size) {
 	if (code != 101)
 		throw std::runtime_error("Unexpected response code " + to_string(code) + " for WebSocket");
 
-	auto headers = parseHttpHeaders(lines);
+	auto headers = utils::parseHttpHeaders(lines);
 
 	auto h = headers.find("upgrade");
 	if (h == headers.end())
@@ -238,46 +238,6 @@ string WsHandshake::computeAcceptKey(const string &key) {
 	return utils::base64_encode(Sha1(string(key) + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
 }
 
-size_t WsHandshake::parseHttpLines(const byte *buffer, size_t size, std::list<string> &lines) {
-	lines.clear();
-	auto begin = reinterpret_cast<const char *>(buffer);
-	auto end = begin + size;
-	auto cur = begin;
-	while (true) {
-		auto last = cur;
-		cur = std::find(cur, end, '\n');
-		if (cur == end)
-			return 0;
-		string line(last, cur != begin && *std::prev(cur) == '\r' ? std::prev(cur++) : cur++);
-		if (line.empty())
-			break;
-		lines.emplace_back(std::move(line));
-	}
-
-	return cur - begin;
-}
-
-std::multimap<string, string> WsHandshake::parseHttpHeaders(const std::list<string> &lines) {
-	std::multimap<string, string> headers;
-	for (const auto &line : lines) {
-		if (size_t pos = line.find_first_of(':'); pos != string::npos) {
-			string key = line.substr(0, pos);
-			string value = "";
-			if (size_t subPos = line.find_first_not_of(' ', pos + 1); subPos != string::npos )
-			{
-				value = line.substr(subPos);
-			}
-			std::transform(key.begin(), key.end(), key.begin(),
-			               [](char c) { return std::tolower(c); });
-			headers.emplace(std::move(key), std::move(value));
-		} else {
-			headers.emplace(line, "");
-		}
-	}
-
-	return headers;
-}
-
 WsHandshake::Error::Error(const string &w) : std::runtime_error(w) {}
 
 WsHandshake::RequestError::RequestError(const string &w, int responseCode)

+ 0 - 4
src/impl/wshandshake.hpp

@@ -13,8 +13,6 @@
 
 #if RTC_ENABLE_WEBSOCKET
 
-#include <list>
-#include <map>
 #include <vector>
 
 namespace rtc::impl {
@@ -52,8 +50,6 @@ public:
 private:
 	static string generateKey();
 	static string computeAcceptKey(const string &key);
-	static size_t parseHttpLines(const byte *buffer, size_t size, std::list<string> &lines);
-	static std::multimap<string, string> parseHttpHeaders(const std::list<string> &lines);
 
 	string mHost;
 	string mPath;

+ 3 - 2
src/impl/wstransport.cpp

@@ -7,6 +7,7 @@
  */
 
 #include "wstransport.hpp"
+#include "httpproxytransport.hpp"
 #include "tcptransport.hpp"
 #include "threadpool.hpp"
 #include "tlstransport.hpp"
@@ -42,14 +43,14 @@ using std::to_integer;
 using std::to_string;
 using std::chrono::system_clock;
 
-WsTransport::WsTransport(variant<shared_ptr<TcpTransport>, shared_ptr<TlsTransport>> lower,
+WsTransport::WsTransport(variant<shared_ptr<TcpTransport>, shared_ptr<HttpProxyTransport>, shared_ptr<TlsTransport>> lower,
                          shared_ptr<WsHandshake> handshake, int maxOutstandingPings,
                          message_callback recvCallback, state_callback stateCallback)
     : Transport(std::visit([](auto l) { return std::static_pointer_cast<Transport>(l); }, lower),
                 std::move(stateCallback)),
       mHandshake(std::move(handshake)),
       mIsClient(
-          std::visit(rtc::overloaded{[](shared_ptr<TcpTransport> l) { return l->isActive(); },
+          std::visit(rtc::overloaded{[](auto l) { return l->isActive(); },
                                      [](shared_ptr<TlsTransport> l) { return l->isClient(); }},
                      lower)),
       mMaxOutstandingPings(maxOutstandingPings) {

+ 2 - 1
src/impl/wstransport.hpp

@@ -19,12 +19,13 @@
 
 namespace rtc::impl {
 
+class HttpProxyTransport;
 class TcpTransport;
 class TlsTransport;
 
 class WsTransport final : public Transport, public std::enable_shared_from_this<WsTransport> {
 public:
-	WsTransport(variant<shared_ptr<TcpTransport>, shared_ptr<TlsTransport>> lower,
+	WsTransport(variant<shared_ptr<TcpTransport>, shared_ptr<HttpProxyTransport>, shared_ptr<TlsTransport>> lower,
 	            shared_ptr<WsHandshake> handshake, int maxOutstandingPings,
 	            message_callback recvCallback, state_callback stateCallback);
 	~WsTransport();