14#include <QDomDocument>
15#include <QJsonDocument>
19#define LOC QString("HTTPEnc: ")
36 auto types = Accept.split(
",", Qt::SkipEmptyParts);
38 std::vector<MimePair> weightings;
39 for (
const auto &
type : std::as_const(
types))
41 QString mime =
type.trimmed();
44 if (
auto index =
type.lastIndexOf(
";"); index > -1)
46 mime =
type.mid(0, index).trimmed().toLower();
47 auto qual =
type.mid(index + 1).trimmed();
48 if (
auto index2 = qual.lastIndexOf(
"="); index2 > -1)
51#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
52 auto newquality = qual.midRef(index2 + 1).toFloat(&ok);
54 auto newquality = QStringView(qual).mid(index2 + 1).toFloat(&ok);
60 weightings.emplace_back(quality, mime);
64 auto comp = [](
const MimePair& First,
const MimePair& Second) {
return First.first > Second.first; };
65 std::ranges::sort(weightings, comp);
70 result.reserve(weightings.size());
71 for (
const auto & weight : weightings)
72 result.append(weight.second);
76 result.append(
"application/xml");
90 auto types = contenttype.split(
";", Qt::SkipEmptyParts);
97 Request->m_content->m_mimeType = mime;
98 if (mime.Name() ==
"application/x-www-form-urlencoded")
100 else if (mime.Name() ==
"text/xml" || mime.Name() ==
"application/xml" ||
101 mime.Name() ==
"application/soap+xml")
103 else if (mime.Name() ==
"application/json")
106 LOG(VB_HTTP, LOG_ERR, QString(
"Don't know how to get the parameters for MIME type: '%1'").arg(mime.Name()));
110 LOG(VB_HTTP, LOG_ERR, QString(
"Unknown MIME type: '%1'").arg(
types[0]));
120 auto payload = QString::fromUtf8(
Request->m_content->constData(),
Request->m_content->size());
125 payload.replace(
"&",
"&");
126 if (!payload.isEmpty())
128 QStringList params = payload.split(
'&', Qt::SkipEmptyParts);
129 for (
const auto & param : std::as_const(params))
131 QString name = param.section(
'=', 0, 0);
132 QString value = param.section(
'=', 1);
133 value.replace(
"+",
" ");
136 name = QUrl::fromPercentEncoding(name.toUtf8());
137 value = QUrl::fromPercentEncoding(value.toUtf8());
138 Request->m_queries.insert(name.trimmed().toLower(), value);
146 LOG(VB_HTTP, LOG_DEBUG,
"Inspecting XML payload");
152 QString soapaction =
Request->m_headers->value(
"soapaction");
153 soapaction.remove(
'"');
154 int lastSlashPos= soapaction.lastIndexOf(
'/');
155 if (lastSlashPos < 0)
159 Request->m_fileName = soapaction.right(soapaction.size()-lastSlashPos-1);
160 LOG(VB_HTTP, LOG_DEBUG, QString(
"Found method call (%1)").arg(
Request->m_fileName));
162 auto payload = QDomDocument();
163#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
167 if (!payload.setContent(
static_cast<QByteArray
>(
Request->m_content->constData()),
168 true, &err_msg, &err_line, &err_col))
170 LOG(VB_HTTP, LOG_WARNING,
"Unable to parse XML request body");
171 LOG(VB_HTTP, LOG_WARNING, QString(
"- Error at line %1, column %2, msg: %3")
172 .arg(err_line).arg(err_col).arg(err_msg));
176 auto parseresult = payload.setContent(
Request->m_content->constData(),
177 QDomDocument::ParseOption::UseNamespaceProcessing);
180 LOG(VB_HTTP, LOG_WARNING,
"Unable to parse XML request body");
181 LOG(VB_HTTP, LOG_WARNING, QString(
"- Error at line %1, column %2, msg: %3")
182 .arg(parseresult.errorLine).arg(parseresult.errorColumn)
183 .arg(parseresult.errorMessage));
187 QString doc_name = payload.documentElement().localName();
188 if (doc_name.compare(
"envelope", Qt::CaseInsensitive) == 0)
190 LOG(VB_HTTP, LOG_DEBUG,
"Found SOAP XML message envelope");
191 auto doc_body = payload.documentElement().namedItem(
"Body");
192 if (doc_body.isNull() || !doc_body.hasChildNodes())
194 LOG(VB_HTTP, LOG_DEBUG,
"Missing or empty SOAP body");
197 auto body_contents = doc_body.firstChild();
198 if (body_contents.hasChildNodes())
200 for (QDomNode node = body_contents.firstChild(); !node.isNull(); node = node.nextSibling())
202 QString name = node.localName();
203 QString value = node.toElement().text();
207 Request->m_queries.insert(name.trimmed().toLower(), value);
208 LOG(VB_HTTP, LOG_DEBUG, QString(
"Found URL param (%1=%2)").arg(name, value));
217 LOG(VB_HTTP, LOG_DEBUG,
"Inspecting JSON payload");
222 QByteArray jstr =
static_cast<QByteArray
>(
Request->m_content->constData());
223 QJsonParseError parseError {};
224 QJsonDocument doc = QJsonDocument::fromJson(jstr, &parseError);
225 if (parseError.error != QJsonParseError::NoError)
227 LOG(VB_HTTP, LOG_WARNING,
228 QString(
"Unable to parse JSON request body - Error at position %1, msg: %2")
229 .arg(parseError.offset).arg(parseError.errorString()));
233 QJsonObject json = doc.object();
234 QStringList keys = json.keys();
235 for (
const QString& key : std::as_const(keys))
240 if (json.value(key).isObject())
242 QJsonDocument vd(json.value(key).toObject());
243 value = vd.toJson(QJsonDocument::Compact);
247 value = json.value(key).toVariant().toString();
251 LOG(VB_HTTP, LOG_WARNING,
252 QString(
"Failed to parse value for key '%1' from %2")
253 .arg(key, QString(jstr)));
257 Request->m_queries.insert(key.trimmed().toLower(), value);
258 LOG(VB_HTTP, LOG_DEBUG,
259 QString(
"Found URL param (%1=%2)").arg(key, value));
268 auto * data = std::get_if<HTTPData>(&Content);
269 auto *
file = std::get_if<HTTPFile>(&Content);
281 if (
types.size() == 1)
282 return types.front();
286 static const std::map<QString,QString> s_mimeOverrides =
288 {
"ts",
"video/mp2t"}
292 for (
const auto &
type : s_mimeOverrides)
293 if (suffix.compare(
type.first, Qt::CaseInsensitive) == 0)
322 auto * data = std::get_if<HTTPData>(&Response->
m_response);
332 if ((data && !(*data)->m_ranges.empty()) || (
file && !(*file)->m_ranges.empty()))
345 bool chunky = Size > 102400;
351 bool gzipsize = Size > 512 && !chunky;
355 bool compressable = (data ? (*data)->m_mimeType : (*file)->m_mimeType).Inherits(
"text/plain");
358 bool gzip = wantgzip && gzipsize && compressable;
359 bool chunk = chunkable && chunky && allowchunk;
367 if (data) (*data)->m_encoding = result;
368 if (
file) (*file)->m_encoding = result;
379 Response->
AddHeader(
"Content-Encoding",
"gzip");
381 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"'%1' compressed from %2 to %3 bytes")
382 .arg(data ? (*data)->m_fileName : (*file)->fileName())
383 .arg(Size).arg(buffer->size()));
386 buffer->m_lastModified = data ? (*data)->m_lastModified : (*file)->m_lastModified;
387 buffer->m_etag = data ? (*data)->m_etag : (*file)->m_etag;
388 buffer->m_fileName = data ? (*data)->m_fileName : (*file)->m_fileName;
389 buffer->m_cacheType = data ? (*data)->m_cacheType : (*file)->m_cacheType;
392 Size = buffer->size();
static MythHTTPEncode Compress(MythHTTPResponse *Response, int64_t &Size)
Compress the response content under certain circumstances or mark the content as 'chunkable'.
static void GetURLEncodedParameters(MythHTTPRequest *Request)
static void GetContentType(MythHTTPRequest *Request)
Parse the incoming Content-Type header for POST/PUT content.
static QStringList GetMimeTypes(const QString &Accept)
static void GetJSONEncodedParameters(MythHTTPRequest *Request)
static MythMimeType GetMimeType(HTTPVariant Content)
Return a QMimeType that represents Content.
static void GetXMLEncodedParameters(MythHTTPRequest *Request)
Limited parsing of HTTP method and some headers to determine validity of request.
MythHTTPVersion m_version
void AddHeader(const QString &key, const T &val)
HTTPHeaders m_requestHeaders
static QString GetHeader(const HTTPHeaders &Headers, const QString &Value, const QString &Default="")
static MythMimeTypes MimeTypesForFileName(const QString &FileName)
Return a vector of mime types that match the given filename.
static QString SuffixForFileName(const QString &FileName)
Return the preferred suffix for the given filename.
static MythMimeType MimeTypeForName(const QString &Name)
Return a mime type that matches the given name.
static MythMimeType MimeTypeForFileNameAndData(const QString &FileName, const QByteArray &Data)
Return a mime type for the given FileName and data.
static const struct wl_interface * types[]
std::pair< float, QString > MimePair
Parse the incoming HTTP 'Accept' header and return an ordered list of preferences.
std::shared_ptr< MythHTTPData > HTTPData
std::variant< std::monostate, HTTPData, HTTPFile > HTTPVariant
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
QByteArray gzipCompress(const QByteArray &data)