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...");