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().toJsonDocument();
201 return bodyData().toJsonDocument().object();
206 return bodyData().toJsonDocument().array();
211 return RequestPrivate::paramsMultiMapToVariantMap(
bodyParameters());
217 if (!(d->parserStatus & RequestPrivate::BodyParsed)) {
228 auto it = query.constFind(key);
229 while (it != query.constEnd() && it.key() == key) {
230 ret.prepend(it.value());
239 if (!(d->parserStatus & RequestPrivate::QueryParsed)) {
242 return d->queryKeywords;
253 if (!(d->parserStatus & RequestPrivate::QueryParsed)) {
256 return d->queryParam;
264 auto it = query.constFind(key);
265 while (it != query.constEnd() && it.key() == key) {
266 ret.prepend(it.value());
275 if (!(d->parserStatus & RequestPrivate::CookiesParsed)) {
279 return d->cookies.value(name).value;
287 if (!(d->parserStatus & RequestPrivate::CookiesParsed)) {
291 for (
auto it = d->cookies.constFind(name); it != d->cookies.constEnd() && it->name == name;
293 ret.prepend(it->value);
301 if (!(d->parserStatus & RequestPrivate::CookiesParsed)) {
310 return d->engineRequest->headers;
313QByteArray Request::method() const noexcept
316 return d->engineRequest->method;
322 return d->engineRequest->method.compare(
"POST") == 0;
328 return d->engineRequest->method.compare(
"GET") == 0;
334 return d->engineRequest->method.compare(
"HEAD") == 0;
340 return d->engineRequest->method.compare(
"PUT") == 0;
346 return d->engineRequest->method.compare(
"PATCH") == 0;
352 return d->engineRequest->method.compare(
"DELETE") == 0;
355QByteArray Request::protocol() const noexcept
358 return d->engineRequest->protocol;
364 return d->engineRequest->headers.header(
"X-Requested-With").compare(
"XMLHttpRequest") == 0;
367QString Request::remoteUser() const noexcept
370 return d->engineRequest->remoteUser;
376 if (!(d->parserStatus & RequestPrivate::BodyParsed)) {
385 if (!(d->parserStatus & RequestPrivate::BodyParsed)) {
388 return d->uploadsMap;
395 const auto range = map.equal_range(name);
396 for (
auto i = range.first; i != range.second; ++i) {
408 auto it = args.constEnd();
409 while (it != args.constBegin()) {
411 ret.replace(it.key(), it.value());
423 auto it = query.constEnd();
424 while (it != query.constBegin()) {
426 urlQuery.addQueryItem(it.key(), it.value());
428 ret.setQuery(urlQuery);
439void RequestPrivate::parseUrlQuery()
const
442 if (engineRequest->query.size()) {
444 if (engineRequest->query.indexOf(
'=') < 0) {
445 QByteArray aux = engineRequest->query;
446 queryKeywords = Utils::decodePercentEncoding(&aux);
448 if (parserStatus & RequestPrivate::UrlParsed) {
449 queryParam = Utils::decodePercentEncoding(engineRequest->query.data(),
450 engineRequest->query.size());
452 QByteArray aux = engineRequest->query;
454 queryParam = Utils::decodePercentEncoding(aux.data(), aux.size());
458 parserStatus |= RequestPrivate::QueryParsed;
461void RequestPrivate::parseBody()
const
464 parserStatus |= RequestPrivate::BodyParsed;
468 bool sequencial = body->isSequential();
469 qint64 posOrig = body->pos();
470 if (sequencial && posOrig) {
471 qCWarning(CUTELYST_REQUEST) <<
"Can not parse sequential post body out of beginning";
472 parserStatus |= RequestPrivate::BodyParsed;
476 const QByteArray contentType = engineRequest->headers.header(
"Content-Type"_qba);
477 if (contentType.startsWith(
"application/x-www-form-urlencoded")) {
484 QByteArray line = body->readAll();
485 bodyParam = Utils::decodePercentEncoding(line.data(), line.size());
486 bodyData = QVariant::fromValue(bodyParam);
487 }
else if (contentType.startsWith(
"multipart/form-data")) {
493 for (
Upload *upload : ups) {
494 if (upload->filename().isEmpty() &&
495 upload->headers().header(
"Content-Type"_qba).isEmpty()) {
496 bodyParam.insert(upload->name(), QString::fromUtf8(upload->readAll()));
499 uploadsMap.insert(upload->name(), upload);
503 }
else if (contentType.startsWith(
"application/json")) {
508 bodyData = QJsonDocument::fromJson(body->readAll());
515 parserStatus |= RequestPrivate::BodyParsed;
518static inline bool isSlit(
char c)
520 return c ==
';' || c ==
',';
523int findNextSplit(QByteArrayView text,
int from,
int length)
525 while (from < length) {
526 if (isSlit(text.at(from))) {
534static inline bool isLWS(
char c)
536 return c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n';
539static int nextNonWhitespace(QByteArrayView text,
int from,
int length)
545 while (from < length) {
546 if (isLWS(text.at(from)))
553 return text.length();
563 const int length = text.length();
564 position = nextNonWhitespace(text, position, length);
566 int semiColonPosition = findNextSplit(text, position, length);
567 if (semiColonPosition < 0)
568 semiColonPosition = length;
570 int equalsPosition = text.indexOf(
'=', position);
571 if (equalsPosition < 0 || equalsPosition > semiColonPosition) {
577 cookie.name = text.sliced(position, equalsPosition - position).toByteArray().trimmed();
578 int secondLength = semiColonPosition - equalsPosition - 1;
579 if (secondLength > 0) {
583 cookie.value = text.sliced(equalsPosition + 1, secondLength).toByteArray().trimmed();
586 position = semiColonPosition;
590void RequestPrivate::parseCookies()
const
592 const QByteArray cookieString = engineRequest->headers.header(
"Cookie"_qba);
594 const int length = cookieString.length();
595 while (position < length) {
596 const auto cookie = nextField(cookieString, position);
597 if (cookie.name.isEmpty()) {
603 if (cookie.value.isEmpty()) {
607 cookies.insert(cookie.name, cookie);
611 parserStatus |= RequestPrivate::CookiesParsed;
614QVariantMap RequestPrivate::paramsMultiMapToVariantMap(
const ParamsMultiMap ¶ms)
617 auto end = params.constEnd();
618 while (params.constBegin() != end) {
620 ret.insert(ret.constBegin(), end.key(), end.value());
625#include "moc_request.cpp"
QIODevice * body
The QIODevice containing the body (if any) of the request.
QVariantMap bodyParametersVariant() 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 request
The Cutelyst namespace holds all public Cutelyst API.
QVector< Upload * > Uploads
QMultiMap< QString, QString > ParamsMultiMap