3#include <QCryptographicHash>
4#include <QMimeDatabase>
17#define LOC QString("HTTPResp: ")
22 : m_serverName(
Request->m_serverName),
24 m_connection(
Request->m_connection),
29 m_requestHeaders(
Request->m_headers)
38 auto * data = std::get_if<HTTPData>(&
m_response);
47 QString
filename = data ? (*data)->m_fileName : (*file)->m_fileName;
49 if (!download.isEmpty())
51 int lastDot =
filename.lastIndexOf(
'.');
55 download = download + extension;
61 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Response has no name");
67 AddHeader(
"Content-Disposition", QString(
"inline; filename=\"%2\"").arg(qPrintable(
filename)));
75 QString mime = data ? (*data)->m_mimeType.Name() : (*file)->m_mimeType.Name();
76 if (mime.startsWith(
"video/") || mime.startsWith(
"audio/"))
82 if (mode ==
"Streaming" || mode ==
"Background" || mode ==
"Interactive")
97 "DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000");
106 AddHeader(
"X-Content-Type-Options",
"nosniff");
126 QString policy =
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://services.mythtv.org; "
127 "style-src 'self' 'unsafe-inline'; "
129 "object-src 'none'; "
134 "img-src http: https: data:; "
135 "form-action 'self'; "
136 "frame-ancestors 'self'; ";
139 AddHeader(
"Content-Security-Policy", policy);
141 AddHeader(
"X-Content-Security-Policy", policy);
142 AddHeader(
"X-XSS-Protection",
"1; mode=block");
149 if (!origin.isEmpty())
157 if (
auto index = origin.lastIndexOf(
"://"); index > -1)
159 auto scheme = origin.mid(0, index);
160 if (scheme ==
"http" || scheme ==
"https")
162 auto host = origin.mid(index + 3);
163 allow = Config.
m_hosts.contains(host);
169 AddHeader(
"Access-Control-Allow-Origin" , origin);
170 AddHeader(
"Access-Control-Allow-Credentials" ,
"true");
171 AddHeader(
"Access-Control-Allow-Headers" ,
"Content-Type, Accept, Range");
173 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Allowing CORS for origin: '%1'").arg(origin));
177 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Disallowing CORS for origin: '%1'").arg(origin));
203 LOG(VB_GENERAL, LOG_WARNING,
LOC + QString(
"'%1' is not allowed for '%2' (Allowed: %3)")
219 auto response = std::make_shared<MythHTTPResponse>();
220 response->m_serverName = ServerName;
221 response->m_status = Status;
227 response->AddDefaultHeaders();
230 response->AddContentHeaders();
237 auto response = std::make_shared<MythHTTPResponse>(
Request);
238 response->AddDefaultHeaders();
239 response->AddHeader(
"Location", Redirect);
240 response->AddContentHeaders();
246 auto response = std::make_shared<MythHTTPResponse>(
Request);
249 response->AddDefaultHeaders();
250 response->AddContentHeaders();
256 auto response = std::make_shared<MythHTTPResponse>(
Request);
257 response->AddDefaultHeaders();
259 response->AddContentHeaders();
265 auto response = std::make_shared<MythHTTPResponse>(
Request);
266 response->m_response = Data;
268 response->AddDefaultHeaders();
269 response->AddContentHeaders();
276 auto response = std::make_shared<MythHTTPResponse>(
Request);
277 response->m_response = File;
279 response->AddDefaultHeaders();
280 response->AddContentHeaders();
287 auto response = std::make_shared<MythHTTPResponse>(
Request);
288 response->AddDefaultHeaders();
289 response->AddContentHeaders();
297 if (!range.isEmpty())
319 auto * data = std::get_if<HTTPData>(&
m_response);
324 size = (*data)->size();
326 size = (*file)->size();
336 auto & mime = data ? (*data)->m_mimeType : (*file)->m_mimeType;
341 HTTPRanges& ranges = data ? (*data)->m_ranges : (*file)->m_ranges;
342 bool rangerequest = !ranges.empty();
343 bool multipart = ranges.size() > 1;
358 AddHeader(
"Content-Range", QString(
"bytes */%1").arg(size));
370 AddHeader(
"Transfer-Encoding",
"chunked");
380 size = data ? (*data)->m_partialSize : (*file)->m_partialSize;
385 size += data ? (*data)->m_multipartHeaderSize : (*file)->m_multipartHeaderSize;
394 auto response = std::make_shared<MythHTTPResponse>(
Request);
395 response->AddDefaultHeaders();
396 response->AddContentHeaders();
414 LOG(VB_HTTP, LOG_ERR,
LOC +
"Must be GET and HTTP/1.1");
427 LOG(VB_HTTP, LOG_ERR,
LOC +
"Invalid Request-URI");
445 if (header.isEmpty() || !header.contains(
"websocket", Qt::CaseInsensitive))
447 LOG(VB_HTTP, LOG_ERR,
LOC +
"Invalid/missing 'Upgrade' header");
457 if (header.isEmpty() || !header.contains(
"upgrade", Qt::CaseInsensitive))
459 LOG(VB_HTTP, LOG_ERR,
LOC +
"Invalid/missing 'Connection' header");
478 LOG(VB_HTTP, LOG_ERR,
LOC +
"No Sec-WebSocket-Key header");
481 auto nonce = QByteArray::fromBase64(key.toLatin1());
482 if (nonce.length() != 16)
484 LOG(VB_HTTP, LOG_ERR,
LOC + QString(
"Invalid Sec-WebSocket-Key header (length: %1)").arg(nonce.length()));
514 LOG(VB_HTTP, LOG_ERR,
LOC + QString(
"Unsupported websocket version %1").arg(header));
515 response->AddHeader(QStringLiteral(
"Sec-WebSocket-Version"), QStringLiteral(
"13"));
536 static const auto magic = QStringLiteral(
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
537 QString hash = QCryptographicHash::hash((key + magic).toUtf8(), QCryptographicHash::Sha1).toBase64();
541 response = std::make_shared<MythHTTPResponse>(
Request);
542 response->AddDefaultHeaders();
543 response->AddContentHeaders();
544 response->AddHeader(QStringLiteral(
"Connection"), QStringLiteral(
"Upgrade"));
545 response->AddHeader(QStringLiteral(
"Upgrade"), QStringLiteral(
"websocket"));
546 response->AddHeader(QStringLiteral(
"Sec-WebSocket-Accept"), hash);
550 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Successful WebSocket upgrade (protocol: %1)")
556 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
void AddHeader(const QString &key, const T &val)
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
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
std::shared_ptr< MythHTTPFile > HTTPFile
std::shared_ptr< MythHTTPRequest > HTTPRequest2
static constexpr int HTTP_SOCKET_TIMEOUT_MS
std::shared_ptr< MythHTTPResponse > HTTPResponse
@ HTTPConnectionKeepAlive
std::shared_ptr< MythHTTPData > HTTPData
static QString s_defaultHTTPPage
@ HTTPRequestedRangeNotSatisfiable
#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.