2#include <QCryptographicHash>
3#include <QMimeDatabase>
16#define LOC QString("HTTPResp: ")
21 : m_serverName(
Request->m_serverName),
23 m_connection(
Request->m_connection),
28 m_requestHeaders(
Request->m_headers)
37 auto * data = std::get_if<HTTPData>(&
m_response);
46 QString
filename = data ? (*data)->m_fileName : (*file)->m_fileName;
48 if (!download.isEmpty())
50 int lastDot =
filename.lastIndexOf(
'.');
54 download = download + extension;
60 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Response has no name");
66 AddHeader(
"Content-Disposition", QString(
"inline; filename=\"%2\"").arg(qPrintable(
filename)));
74 QString mime = data ? (*data)->m_mimeType.Name() : (*file)->m_mimeType.Name();
75 if (mime.startsWith(
"video/") || mime.startsWith(
"audio/"))
81 if (mode ==
"Streaming" || mode ==
"Background" || mode ==
"Interactive")
96 "DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000");
105 AddHeader(
"X-Content-Type-Options",
"nosniff");
125 QString policy =
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://services.mythtv.org; "
126 "style-src 'self' 'unsafe-inline'; "
128 "object-src 'none'; "
133 "img-src http: https: data:; "
134 "form-action 'self'; "
135 "frame-ancestors 'self'; ";
138 AddHeader(
"Content-Security-Policy", policy);
140 AddHeader(
"X-Content-Security-Policy", policy);
141 AddHeader(
"X-XSS-Protection",
"1; mode=block");
148 if (!origin.isEmpty())
156 if (
auto index = origin.lastIndexOf(
"://"); index > -1)
158 auto scheme = origin.mid(0, index);
159 if (scheme ==
"http" || scheme ==
"https")
161 auto host = origin.mid(index + 3);
162 allow = Config.
m_hosts.contains(host);
168 AddHeader(
"Access-Control-Allow-Origin" , origin);
169 AddHeader(
"Access-Control-Allow-Credentials" ,
"true");
170 AddHeader(
"Access-Control-Allow-Headers" ,
"Content-Type, Accept, Range");
172 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Allowing CORS for origin: '%1'").arg(origin));
176 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Disallowing CORS for origin: '%1'").arg(origin));
202 LOG(VB_GENERAL, LOG_WARNING,
LOC + QString(
"'%1' is not allowed for '%2' (Allowed: %3)")
218 auto response = std::make_shared<MythHTTPResponse>();
219 response->m_serverName = ServerName;
220 response->m_status = Status;
226 response->AddDefaultHeaders();
229 response->AddContentHeaders();
236 auto response = std::make_shared<MythHTTPResponse>(
Request);
237 response->AddDefaultHeaders();
238 response->AddHeader(
"Location", Redirect);
239 response->AddContentHeaders();
245 auto response = std::make_shared<MythHTTPResponse>(
Request);
248 response->AddDefaultHeaders();
249 response->AddContentHeaders();
255 auto response = std::make_shared<MythHTTPResponse>(
Request);
256 response->AddDefaultHeaders();
258 response->AddContentHeaders();
264 auto response = std::make_shared<MythHTTPResponse>(
Request);
265 response->m_response = Data;
267 response->AddDefaultHeaders();
268 response->AddContentHeaders();
275 auto response = std::make_shared<MythHTTPResponse>(
Request);
276 response->m_response = File;
278 response->AddDefaultHeaders();
279 response->AddContentHeaders();
286 auto response = std::make_shared<MythHTTPResponse>(
Request);
287 response->AddDefaultHeaders();
288 response->AddContentHeaders();
296 if (!range.isEmpty())
318 auto * data = std::get_if<HTTPData>(&
m_response);
323 size = (*data)->size();
325 size = (*file)->size();
335 auto & mime = data ? (*data)->m_mimeType : (*file)->m_mimeType;
340 HTTPRanges& ranges = data ? (*data)->m_ranges : (*file)->m_ranges;
341 bool rangerequest = !ranges.empty();
342 bool multipart = ranges.size() > 1;
357 AddHeader(
"Content-Range", QString(
"bytes */%1").arg(size));
369 AddHeader(
"Transfer-Encoding",
"chunked");
379 size = data ? (*data)->m_partialSize : (*file)->m_partialSize;
384 size += data ? (*data)->m_multipartHeaderSize : (*file)->m_multipartHeaderSize;
393 auto response = std::make_shared<MythHTTPResponse>(
Request);
394 response->AddDefaultHeaders();
395 response->AddContentHeaders();
413 LOG(VB_HTTP, LOG_ERR,
LOC +
"Must be GET and HTTP/1.1");
426 LOG(VB_HTTP, LOG_ERR,
LOC +
"Invalid Request-URI");
444 if (header.isEmpty() || !header.contains(
"websocket", Qt::CaseInsensitive))
446 LOG(VB_HTTP, LOG_ERR,
LOC +
"Invalid/missing 'Upgrade' header");
456 if (header.isEmpty() || !header.contains(
"upgrade", Qt::CaseInsensitive))
458 LOG(VB_HTTP, LOG_ERR,
LOC +
"Invalid/missing 'Connection' header");
477 LOG(VB_HTTP, LOG_ERR,
LOC +
"No Sec-WebSocket-Key header");
480 auto nonce = QByteArray::fromBase64(key.toLatin1());
481 if (nonce.length() != 16)
483 LOG(VB_HTTP, LOG_ERR,
LOC + QString(
"Invalid Sec-WebSocket-Key header (length: %1)").arg(nonce.length()));
513 LOG(VB_HTTP, LOG_ERR,
LOC + QString(
"Unsupported websocket version %1").arg(header));
514 response->AddHeader(QStringLiteral(
"Sec-WebSocket-Version"), QStringLiteral(
"13"));
535 static const auto magic = QStringLiteral(
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
536 QString hash = QCryptographicHash::hash((key + magic).toUtf8(), QCryptographicHash::Sha1).toBase64();
540 response = std::make_shared<MythHTTPResponse>(
Request);
541 response->AddDefaultHeaders();
542 response->AddContentHeaders();
543 response->AddHeader(QStringLiteral(
"Connection"), QStringLiteral(
"Upgrade"));
544 response->AddHeader(QStringLiteral(
"Upgrade"), QStringLiteral(
"websocket"));
545 response->AddHeader(QStringLiteral(
"Sec-WebSocket-Accept"), hash);
549 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Successful WebSocket upgrade (protocol: %1)")
555 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Autobahn test suite detected. Will echooooo...");
static void PreConditionHeaders(const HTTPResponse &Response)
Add precondition (cache) headers to the response.
static void PreConditionCheck(const HTTPResponse &Response)
Process precondition checks.
QStringList m_allowedOrigins
static MythHTTPEncode Compress(MythHTTPResponse *Response, int64_t &Size)
Compress the response content under certain circumstances or mark the content as 'chunkable'.
static MythMimeType GetMimeType(HTTPVariant Content)
Return a QMimeType that represents Content.
static QString GetRangeHeader(HTTPRanges &Ranges, int64_t Size)
static void BuildMultipartHeaders(MythHTTPResponse *Response)
static void HandleRangeRequest(MythHTTPResponse *Response, const QString &Request)
static HTTPResponse OptionsResponse(const HTTPRequest2 &Request)
static HTTPResponse RedirectionResponse(const HTTPRequest2 &Request, const QString &Redirect)
MythHTTPVersion m_version
MythHTTPResponse()=default
static HTTPResponse FileResponse(const HTTPRequest2 &Request, const HTTPFile &File)
HTTPHeaders m_requestHeaders
static HTTPResponse UpgradeResponse(const HTTPRequest2 &Request, MythSocketProtocol &Protocol, bool &Testing)
static HTTPResponse HandleOptions(const HTTPRequest2 &Request)
std::chrono::milliseconds m_timeout
MythHTTPRequestType m_requestType
static HTTPResponse ErrorResponse(MythHTTPStatus Status, const QString &ServerName)
HTTPContents m_responseHeaders
static HTTPResponse EmptyResponse(const HTTPRequest2 &Request)
MythHTTPConnection m_connection
std::enable_if_t< std::is_convertible_v< T, QString >, void > AddHeader(const QString &key, const T &val)
void Finalise(const MythHTTPConfig &Config)
Complete all necessary headers, add final line break after headers, remove data etc.
static HTTPResponse DataResponse(const HTTPRequest2 &Request, const HTTPData &Data)
static QString ProtocolToString(MythSocketProtocol Protocol)
static MythSocketProtocol ProtocolFromString(const QString &Protocols)
static QString GetContentType(const MythMimeType &Mime)
static QString AllowedRequestsToString(int Allowed)
static QString RequestToString(MythHTTPRequestType Type)
static QString GetHeader(const HTTPHeaders &Headers, const QString &Value, const QString &Default="")
static QString VersionToString(MythHTTPVersion Version)
static QString StatusToString(MythHTTPStatus Status)
std::vector< HTTPRange > HTTPRanges
@ HTTPRequestedRangeNotSatisfiable
@ HTTPConnectionKeepAlive
std::shared_ptr< MythHTTPFile > HTTPFile
std::shared_ptr< MythHTTPRequest > HTTPRequest2
std::shared_ptr< MythHTTPResponse > HTTPResponse
std::shared_ptr< MythHTTPData > HTTPData
static QString s_defaultHTTPPage
#define HTTP_SOCKET_TIMEOUT_MS
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
@ kRFC822
HTTP Date format.
QDateTime current(bool stripped)
Returns current Date and Time in UTC.