mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-03-10 20:31:47 +00:00
Merge pull request #12793 from Chocobo1/prebuilt_headers
Prebuild HTTP headers
This commit is contained in:
commit
e015710ffd
@ -57,7 +57,7 @@ namespace
|
|||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool parseHeaderLine(const QString &line, QStringMap &out)
|
bool parseHeaderLine(const QString &line, HeaderMap &out)
|
||||||
{
|
{
|
||||||
// [rfc7230] 3.2. Header Fields
|
// [rfc7230] 3.2. Header Fields
|
||||||
const int i = line.indexOf(':');
|
const int i = line.indexOf(':');
|
||||||
@ -287,7 +287,7 @@ bool RequestParser::parseFormData(const QByteArray &data)
|
|||||||
const QString headers = QString::fromLatin1(list[0]);
|
const QString headers = QString::fromLatin1(list[0]);
|
||||||
const QByteArray payload = viewWithoutEndingWith(list[1], CRLF);
|
const QByteArray payload = viewWithoutEndingWith(list[1], CRLF);
|
||||||
|
|
||||||
QStringMap headersMap;
|
HeaderMap headersMap;
|
||||||
const QVector<QStringRef> headerLines = headers.splitRef(CRLF, QString::SkipEmptyParts);
|
const QVector<QStringRef> headerLines = headers.splitRef(CRLF, QString::SkipEmptyParts);
|
||||||
for (const auto &line : headerLines) {
|
for (const auto &line : headerLines) {
|
||||||
if (line.trimmed().startsWith(HEADER_CONTENT_DISPOSITION, Qt::CaseInsensitive)) {
|
if (line.trimmed().startsWith(HEADER_CONTENT_DISPOSITION, Qt::CaseInsensitive)) {
|
||||||
|
@ -35,9 +35,9 @@ void ResponseBuilder::status(const uint code, const QString &text)
|
|||||||
m_response.status = {code, text};
|
m_response.status = {code, text};
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResponseBuilder::header(const QString &name, const QString &value)
|
void ResponseBuilder::setHeader(const Header &header)
|
||||||
{
|
{
|
||||||
m_response.headers[name] = value;
|
m_response.headers[header.name] = header.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResponseBuilder::print(const QString &text, const QString &type)
|
void ResponseBuilder::print(const QString &text, const QString &type)
|
||||||
|
@ -37,7 +37,7 @@ namespace Http
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void status(uint code = 200, const QString &text = QLatin1String("OK"));
|
void status(uint code = 200, const QString &text = QLatin1String("OK"));
|
||||||
void header(const QString &name, const QString &value);
|
void setHeader(const Header &header);
|
||||||
void print(const QString &text, const QString &type = CONTENT_TYPE_HTML);
|
void print(const QString &text, const QString &type = CONTENT_TYPE_HTML);
|
||||||
void print(const QByteArray &data, const QString &type = CONTENT_TYPE_HTML);
|
void print(const QByteArray &data, const QString &type = CONTENT_TYPE_HTML);
|
||||||
void clear();
|
void clear();
|
||||||
|
@ -34,8 +34,6 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
#include "base/types.h"
|
|
||||||
|
|
||||||
namespace Http
|
namespace Http
|
||||||
{
|
{
|
||||||
const char METHOD_GET[] = "GET";
|
const char METHOD_GET[] = "GET";
|
||||||
@ -92,12 +90,20 @@ namespace Http
|
|||||||
QByteArray data;
|
QByteArray data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Header
|
||||||
|
{
|
||||||
|
QString name;
|
||||||
|
QString value;
|
||||||
|
};
|
||||||
|
|
||||||
|
using HeaderMap = QMap<QString, QString>; // <Header name, Header value>
|
||||||
|
|
||||||
struct Request
|
struct Request
|
||||||
{
|
{
|
||||||
QString version;
|
QString version;
|
||||||
QString method;
|
QString method;
|
||||||
QString path;
|
QString path;
|
||||||
QStringMap headers;
|
HeaderMap headers;
|
||||||
QHash<QString, QByteArray> query;
|
QHash<QString, QByteArray> query;
|
||||||
QHash<QString, QString> posts;
|
QHash<QString, QString> posts;
|
||||||
QVector<UploadedFile> files;
|
QVector<UploadedFile> files;
|
||||||
@ -112,7 +118,7 @@ namespace Http
|
|||||||
struct Response
|
struct Response
|
||||||
{
|
{
|
||||||
ResponseStatus status;
|
ResponseStatus status;
|
||||||
QStringMap headers;
|
HeaderMap headers;
|
||||||
QByteArray content;
|
QByteArray content;
|
||||||
|
|
||||||
Response(uint code = 200, const QString &text = "OK")
|
Response(uint code = 200, const QString &text = "OK")
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
#include "base/http/httperror.h"
|
#include "base/http/httperror.h"
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
#include "base/preferences.h"
|
#include "base/preferences.h"
|
||||||
|
#include "base/types.h"
|
||||||
#include "base/utils/bytearray.h"
|
#include "base/utils/bytearray.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
#include "base/utils/misc.h"
|
#include "base/utils/misc.h"
|
||||||
@ -335,25 +336,34 @@ void WebApplication::configure()
|
|||||||
m_domainList = pref->getServerDomains().split(';', QString::SkipEmptyParts);
|
m_domainList = pref->getServerDomains().split(';', QString::SkipEmptyParts);
|
||||||
std::for_each(m_domainList.begin(), m_domainList.end(), [](QString &entry) { entry = entry.trimmed(); });
|
std::for_each(m_domainList.begin(), m_domainList.end(), [](QString &entry) { entry = entry.trimmed(); });
|
||||||
|
|
||||||
m_isClickjackingProtectionEnabled = pref->isWebUiClickjackingProtectionEnabled();
|
|
||||||
m_isCSRFProtectionEnabled = pref->isWebUiCSRFProtectionEnabled();
|
m_isCSRFProtectionEnabled = pref->isWebUiCSRFProtectionEnabled();
|
||||||
m_isSecureCookieEnabled = pref->isWebUiSecureCookieEnabled();
|
m_isSecureCookieEnabled = pref->isWebUiSecureCookieEnabled();
|
||||||
m_isHostHeaderValidationEnabled = pref->isWebUIHostHeaderValidationEnabled();
|
m_isHostHeaderValidationEnabled = pref->isWebUIHostHeaderValidationEnabled();
|
||||||
m_isHttpsEnabled = pref->isWebUiHttpsEnabled();
|
m_isHttpsEnabled = pref->isWebUiHttpsEnabled();
|
||||||
|
|
||||||
m_contentSecurityPolicy =
|
m_prebuiltHeaders.clear();
|
||||||
|
m_prebuiltHeaders.push_back({QLatin1String(Http::HEADER_X_XSS_PROTECTION), QLatin1String("1; mode=block")});
|
||||||
|
m_prebuiltHeaders.push_back({QLatin1String(Http::HEADER_X_CONTENT_TYPE_OPTIONS), QLatin1String("nosniff")});
|
||||||
|
|
||||||
|
if (!m_isAltUIUsed)
|
||||||
|
m_prebuiltHeaders.push_back({QLatin1String(Http::HEADER_REFERRER_POLICY), QLatin1String("same-origin")});
|
||||||
|
|
||||||
|
const bool isClickjackingProtectionEnabled = pref->isWebUiClickjackingProtectionEnabled();
|
||||||
|
if (isClickjackingProtectionEnabled)
|
||||||
|
m_prebuiltHeaders.push_back({QLatin1String(Http::HEADER_X_FRAME_OPTIONS), QLatin1String("SAMEORIGIN")});
|
||||||
|
|
||||||
|
const QString contentSecurityPolicy =
|
||||||
(m_isAltUIUsed
|
(m_isAltUIUsed
|
||||||
? QLatin1String("")
|
? QLatin1String("")
|
||||||
: QLatin1String("default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; script-src 'self' 'unsafe-inline'; object-src 'none'; form-action 'self';"))
|
: QLatin1String("default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; script-src 'self' 'unsafe-inline'; object-src 'none'; form-action 'self';"))
|
||||||
+ (m_isClickjackingProtectionEnabled ? QLatin1String(" frame-ancestors 'self';") : QLatin1String(""))
|
+ (isClickjackingProtectionEnabled ? QLatin1String(" frame-ancestors 'self';") : QLatin1String(""))
|
||||||
+ (m_isHttpsEnabled ? QLatin1String(" upgrade-insecure-requests;") : QLatin1String(""));
|
+ (m_isHttpsEnabled ? QLatin1String(" upgrade-insecure-requests;") : QLatin1String(""));
|
||||||
|
if (!contentSecurityPolicy.isEmpty())
|
||||||
|
m_prebuiltHeaders.push_back({QLatin1String(Http::HEADER_CONTENT_SECURITY_POLICY), contentSecurityPolicy});
|
||||||
|
|
||||||
m_useCustomHTTPHeaders = pref->isWebUICustomHTTPHeadersEnabled();
|
if (pref->isWebUICustomHTTPHeadersEnabled()) {
|
||||||
m_customHTTPHeaders.clear();
|
|
||||||
if (m_useCustomHTTPHeaders) {
|
|
||||||
const QString customHeaders = pref->getWebUICustomHTTPHeaders().trimmed();
|
const QString customHeaders = pref->getWebUICustomHTTPHeaders().trimmed();
|
||||||
const QVector<QStringRef> customHeaderLines = customHeaders.splitRef('\n', QString::SkipEmptyParts);
|
const QVector<QStringRef> customHeaderLines = customHeaders.splitRef('\n', QString::SkipEmptyParts);
|
||||||
m_customHTTPHeaders.reserve(customHeaderLines.size());
|
|
||||||
|
|
||||||
for (const QStringRef &line : customHeaderLines) {
|
for (const QStringRef &line : customHeaderLines) {
|
||||||
const int idx = line.indexOf(':');
|
const int idx = line.indexOf(':');
|
||||||
@ -365,7 +375,7 @@ void WebApplication::configure()
|
|||||||
|
|
||||||
const QString header = line.left(idx).trimmed().toString();
|
const QString header = line.left(idx).trimmed().toString();
|
||||||
const QString value = line.mid(idx + 1).trimmed().toString();
|
const QString value = line.mid(idx + 1).trimmed().toString();
|
||||||
m_customHTTPHeaders.push_back({header, value});
|
m_prebuiltHeaders.push_back({header, value});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -391,7 +401,7 @@ void WebApplication::sendFile(const QString &path)
|
|||||||
const auto it = m_translatedFiles.constFind(path);
|
const auto it = m_translatedFiles.constFind(path);
|
||||||
if ((it != m_translatedFiles.constEnd()) && (lastModified <= it->lastModified)) {
|
if ((it != m_translatedFiles.constEnd()) && (lastModified <= it->lastModified)) {
|
||||||
print(it->data, it->mimeType);
|
print(it->data, it->mimeType);
|
||||||
header(Http::HEADER_CACHE_CONTROL, getCachingInterval(it->mimeType));
|
setHeader({Http::HEADER_CACHE_CONTROL, getCachingInterval(it->mimeType)});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -423,7 +433,7 @@ void WebApplication::sendFile(const QString &path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
print(data, mimeType.name());
|
print(data, mimeType.name());
|
||||||
header(Http::HEADER_CACHE_CONTROL, getCachingInterval(mimeType.name()));
|
setHeader({Http::HEADER_CACHE_CONTROL, getCachingInterval(mimeType.name())});
|
||||||
}
|
}
|
||||||
|
|
||||||
Http::Response WebApplication::processRequest(const Http::Request &request, const Http::Environment &env)
|
Http::Response WebApplication::processRequest(const Http::Request &request, const Http::Environment &env)
|
||||||
@ -460,22 +470,8 @@ Http::Response WebApplication::processRequest(const Http::Request &request, cons
|
|||||||
print(error.message(), Http::CONTENT_TYPE_TXT);
|
print(error.message(), Http::CONTENT_TYPE_TXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
header(QLatin1String(Http::HEADER_X_XSS_PROTECTION), QLatin1String("1; mode=block"));
|
for (const Http::Header &prebuiltHeader : asConst(m_prebuiltHeaders))
|
||||||
header(QLatin1String(Http::HEADER_X_CONTENT_TYPE_OPTIONS), QLatin1String("nosniff"));
|
setHeader(prebuiltHeader);
|
||||||
|
|
||||||
if (m_isClickjackingProtectionEnabled)
|
|
||||||
header(QLatin1String(Http::HEADER_X_FRAME_OPTIONS), QLatin1String("SAMEORIGIN"));
|
|
||||||
|
|
||||||
if (!m_isAltUIUsed)
|
|
||||||
header(QLatin1String(Http::HEADER_REFERRER_POLICY), QLatin1String("same-origin"));
|
|
||||||
|
|
||||||
if (!m_contentSecurityPolicy.isEmpty())
|
|
||||||
header(QLatin1String(Http::HEADER_CONTENT_SECURITY_POLICY), m_contentSecurityPolicy);
|
|
||||||
|
|
||||||
if (m_useCustomHTTPHeaders) {
|
|
||||||
for (const CustomHTTPHeader &customHeader : asConst(m_customHTTPHeaders))
|
|
||||||
header(customHeader.name, customHeader.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response();
|
return response();
|
||||||
}
|
}
|
||||||
@ -567,7 +563,7 @@ void WebApplication::sessionStart()
|
|||||||
QByteArray cookieRawForm = cookie.toRawForm();
|
QByteArray cookieRawForm = cookie.toRawForm();
|
||||||
if (m_isCSRFProtectionEnabled)
|
if (m_isCSRFProtectionEnabled)
|
||||||
cookieRawForm.append("; SameSite=Strict");
|
cookieRawForm.append("; SameSite=Strict");
|
||||||
header(Http::HEADER_SET_COOKIE, cookieRawForm);
|
setHeader({Http::HEADER_SET_COOKIE, cookieRawForm});
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebApplication::sessionEnd()
|
void WebApplication::sessionEnd()
|
||||||
@ -581,7 +577,7 @@ void WebApplication::sessionEnd()
|
|||||||
delete m_sessions.take(m_currentSession->id());
|
delete m_sessions.take(m_currentSession->id());
|
||||||
m_currentSession = nullptr;
|
m_currentSession = nullptr;
|
||||||
|
|
||||||
header(Http::HEADER_SET_COOKIE, cookie.toRawForm());
|
setHeader({Http::HEADER_SET_COOKIE, cookie.toRawForm()});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebApplication::isCrossSiteRequest(const Http::Request &request) const
|
bool WebApplication::isCrossSiteRequest(const Http::Request &request) const
|
||||||
|
@ -151,19 +151,10 @@ private:
|
|||||||
|
|
||||||
// security related
|
// security related
|
||||||
QStringList m_domainList;
|
QStringList m_domainList;
|
||||||
bool m_isClickjackingProtectionEnabled;
|
|
||||||
bool m_isCSRFProtectionEnabled;
|
bool m_isCSRFProtectionEnabled;
|
||||||
bool m_isSecureCookieEnabled;
|
bool m_isSecureCookieEnabled;
|
||||||
bool m_isHostHeaderValidationEnabled;
|
bool m_isHostHeaderValidationEnabled;
|
||||||
bool m_isHttpsEnabled;
|
bool m_isHttpsEnabled;
|
||||||
QString m_contentSecurityPolicy;
|
|
||||||
|
|
||||||
// Custom HTTP headers
|
QVector<Http::Header> m_prebuiltHeaders;
|
||||||
struct CustomHTTPHeader
|
|
||||||
{
|
|
||||||
QString name;
|
|
||||||
QString value;
|
|
||||||
};
|
|
||||||
bool m_useCustomHTTPHeaders;
|
|
||||||
QVector<CustomHTTPHeader> m_customHTTPHeaders;
|
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user