9 #include "multipartformdataparser.h"
13 #include <QJsonDocument>
15 #include <QJsonObject>
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();
52 QString 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" << d->engineRequest->remoteAddress;
69 d->remoteHostname = ptr.hostName();
70 ret = d->remoteHostname;
74 quint16 Request::port()
const
77 return d->engineRequest->remotePort;
80 QUrl Request::uri()
const
85 if (!(d->parserStatus & RequestPrivate::UrlParsed)) {
87 if (d->engineRequest->serverAddress.isEmpty()) {
88 uri.setHost(QHostInfo::localHostName());
90 uri.setAuthority(d->engineRequest->serverAddress);
93 uri.setScheme(d->engineRequest->isSecure ? QStringLiteral(
"https") : QStringLiteral(
"http"));
96 uri.setPath(QLatin1Char(
'/') + d->engineRequest->path);
98 if (!d->engineRequest->query.isEmpty()) {
99 uri.setQuery(QString::fromLatin1(d->engineRequest->query));
103 d->parserStatus |= RequestPrivate::UrlParsed;
108 QString Request::base()
const
111 QString base = d->base;
112 if (!(d->parserStatus & RequestPrivate::BaseParsed)) {
113 base = d->engineRequest->isSecure ? QStringLiteral(
"https://") : QStringLiteral(
"http://");
116 if (d->engineRequest->serverAddress.isEmpty()) {
117 base.append(QHostInfo::localHostName());
119 base.append(d->engineRequest->serverAddress);
123 base.append(QLatin1Char(
'/'));
126 d->parserStatus |= RequestPrivate::BaseParsed;
131 QString Request::path() const noexcept
134 return d->engineRequest->path;
137 QString Request::match() const noexcept
149 QStringList Request::arguments() const noexcept
173 bool Request::secure() const noexcept
176 return d->engineRequest->isSecure;
185 QVariant 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);
287 if (!(d->parserStatus & RequestPrivate::CookiesParsed)) {
291 auto it = d->cookies.constFind(name);
292 while (it != d->cookies.constEnd() && it.key() == name) {
293 ret.prepend(it.value());
302 if (!(d->parserStatus & RequestPrivate::CookiesParsed)) {
311 return d->engineRequest->headers;
314 QString Request::method() const noexcept
317 return d->engineRequest->method;
323 return d->engineRequest->method.compare(u
"POST") == 0;
329 return d->engineRequest->method.compare(u
"GET") == 0;
335 return d->engineRequest->method.compare(u
"HEAD") == 0;
341 return d->engineRequest->method.compare(u
"PUT") == 0;
347 return d->engineRequest->method.compare(u
"PATCH") == 0;
353 return d->engineRequest->method.compare(u
"DELETE") == 0;
356 QString Request::protocol() const noexcept
359 return d->engineRequest->protocol;
365 return d->engineRequest->headers.header(QStringLiteral(
"X_REQUESTED_WITH")).compare(u
"XMLHttpRequest") == 0;
368 QString Request::remoteUser() const noexcept
371 return d->engineRequest->remoteUser;
377 if (!(d->parserStatus & RequestPrivate::BodyParsed)) {
386 if (!(d->parserStatus & RequestPrivate::BodyParsed)) {
389 return d->uploadsMap;
396 const auto range = map.equal_range(name);
397 for (
auto i = range.first; i != range.second; ++i) {
409 auto it = args.constEnd();
410 while (it != args.constBegin()) {
412 ret.replace(it.key(), it.value());
424 auto it = query.constEnd();
425 while (it != query.constBegin()) {
427 urlQuery.addQueryItem(it.key(), it.value());
429 ret.setQuery(urlQuery);
440 void RequestPrivate::parseUrlQuery()
const
443 if (engineRequest->query.size()) {
445 if (engineRequest->query.indexOf(
'=') < 0) {
446 QByteArray aux = engineRequest->query;
447 queryKeywords = Utils::decodePercentEncoding(&aux);
449 if (parserStatus & RequestPrivate::UrlParsed) {
450 queryParam = Utils::decodePercentEncoding(engineRequest->query.data(), engineRequest->query.size());
452 QByteArray aux = engineRequest->query;
454 queryParam = Utils::decodePercentEncoding(aux.data(), aux.size());
458 parserStatus |= RequestPrivate::QueryParsed;
461 void 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 QString contentTypeKey = QStringLiteral(
"CONTENT_TYPE");
477 const QString contentType = engineRequest->headers.header(contentTypeKey);
478 if (contentType.startsWith(u
"application/x-www-form-urlencoded", Qt::CaseInsensitive)) {
485 QByteArray line = body->readAll();
486 bodyParam = Utils::decodePercentEncoding(line.data(), line.size());
487 bodyData = QVariant::fromValue(bodyParam);
488 }
else if (contentType.startsWith(u
"multipart/form-data", Qt::CaseInsensitive)) {
494 for (
Upload *upload : ups) {
495 if (upload->filename().isEmpty() && upload->headers().header(contentTypeKey).isEmpty()) {
496 bodyParam.insert(upload->name(), QString::fromUtf8(upload->readAll()));
499 uploadsMap.insert(upload->name(), upload);
503 }
else if (contentType.startsWith(u
"application/json", Qt::CaseInsensitive)) {
508 bodyData = QJsonDocument::fromJson(body->readAll());
515 parserStatus |= RequestPrivate::BodyParsed;
518 static inline bool isSlit(QChar c)
520 return c == u
';' || c == u
',';
523 int findNextSplit(
const QString &text,
int from,
int length)
525 while (from < length) {
526 if (isSlit(text.at(from))) {
534 static inline bool isLWS(QChar c)
536 return c == u
' ' || c == u
'\t' || c == u
'\r' || c == u
'\n';
539 static int nextNonWhitespace(
const QString &text,
int from,
int length)
545 while (from < length) {
546 if (isLWS(text.at(from)))
553 return text.length();
556 static std::pair<QString, QString> nextField(
const QString &text,
int &position)
558 std::pair<QString, QString> ret;
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(QLatin1Char(
'='), position);
571 if (equalsPosition < 0 || equalsPosition > semiColonPosition) {
575 ret.first = QStringView(text).mid(position, equalsPosition - position).trimmed().toString();
576 int secondLength = semiColonPosition - equalsPosition - 1;
577 if (secondLength > 0) {
578 ret.second = QStringView(text).mid(equalsPosition + 1, secondLength).trimmed().toString();
581 position = semiColonPosition;
585 void RequestPrivate::parseCookies()
const
587 const QString cookieString = engineRequest->headers.header(QStringLiteral(
"COOKIE"));
589 const int length = cookieString.length();
590 while (position < length) {
591 const auto field = nextField(cookieString, position);
592 if (field.first.isEmpty()) {
598 if (field.second.isEmpty()) {
602 cookies.insert(field.first, field.second);
606 parserStatus |= RequestPrivate::CookiesParsed;
609 QVariantMap RequestPrivate::paramsMultiMapToVariantMap(
const ParamsMultiMap ¶ms)
612 auto end = params.constEnd();
613 while (params.constBegin() != end) {
615 ret.insert(ret.constBegin(), end.key(), end.value());
620 #include "moc_request.cpp"
QIODevice * body
The QIODevice containing the body (if any) of the request.
QMultiMap< QString, Upload * > uploadsMap() const
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
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)
Headers headers() const noexcept
ParamsMultiMap queryParameters() const
QString cookie(const QString &name) 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)
ParamsMultiMap cookies() const
Cutelyst Upload handles file upload request
The Cutelyst namespace holds all public Cutelyst API.
QVector< Upload * > Uploads
QMultiMap< QString, QString > ParamsMultiMap