8#include "multipartformdataparser.h"
14#include <QJsonDocument>
18using namespace Qt::Literals::StringLiterals;
21 : d_ptr(new RequestPrivate)
23 d_ptr->engineRequest = engineRequest;
29 qDeleteAll(d_ptr->uploads);
37 return d->engineRequest->remoteAddress;
45 quint32 data = d->engineRequest->remoteAddress.toIPv4Address(&ok);
47 return QHostAddress(data).toString();
49 return d->engineRequest->remoteAddress.toString();
59 if (!d->remoteHostname.isEmpty()) {
60 ret = d->remoteHostname;
64 const QHostInfo ptr = QHostInfo::fromName(d->engineRequest->remoteAddress.toString());
65 if (ptr.error() != QHostInfo::NoError) {
66 qCDebug(CUTELYST_REQUEST) <<
"DNS lookup for the client hostname failed"
67 << d->engineRequest->remoteAddress;
71 d->remoteHostname = ptr.hostName();
72 ret = d->remoteHostname;
79 return d->engineRequest->remotePort;
87 if (!(d->parserStatus & RequestPrivate::UrlParsed)) {
89 if (d->engineRequest->serverAddress.isEmpty()) {
90 uri.setHost(QHostInfo::localHostName());
92 uri.setAuthority(QString::fromLatin1(d->engineRequest->serverAddress));
95 uri.setScheme(d->engineRequest->isSecure ? QStringLiteral(
"https")
96 : QStringLiteral(
"http"));
100 uri.setPath(d->engineRequest->path);
102 if (!d->engineRequest->query.isEmpty()) {
103 uri.setQuery(QString::fromLatin1(d->engineRequest->query));
107 d->parserStatus |= RequestPrivate::UrlParsed;
115 QString base = d->base;
116 if (!(d->parserStatus & RequestPrivate::BaseParsed)) {
117 base = d->engineRequest->isSecure ? QStringLiteral(
"https://") : QStringLiteral(
"http://");
120 if (d->engineRequest->serverAddress.isEmpty()) {
121 base.append(QHostInfo::localHostName());
123 base.append(QString::fromLatin1(d->engineRequest->serverAddress));
127 d->parserStatus |= RequestPrivate::BaseParsed;
135 return d->engineRequest->path;
177 return d->engineRequest->isSecure;
189 if (!(d->parserStatus & RequestPrivate::BodyParsed)) {
197 return bodyData().value<QCborValue>();
207 return bodyData().toJsonDocument().object();
212 return bodyData().toJsonDocument().array();
217 return RequestPrivate::paramsMultiMapToVariantMap(
bodyParameters());
223 if (!(d->parserStatus & RequestPrivate::BodyParsed)) {
234 auto it = query.constFind(key);
235 while (it != query.constEnd() && it.key() == key) {
236 ret.prepend(it.value());
245 if (!(d->parserStatus & RequestPrivate::QueryParsed)) {
248 return d->queryKeywords;
259 if (!(d->parserStatus & RequestPrivate::QueryParsed)) {
262 return d->queryParam;
270 auto it = query.constFind(key);
271 while (it != query.constEnd() && it.key() == key) {
272 ret.prepend(it.value());
281 if (!(d->parserStatus & RequestPrivate::CookiesParsed)) {
285 return d->cookies.value(name).value;
293 if (!(d->parserStatus & RequestPrivate::CookiesParsed)) {
297 for (
auto it = d->cookies.constFind(name); it != d->cookies.constEnd() && it->name == name;
299 ret.prepend(it->value);
307 if (!(d->parserStatus & RequestPrivate::CookiesParsed)) {
316 return d->engineRequest->headers;
322 return d->engineRequest->method;
328 return d->engineRequest->method.compare(
"POST") == 0;
334 return d->engineRequest->method.compare(
"GET") == 0;
340 return d->engineRequest->method.compare(
"HEAD") == 0;
346 return d->engineRequest->method.compare(
"PUT") == 0;
352 return d->engineRequest->method.compare(
"PATCH") == 0;
358 return d->engineRequest->method.compare(
"DELETE") == 0;
364 return d->engineRequest->protocol;
370 return d->engineRequest->headers.header(
"X-Requested-With").compare(
"XMLHttpRequest") == 0;
376 return d->engineRequest->remoteUser;
382 if (!(d->parserStatus & RequestPrivate::BodyParsed)) {
391 if (!(d->parserStatus & RequestPrivate::BodyParsed)) {
394 return d->uploadsMap;
401 const auto range = map.equal_range(name);
402 for (
auto i = range.first; i != range.second; ++i) {
414 auto it = args.constEnd();
415 while (it != args.constBegin()) {
417 ret.replace(it.key(), it.value());
429 auto it = query.constEnd();
430 while (it != query.constBegin()) {
432 urlQuery.addQueryItem(it.key(), it.value());
434 ret.setQuery(urlQuery);
445void RequestPrivate::parseUrlQuery()
const
448 if (engineRequest->query.size()) {
450 if (engineRequest->query.indexOf(
'=') < 0) {
451 QByteArray aux = engineRequest->query;
452 queryKeywords = Utils::decodePercentEncoding(&aux);
454 if (parserStatus & RequestPrivate::UrlParsed) {
455 queryParam = Utils::decodePercentEncoding(engineRequest->query.data(),
456 engineRequest->query.size());
458 QByteArray aux = engineRequest->query;
460 queryParam = Utils::decodePercentEncoding(aux.data(), aux.size());
464 parserStatus |= RequestPrivate::QueryParsed;
467void RequestPrivate::parseBody()
const
470 parserStatus |= RequestPrivate::BodyParsed;
474 bool sequencial = body->isSequential();
475 qint64 posOrig = body->pos();
476 if (sequencial && posOrig) {
477 qCWarning(CUTELYST_REQUEST) <<
"Can not parse sequential post body out of beginning";
478 parserStatus |= RequestPrivate::BodyParsed;
482 const QByteArray contentType = engineRequest->headers.header(
"Content-Type");
483 if (contentType.startsWith(
"application/x-www-form-urlencoded")) {
490 QByteArray line = body->readAll();
491 bodyParam = Utils::decodePercentEncoding(line.data(), line.size());
492 bodyData = QVariant::fromValue(bodyParam);
493 }
else if (contentType.startsWith(
"multipart/form-data")) {
499 for (
Upload *upload : ups) {
500 if (upload->filename().isEmpty() &&
501 upload->headers().header(
"Content-Type"_ba).isEmpty()) {
502 bodyParam.insert(upload->name(), QString::fromUtf8(upload->readAll()));
505 uploadsMap.insert(upload->name(), upload);
509 }
else if (contentType.startsWith(
"application/cbor")) {
514 bodyData = QVariant::fromValue(QCborValue::fromCbor(body->readAll()));
515 }
else if (contentType.startsWith(
"application/json")) {
520 bodyData = QJsonDocument::fromJson(body->readAll());
527 parserStatus |= RequestPrivate::BodyParsed;
530static inline bool isSlit(
char c)
532 return c ==
';' || c ==
',';
535int findNextSplit(QByteArrayView text,
int from,
int length)
537 while (from < length) {
538 if (isSlit(text.at(from))) {
546static inline bool isLWS(
char c)
548 return c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n';
551static int nextNonWhitespace(QByteArrayView text,
int from,
int length)
557 while (from < length) {
558 if (isLWS(text.at(from)))
565 return text.length();
575 const int length = text.length();
576 position = nextNonWhitespace(text, position, length);
578 int semiColonPosition = findNextSplit(text, position, length);
579 if (semiColonPosition < 0)
580 semiColonPosition = length;
582 int equalsPosition = text.indexOf(
'=', position);
583 if (equalsPosition < 0 || equalsPosition > semiColonPosition) {
587 cookie.name = text.sliced(position, equalsPosition - position).trimmed().toByteArray();
588 int secondLength = semiColonPosition - equalsPosition - 1;
589 if (secondLength > 0) {
590 cookie.value = text.sliced(equalsPosition + 1, secondLength).trimmed().toByteArray();
593 position = semiColonPosition;
597void RequestPrivate::parseCookies()
const
599 const QByteArray cookieString = engineRequest->headers.header(
"Cookie"_ba);
601 const int length = cookieString.length();
602 while (position < length) {
603 const auto cookie = nextField(cookieString, position);
604 if (cookie.name.isEmpty()) {
610 if (cookie.value.isEmpty()) {
614 cookies.insert(cookie.name, cookie);
618 parserStatus |= RequestPrivate::CookiesParsed;
621QVariantMap RequestPrivate::paramsMultiMapToVariantMap(
const ParamsMultiMap ¶ms)
624 auto end = params.constEnd();
625 while (params.constBegin() != end) {
627 ret.insert(ret.constBegin(), end.key(), end.value());
632#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
QVariant bodyData() const
quint16 port() const noexcept
QJsonArray bodyJsonArray() const
bool xhr() const noexcept
bool secure() const noexcept
QJsonObject bodyJsonObject() const
QString path() const noexcept
ParamsMultiMap queryParams() const
QStringList captures() const noexcept
bool isPut() const noexcept
bool isDelete() const noexcept
QByteArray protocol() const noexcept
QByteArray cookie(QByteArrayView name) const
QUrl uriWith(const ParamsMultiMap &args, bool append=false) const
bool isPost() const noexcept
QString remoteUser() 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)
QByteArray method() const noexcept
bool isHead() const noexcept
void setArguments(const QStringList &arguments)
QString match() const noexcept
QStringList arguments() const noexcept
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.