8#include "multipartformdataparser.h"
14#include <QJsonDocument>
20 : d_ptr(new RequestPrivate)
22 d_ptr->engineRequest = engineRequest;
28 qDeleteAll(d_ptr->uploads);
36 return d->engineRequest->remoteAddress;
44 quint32 data = d->engineRequest->remoteAddress.toIPv4Address(&ok);
46 return QHostAddress(data).toString();
48 return d->engineRequest->remoteAddress.toString();
52QString Request::hostname()
const
58 if (!d->remoteHostname.isEmpty()) {
59 ret = d->remoteHostname;
63 const QHostInfo ptr = QHostInfo::fromName(d->engineRequest->remoteAddress.toString());
64 if (ptr.error() != QHostInfo::NoError) {
65 qCDebug(CUTELYST_REQUEST) <<
"DNS lookup for the client hostname failed"
66 << d->engineRequest->remoteAddress;
70 d->remoteHostname = ptr.hostName();
71 ret = d->remoteHostname;
75quint16 Request::port() const noexcept
78 return d->engineRequest->remotePort;
81QUrl Request::uri()
const
86 if (!(d->parserStatus & RequestPrivate::UrlParsed)) {
88 if (d->engineRequest->serverAddress.isEmpty()) {
89 uri.setHost(QHostInfo::localHostName());
91 uri.setAuthority(QString::fromLatin1(d->engineRequest->serverAddress));
94 uri.setScheme(d->engineRequest->isSecure ? QStringLiteral(
"https")
95 : QStringLiteral(
"http"));
99 uri.setPath(d->engineRequest->path);
101 if (!d->engineRequest->query.isEmpty()) {
102 uri.setQuery(QString::fromLatin1(d->engineRequest->query));
106 d->parserStatus |= RequestPrivate::UrlParsed;
111QString Request::base()
const
114 QString base = d->base;
115 if (!(d->parserStatus & RequestPrivate::BaseParsed)) {
116 base = d->engineRequest->isSecure ? QStringLiteral(
"https://") : QStringLiteral(
"http://");
119 if (d->engineRequest->serverAddress.isEmpty()) {
120 base.append(QHostInfo::localHostName());
122 base.append(QString::fromLatin1(d->engineRequest->serverAddress));
126 d->parserStatus |= RequestPrivate::BaseParsed;
131QString Request::path() const noexcept
134 return d->engineRequest->path;
137QString Request::match() const noexcept
149QStringList Request::arguments() const noexcept
173bool Request::secure() const noexcept
176 return d->engineRequest->isSecure;
185QVariant Request::bodyData()
const
188 if (!(d->parserStatus & RequestPrivate::BodyParsed)) {
196 return bodyData().value<QCborValue>();
201 return bodyData().toJsonDocument();
206 return bodyData().toJsonDocument().object();
211 return bodyData().toJsonDocument().array();
216 return RequestPrivate::paramsMultiMapToVariantMap(
bodyParameters());
222 if (!(d->parserStatus & RequestPrivate::BodyParsed)) {
233 auto it = query.constFind(key);
234 while (it != query.constEnd() && it.key() == key) {
235 ret.prepend(it.value());
244 if (!(d->parserStatus & RequestPrivate::QueryParsed)) {
247 return d->queryKeywords;
258 if (!(d->parserStatus & RequestPrivate::QueryParsed)) {
261 return d->queryParam;
269 auto it = query.constFind(key);
270 while (it != query.constEnd() && it.key() == key) {
271 ret.prepend(it.value());
280 if (!(d->parserStatus & RequestPrivate::CookiesParsed)) {
284 return d->cookies.value(name).value;
292 if (!(d->parserStatus & RequestPrivate::CookiesParsed)) {
296 for (
auto it = d->cookies.constFind(name); it != d->cookies.constEnd() && it->name == name;
298 ret.prepend(it->value);
306 if (!(d->parserStatus & RequestPrivate::CookiesParsed)) {
315 return d->engineRequest->headers;
318QByteArray Request::method() const noexcept
321 return d->engineRequest->method;
327 return d->engineRequest->method.compare(
"POST") == 0;
333 return d->engineRequest->method.compare(
"GET") == 0;
339 return d->engineRequest->method.compare(
"HEAD") == 0;
345 return d->engineRequest->method.compare(
"PUT") == 0;
351 return d->engineRequest->method.compare(
"PATCH") == 0;
357 return d->engineRequest->method.compare(
"DELETE") == 0;
360QByteArray Request::protocol() const noexcept
363 return d->engineRequest->protocol;
369 return d->engineRequest->headers.header(
"X-Requested-With").compare(
"XMLHttpRequest") == 0;
372QString Request::remoteUser() const noexcept
375 return d->engineRequest->remoteUser;
381 if (!(d->parserStatus & RequestPrivate::BodyParsed)) {
390 if (!(d->parserStatus & RequestPrivate::BodyParsed)) {
393 return d->uploadsMap;
400 const auto range = map.equal_range(name);
401 for (
auto i = range.first; i != range.second; ++i) {
413 auto it = args.constEnd();
414 while (it != args.constBegin()) {
416 ret.replace(it.key(), it.value());
428 auto it = query.constEnd();
429 while (it != query.constBegin()) {
431 urlQuery.addQueryItem(it.key(), it.value());
433 ret.setQuery(urlQuery);
444void RequestPrivate::parseUrlQuery()
const
447 if (engineRequest->query.size()) {
449 if (engineRequest->query.indexOf(
'=') < 0) {
450 QByteArray aux = engineRequest->query;
451 queryKeywords = Utils::decodePercentEncoding(&aux);
453 if (parserStatus & RequestPrivate::UrlParsed) {
454 queryParam = Utils::decodePercentEncoding(engineRequest->query.data(),
455 engineRequest->query.size());
457 QByteArray aux = engineRequest->query;
459 queryParam = Utils::decodePercentEncoding(aux.data(), aux.size());
463 parserStatus |= RequestPrivate::QueryParsed;
466void RequestPrivate::parseBody()
const
469 parserStatus |= RequestPrivate::BodyParsed;
473 bool sequencial = body->isSequential();
474 qint64 posOrig = body->pos();
475 if (sequencial && posOrig) {
476 qCWarning(CUTELYST_REQUEST) <<
"Can not parse sequential post body out of beginning";
477 parserStatus |= RequestPrivate::BodyParsed;
481 const QByteArray contentType = engineRequest->headers.header(
"Content-Type");
482 if (contentType.startsWith(
"application/x-www-form-urlencoded")) {
489 QByteArray line = body->readAll();
490 bodyParam = Utils::decodePercentEncoding(line.data(), line.size());
491 bodyData = QVariant::fromValue(bodyParam);
492 }
else if (contentType.startsWith(
"multipart/form-data")) {
498 for (
Upload *upload : ups) {
499 if (upload->filename().isEmpty() &&
500 upload->headers().header(
"Content-Type"_qba).isEmpty()) {
501 bodyParam.insert(upload->name(), QString::fromUtf8(upload->readAll()));
504 uploadsMap.insert(upload->name(), upload);
508 }
else if (contentType.startsWith(
"application/cbor")) {
513 bodyData = QVariant::fromValue(QCborValue::fromCbor(body->readAll()));
514 }
else if (contentType.startsWith(
"application/json")) {
519 bodyData = QJsonDocument::fromJson(body->readAll());
526 parserStatus |= RequestPrivate::BodyParsed;
529static inline bool isSlit(
char c)
531 return c ==
';' || c ==
',';
534int findNextSplit(QByteArrayView text,
int from,
int length)
536 while (from < length) {
537 if (isSlit(text.at(from))) {
545static inline bool isLWS(
char c)
547 return c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n';
550static int nextNonWhitespace(QByteArrayView text,
int from,
int length)
556 while (from < length) {
557 if (isLWS(text.at(from)))
564 return text.length();
574 const int length = text.length();
575 position = nextNonWhitespace(text, position, length);
577 int semiColonPosition = findNextSplit(text, position, length);
578 if (semiColonPosition < 0)
579 semiColonPosition = length;
581 int equalsPosition = text.indexOf(
'=', position);
582 if (equalsPosition < 0 || equalsPosition > semiColonPosition) {
588 cookie.name = text.sliced(position, equalsPosition - position).toByteArray().trimmed();
589 int secondLength = semiColonPosition - equalsPosition - 1;
590 if (secondLength > 0) {
594 cookie.value = text.sliced(equalsPosition + 1, secondLength).toByteArray().trimmed();
597 position = semiColonPosition;
601void RequestPrivate::parseCookies()
const
603 const QByteArray cookieString = engineRequest->headers.header(
"Cookie"_qba);
605 const int length = cookieString.length();
606 while (position < length) {
607 const auto cookie = nextField(cookieString, position);
608 if (cookie.name.isEmpty()) {
614 if (cookie.value.isEmpty()) {
618 cookies.insert(cookie.name, cookie);
622 parserStatus |= RequestPrivate::CookiesParsed;
625QVariantMap RequestPrivate::paramsMultiMapToVariantMap(
const ParamsMultiMap ¶ms)
628 auto end = params.constEnd();
629 while (params.constBegin() != end) {
631 ret.insert(ret.constBegin(), end.key(), end.value());
636#include "moc_request.cpp"
QVariantMap bodyParametersVariant() const
QCborValue bodyCbor() const
QVariantMap queryParametersVariant() const
QString addressString() const
bool isGet() const noexcept
QString queryKeywords() const
QVector< Upload * > uploads() const
ParamsMultiMap bodyParameters() const
QJsonArray bodyJsonArray() const
bool xhr() const noexcept
QJsonObject bodyJsonObject() const
QStringList captures() const noexcept
bool isPut() const noexcept
bool isDelete() const noexcept
QByteArray cookie(QByteArrayView name) const
QUrl uriWith(const ParamsMultiMap &args, bool append=false) const
bool isPost() const noexcept
QJsonDocument bodyJsonDocument() const
ParamsMultiMap mangleParams(const ParamsMultiMap &args, bool append=false) const
void setCaptures(const QStringList &captures)
QMultiMap< QByteArrayView, Cookie > cookies() const
Headers headers() const noexcept
QIODevice * body() const noexcept
ParamsMultiMap queryParameters() const
bool isPatch() const noexcept
Engine * engine() const noexcept
Request(EngineRequest *engineRequest)
bool isHead() const noexcept
void setArguments(const QStringList &arguments)
QHostAddress address() const noexcept
void setMatch(const QString &match)
QMultiMap< QStringView, Upload * > uploadsMap() const
Cutelyst Upload handles file upload requests.
QMultiMap< QString, QString > ParamsMultiMap
The Cutelyst namespace holds all public Cutelyst API.