fix: keep mpv relay routes isolated

This commit is contained in:
dela
2026-06-22 13:48:35 +08:00
parent 5717181e37
commit 9a5e04d281
2 changed files with 64 additions and 9 deletions
+52 -8
View File
@@ -9,6 +9,7 @@
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QPointer>
#include <QStringList>
#include <QTcpServer>
#include <QTcpSocket>
#include <QTimer>
@@ -55,7 +56,6 @@ QUrl MpvHttpStreamRelay::prepare(const QUrl &targetUrl, const QString &serverId,
return {};
}
stop();
if (!m_server->isListening() && !m_server->listen(QHostAddress::LocalHost, 0))
{
qWarning() << "[MpvHttpStreamRelay] failed to listen"
@@ -66,9 +66,12 @@ QUrl MpvHttpStreamRelay::prepare(const QUrl &targetUrl, const QString &serverId,
m_targetUrl = targetUrl;
m_serverId = serverId;
m_streamToken = QUuid::createUuid().toString(QUuid::WithoutBraces);
m_network->setProxy(proxy);
m_routes.insert(m_streamToken, RouteState{targetUrl, serverId, proxy});
m_bytesRelayedSinceLastTick = 0;
if (!m_speedTimer->isActive())
{
m_speedTimer->start();
}
QUrl localUrl;
localUrl.setScheme(QStringLiteral("http"));
@@ -102,6 +105,7 @@ void MpvHttpStreamRelay::stop()
Q_EMIT upstreamSpeedChanged(0);
}
m_connections.clear();
m_routes.clear();
m_targetUrl.clear();
m_serverId.clear();
m_streamToken.clear();
@@ -176,7 +180,7 @@ void MpvHttpStreamRelay::onSocketDisconnected(QTcpSocket *socket)
void MpvHttpStreamRelay::processRequest(QTcpSocket *socket, const QByteArray &requestData)
{
if (m_targetUrl.isEmpty() || m_streamToken.isEmpty())
if (m_routes.isEmpty())
{
writeError(socket, 503, "Relay target is not ready");
return;
@@ -198,14 +202,24 @@ void MpvHttpStreamRelay::processRequest(QTcpSocket *socket, const QByteArray &re
const QByteArray method = requestParts.at(0).toUpper();
const QByteArray path = requestParts.at(1);
const QByteArray expectedPrefix = QByteArray("/") + m_streamToken.toUtf8() + QByteArray("/");
if (!(method == "GET" || method == "HEAD") || !path.startsWith(expectedPrefix))
QByteArray requestPath = path;
const int queryStart = requestPath.indexOf('?');
if (queryStart >= 0)
{
requestPath.truncate(queryStart);
}
const QList<QByteArray> pathParts = requestPath.split('/');
const QByteArray tokenBytes = pathParts.size() >= 2 ? pathParts.at(1) : QByteArray();
const QString routeToken = QString::fromUtf8(tokenBytes);
const auto routeIt = m_routes.constFind(routeToken);
if (!(method == "GET" || method == "HEAD") || routeToken.isEmpty() || routeIt == m_routes.constEnd())
{
writeError(socket, 404, "Not found");
return;
}
QNetworkRequest request(m_targetUrl);
const RouteState route = routeIt.value();
QNetworkRequest request(route.targetUrl);
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
for (int i = 1; i < lines.size(); ++i)
@@ -239,12 +253,16 @@ void MpvHttpStreamRelay::processRequest(QTcpSocket *socket, const QByteArray &re
{
return;
}
it->routeToken = routeToken;
it->targetUrl = route.targetUrl;
it->serverId = route.serverId;
it->headOnly = method == "HEAD";
m_network->setProxy(route.proxy);
it->reply = it->headOnly ? m_network->head(request) : m_network->get(request);
it->reply->setReadBufferSize(kReplyReadBufferBytes);
qDebug() << "[MpvHttpStreamRelay] request"
<< "| method:" << method << "| target:" << LogRedactionUtils::url(m_targetUrl)
<< "| method:" << method << "| target:" << LogRedactionUtils::url(route.targetUrl)
<< "| range:" << request.rawHeader("Range");
QNetworkReply *reply = it->reply;
@@ -293,7 +311,7 @@ void MpvHttpStreamRelay::processRequest(QTcpSocket *socket, const QByteArray &re
reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 0)
{
qWarning() << "[MpvHttpStreamRelay] upstream failed"
<< "| target:" << LogRedactionUtils::url(m_targetUrl)
<< "| target:" << LogRedactionUtils::url(it->targetUrl)
<< "| error:" << reply->errorString();
}
@@ -475,6 +493,7 @@ void MpvHttpStreamRelay::closeConnection(QTcpSocket *socket)
socket->disconnectFromHost();
}
socket->deleteLater();
cleanupUnusedRoutes();
}
void MpvHttpStreamRelay::recordRelayedBytes(qint64 bytes)
@@ -485,6 +504,31 @@ void MpvHttpStreamRelay::recordRelayedBytes(qint64 bytes)
}
}
void MpvHttpStreamRelay::cleanupUnusedRoutes()
{
QStringList activeTokens;
activeTokens.reserve(m_connections.size());
for (auto it = m_connections.cbegin(); it != m_connections.cend(); ++it)
{
if (!it->routeToken.isEmpty())
{
activeTokens.append(it->routeToken);
}
}
for (auto it = m_routes.begin(); it != m_routes.end();)
{
if (it.key() != m_streamToken && !activeTokens.contains(it.key()))
{
it = m_routes.erase(it);
}
else
{
++it;
}
}
}
QByteArray MpvHttpStreamRelay::reasonPhrase(int statusCode)
{
switch (statusCode)
@@ -29,11 +29,20 @@ private:
struct ConnectionState {
QByteArray buffer;
QNetworkReply *reply = nullptr;
QString routeToken;
QUrl targetUrl;
QString serverId;
bool headersSent = false;
bool headOnly = false;
bool upstreamFinished = false;
};
struct RouteState {
QUrl targetUrl;
QString serverId;
QNetworkProxy proxy;
};
void onNewConnection();
void onSocketReadyRead(QTcpSocket *socket);
void onSocketDisconnected(QTcpSocket *socket);
@@ -43,6 +52,7 @@ private:
void writeError(QTcpSocket *socket, int statusCode, const QByteArray &message);
void closeConnection(QTcpSocket *socket);
void recordRelayedBytes(qint64 bytes);
void cleanupUnusedRoutes();
static QByteArray reasonPhrase(int statusCode);
static bool isHopByHopHeader(QByteArray name);
@@ -51,6 +61,7 @@ private:
QNetworkAccessManager *m_network = nullptr;
QTimer *m_speedTimer = nullptr;
QHash<QTcpSocket *, ConnectionState> m_connections;
QHash<QString, RouteState> m_routes;
QUrl m_targetUrl;
QString m_serverId;
QString m_streamToken;