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 auto newquality = qual.mid(index2 + 1).toFloat(&ok);
56 weightings.emplace_back(quality, mime);
60 auto comp = [](
const MimePair& First,
const MimePair& Second) {
return First.first > Second.first; };
61 std::ranges::sort(weightings, comp);
66 for (
const auto & weight : weightings)
67 result.append(weight.second);
71 result.append(
"application/xml");
85 auto types = contenttype.split(
";", Qt::SkipEmptyParts);
92 Request->m_content->m_mimeType = mime;
93 if (mime.Name() ==
"application/x-www-form-urlencoded")
95 else if (mime.Name() ==
"text/xml" || mime.Name() ==
"application/xml" ||
96 mime.Name() ==
"application/soap+xml")
98 else if (mime.Name() ==
"application/json")
101 LOG(VB_HTTP, LOG_ERR, QString(
"Don't know how to get the parameters for MIME type: '%1'").arg(mime.Name()));
105 LOG(VB_HTTP, LOG_ERR, QString(
"Unknown MIME type: '%1'").arg(
types[0]));
115 auto payload = QString::fromUtf8(
Request->m_content->constData(),
Request->m_content->size());
120 payload.replace(
"&",
"&");
121 if (!payload.isEmpty())
123 QStringList params = payload.split(
'&', Qt::SkipEmptyParts);
124 for (
const auto & param : std::as_const(params))
126 QString name = param.section(
'=', 0, 0);
127 QString value = param.section(
'=', 1);
128 value.replace(
"+",
" ");
131 name = QUrl::fromPercentEncoding(name.toUtf8());
132 value = QUrl::fromPercentEncoding(value.toUtf8());
133 Request->m_queries.insert(name.trimmed().toLower(), value);
141 LOG(VB_HTTP, LOG_DEBUG,
"Inspecting XML payload");
147 QString soapaction =
Request->m_headers->value(
"soapaction");
148 soapaction.remove(
'"');
149 int lastSlashPos= soapaction.lastIndexOf(
'/');
150 if (lastSlashPos < 0)
154 Request->m_fileName = soapaction.right(soapaction.size()-lastSlashPos-1);
155 LOG(VB_HTTP, LOG_DEBUG, QString(
"Found method call (%1)").arg(
Request->m_fileName));
157 auto payload = QDomDocument();
158#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
162 if (!payload.setContent(
static_cast<QByteArray
>(
Request->m_content->constData()),
163 true, &err_msg, &err_line, &err_col))
165 LOG(VB_HTTP, LOG_WARNING,
"Unable to parse XML request body");
166 LOG(VB_HTTP, LOG_WARNING, QString(
"- Error at line %1, column %2, msg: %3")
167 .arg(err_line).arg(err_col).arg(err_msg));
171 auto parseresult = payload.setContent(
Request->m_content->constData(),
172 QDomDocument::ParseOption::UseNamespaceProcessing);
175 LOG(VB_HTTP, LOG_WARNING,
"Unable to parse XML request body");
176 LOG(VB_HTTP, LOG_WARNING, QString(
"- Error at line %1, column %2, msg: %3")
177 .arg(parseresult.errorLine).arg(parseresult.errorColumn)
178 .arg(parseresult.errorMessage));
182 QString doc_name = payload.documentElement().localName();
183 if (doc_name.compare(
"envelope", Qt::CaseInsensitive) == 0)
185 LOG(VB_HTTP, LOG_DEBUG,
"Found SOAP XML message envelope");
186 auto doc_body = payload.documentElement().namedItem(
"Body");
187 if (doc_body.isNull() || !doc_body.hasChildNodes())
189 LOG(VB_HTTP, LOG_DEBUG,
"Missing or empty SOAP body");
192 auto body_contents = doc_body.firstChild();
193 if (body_contents.hasChildNodes())
195 for (QDomNode node = body_contents.firstChild(); !node.isNull(); node = node.nextSibling())
197 QString name = node.localName();
198 QString value = node.toElement().text();
202 Request->m_queries.insert(name.trimmed().toLower(), value);
203 LOG(VB_HTTP, LOG_DEBUG, QString(
"Found URL param (%1=%2)").arg(name, value));
212 LOG(VB_HTTP, LOG_DEBUG,
"Inspecting JSON payload");
217 QByteArray jstr =
static_cast<QByteArray
>(
Request->m_content->constData());
218 QJsonParseError parseError {};
219 QJsonDocument doc = QJsonDocument::fromJson(jstr, &parseError);
220 if (parseError.error != QJsonParseError::NoError)
222 LOG(VB_HTTP, LOG_WARNING,
223 QString(
"Unable to parse JSON request body - Error at position %1, msg: %2")
224 .arg(parseError.offset).arg(parseError.errorString()));
228 QJsonObject json = doc.object();
229 QStringList keys = json.keys();
230 for (
const QString& key : std::as_const(keys))
235 if (json.value(key).isObject())
237 QJsonDocument vd(json.value(key).toObject());
238 value = vd.toJson(QJsonDocument::Compact);
242 value = json.value(key).toVariant().toString();
246 LOG(VB_HTTP, LOG_WARNING,
247 QString(
"Failed to parse value for key '%1' from %2")
248 .arg(key, QString(jstr)));
252 Request->m_queries.insert(key.trimmed().toLower(), value);
253 LOG(VB_HTTP, LOG_DEBUG,
254 QString(
"Found URL param (%1=%2)").arg(key, value));
263 auto * data = std::get_if<HTTPData>(&Content);
264 auto *
file = std::get_if<HTTPFile>(&Content);
276 if (
types.size() == 1)
277 return types.front();
281 static const std::map<QString,QString> s_mimeOverrides =
283 {
"ts",
"video/mp2t"}
287 for (
const auto &
type : s_mimeOverrides)
288 if (suffix.compare(
type.first, Qt::CaseInsensitive) == 0)
317 auto * data = std::get_if<HTTPData>(&Response->
m_response);
327 if ((data && !(*data)->m_ranges.empty()) || (
file && !(*file)->m_ranges.empty()))
340 bool chunky = Size > 102400;
346 bool gzipsize = Size > 512 && !chunky;
350 bool compressable = (data ? (*data)->m_mimeType : (*file)->m_mimeType).Inherits(
"text/plain");
353 bool gzip = wantgzip && gzipsize && compressable;
354 bool chunk = chunkable && chunky && allowchunk;
362 if (data) (*data)->m_encoding = result;
363 if (
file) (*file)->m_encoding = result;
374 Response->
AddHeader(
"Content-Encoding",
"gzip");
376 LOG(VB_HTTP, LOG_INFO,
LOC + QString(
"'%1' compressed from %2 to %3 bytes")
377 .arg(data ? (*data)->m_fileName : (*file)->fileName())
378 .arg(Size).arg(buffer->size()));
381 buffer->m_lastModified = data ? (*data)->m_lastModified : (*file)->m_lastModified;
382 buffer->m_etag = data ? (*data)->m_etag : (*file)->m_etag;
383 buffer->m_fileName = data ? (*data)->m_fileName : (*file)->m_fileName;
384 buffer->m_cacheType = data ? (*data)->m_cacheType : (*file)->m_cacheType;
387 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)