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;
49 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Response has no name");
55 AddHeader(
"Content-Disposition", QString(
"inline; filename=\"%2\"").arg(qPrintable(
filename)));
63 QString mime = data ? (*data)->m_mimeType.Name() : (*file)->m_mimeType.Name();
64 if (mime.startsWith(
"video/") || mime.startsWith(
"audio/"))
70 if (mode ==
"Streaming" || mode ==
"Background" || mode ==
"Interactive")
85 "DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000");
94 AddHeader(
"X-Content-Type-Options",
"nosniff");
114 QString policy =
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://services.mythtv.org; "
115 "style-src 'self' 'unsafe-inline'; "
117 "object-src 'none'; "
121 "form-action 'self'; "
122 "frame-ancestors 'self'; ";
125 AddHeader(
"Content-Security-Policy", policy);
127 AddHeader(
"X-Content-Security-Policy", policy);
128 AddHeader(
"X-XSS-Protection",
"1; mode=block");
135 if (!origin.isEmpty())
143 if (
auto index = origin.lastIndexOf(
"://"); index > -1)
145 auto scheme = origin.mid(0, index);
146 if (scheme ==
"http" || scheme ==
"https")
148 auto host = origin.mid(index + 3);
149 allow = Config.
m_hosts.contains(host);
155 AddHeader(
"Access-Control-Allow-Origin" , origin);
156 AddHeader(
"Access-Control-Allow-Credentials" ,
"true");
157 AddHeader(
"Access-Control-Allow-Headers" ,
"Content-Type, Accept, Range");
159 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Allowing CORS for origin: '%1'").arg(origin));
163 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Disallowing CORS for origin: '%1'").arg(origin));
189 LOG(VB_GENERAL, LOG_WARNING,
LOC + QString(
"'%1' is not allowed for '%2' (Allowed: %3)")
205 auto response = std::make_shared<MythHTTPResponse>();
206 response->m_serverName = ServerName;
207 response->m_status = Status;
213 response->AddDefaultHeaders();
216 response->AddContentHeaders();
223 auto response = std::make_shared<MythHTTPResponse>(
Request);
224 response->AddDefaultHeaders();
225 response->AddHeader(
"Location", Redirect);
226 response->AddContentHeaders();
232 auto response = std::make_shared<MythHTTPResponse>(
Request);
235 response->AddDefaultHeaders();
236 response->AddContentHeaders();
242 auto response = std::make_shared<MythHTTPResponse>(
Request);
243 response->AddDefaultHeaders();
245 response->AddContentHeaders();
251 auto response = std::make_shared<MythHTTPResponse>(
Request);
252 response->m_response = Data;
254 response->AddDefaultHeaders();
255 response->AddContentHeaders();
262 auto response = std::make_shared<MythHTTPResponse>(
Request);
263 response->m_response = File;
265 response->AddDefaultHeaders();
266 response->AddContentHeaders();
273 auto response = std::make_shared<MythHTTPResponse>(
Request);
274 response->AddDefaultHeaders();
275 response->AddContentHeaders();
283 if (!range.isEmpty())
305 auto * data = std::get_if<HTTPData>(&
m_response);
307 int64_t size = data ? (*data)->size() :
file ? (*file)->size() : 0;
317 auto & mime = data ? (*data)->m_mimeType : (*file)->m_mimeType;
322 HTTPRanges& ranges = data ? (*data)->m_ranges : (*file)->m_ranges;
323 bool rangerequest = !ranges.empty();
324 bool multipart = ranges.size() > 1;
339 AddHeader(
"Content-Range", QString(
"bytes */%1").arg(size));
351 AddHeader(
"Transfer-Encoding",
"chunked");
361 size = data ? (*data)->m_partialSize : (*file)->m_partialSize;
366 size += data ? (*data)->m_multipartHeaderSize : (*file)->m_multipartHeaderSize;
375 auto response = std::make_shared<MythHTTPResponse>(
Request);
376 response->AddDefaultHeaders();
377 response->AddContentHeaders();
395 LOG(VB_HTTP, LOG_ERR,
LOC +
"Must be GET and HTTP/1.1");
408 LOG(VB_HTTP, LOG_ERR,
LOC +
"Invalid Request-URI");
426 if (header.isEmpty() || !header.contains(
"websocket", Qt::CaseInsensitive))
428 LOG(VB_HTTP, LOG_ERR,
LOC +
"Invalid/missing 'Upgrade' header");
438 if (header.isEmpty() || !header.contains(
"upgrade", Qt::CaseInsensitive))
440 LOG(VB_HTTP, LOG_ERR,
LOC +
"Invalid/missing 'Connection' header");
459 LOG(VB_HTTP, LOG_ERR,
LOC +
"No Sec-WebSocket-Key header");
462 auto nonce = QByteArray::fromBase64(key.toLatin1());
463 if (nonce.length() != 16)
465 LOG(VB_HTTP, LOG_ERR,
LOC + QString(
"Invalid Sec-WebSocket-Key header (length: %1)").arg(nonce.length()));
495 LOG(VB_HTTP, LOG_ERR,
LOC + QString(
"Unsupported websocket version %1").arg(header));
496 response->AddHeader(QStringLiteral(
"Sec-WebSocket-Version"), QStringLiteral(
"13"));
517 static const auto magic = QStringLiteral(
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
518 QString hash = QCryptographicHash::hash((key + magic).toUtf8(), QCryptographicHash::Sha1).toBase64();
522 response = std::make_shared<MythHTTPResponse>(
Request);
523 response->AddDefaultHeaders();
524 response->AddContentHeaders();
525 response->AddHeader(QStringLiteral(
"Connection"), QStringLiteral(
"Upgrade"));
526 response->AddHeader(QStringLiteral(
"Upgrade"), QStringLiteral(
"websocket"));
527 response->AddHeader(QStringLiteral(
"Sec-WebSocket-Accept"), hash);
531 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"Successful WebSocket upgrade (protocol: %1)")
537 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Autobahn test suite detected. Will echooooo...");