From 73175545e69cc5f07a7a1447a6b8c4c74d9795c8 Mon Sep 17 00:00:00 2001 From: Mikhail Svetkin Date: Sun, 21 Jul 2019 20:52:31 +0200 Subject: QHttpServerResponse: Extend the API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add new API for HTTP headers manipulations. Add QHttpServerResponse::write function which will allow to write custom response objects in a future. Fixes: QTBUG-76933 Change-Id: I744303be1b517c07f698c4a3dd2c4296f77e3b03 Reviewed-by: Tasuku Suzuki Reviewed-by: Edward Welbourne Reviewed-by: MÃ¥rten Nordheim --- src/httpserver/qhttpserver.cpp | 5 +- src/httpserver/qhttpserverresponse.cpp | 225 +++++++++++++++++++++++++++++++-- src/httpserver/qhttpserverresponse.h | 38 +++++- src/httpserver/qhttpserverresponse_p.h | 13 +- 4 files changed, 264 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/httpserver/qhttpserver.cpp b/src/httpserver/qhttpserver.cpp index 5b9ed05..7cc91db 100644 --- a/src/httpserver/qhttpserver.cpp +++ b/src/httpserver/qhttpserver.cpp @@ -126,10 +126,7 @@ void QHttpServer::sendResponse(const QHttpServerResponse &response, const QHttpServerRequest &request, QTcpSocket *socket) { - auto responder = makeResponder(request, socket); - responder.write(response.data(), - response.mimeType(), - response.statusCode()); + response.write(makeResponder(request, socket)); } /*! diff --git a/src/httpserver/qhttpserverresponse.cpp b/src/httpserver/qhttpserverresponse.cpp index ca2e657..a7ba006 100644 --- a/src/httpserver/qhttpserverresponse.cpp +++ b/src/httpserver/qhttpserverresponse.cpp @@ -29,8 +29,9 @@ #include -#include #include +#include +#include #include #include @@ -89,15 +90,16 @@ QHttpServerResponse::QHttpServerResponse(const QJsonArray &data) QHttpServerResponse::QHttpServerResponse(const QByteArray &mimeType, const QByteArray &data, const StatusCode status) - : QHttpServerResponse(new QHttpServerResponsePrivate{mimeType, data, status}) + : QHttpServerResponse(mimeType, + new QHttpServerResponsePrivate{data, status, {}}) { } QHttpServerResponse::QHttpServerResponse(QByteArray &&mimeType, const QByteArray &data, const StatusCode status) - : QHttpServerResponse( - new QHttpServerResponsePrivate{std::move(mimeType), data, status}) + : QHttpServerResponse(std::move(mimeType), + new QHttpServerResponsePrivate{data, status, {}}) { } @@ -105,7 +107,8 @@ QHttpServerResponse::QHttpServerResponse(const QByteArray &mimeType, QByteArray &&data, const StatusCode status) : QHttpServerResponse( - new QHttpServerResponsePrivate{mimeType, std::move(data), status}) + mimeType, + new QHttpServerResponsePrivate{std::move(data), status, {}}) { } @@ -113,8 +116,8 @@ QHttpServerResponse::QHttpServerResponse(QByteArray &&mimeType, QByteArray &&data, const StatusCode status) : QHttpServerResponse( - new QHttpServerResponsePrivate{std::move(mimeType), std::move(data), - status}) + std::move(mimeType), + new QHttpServerResponsePrivate{std::move(data), status, {}}) { } @@ -133,27 +136,227 @@ QHttpServerResponse QHttpServerResponse::fromFile(const QString &fileName) return QHttpServerResponse(mimeType, data); } -QHttpServerResponse::QHttpServerResponse(QHttpServerResponsePrivate *d) +QHttpServerResponse::QHttpServerResponse(const QByteArray &mimeType, + QHttpServerResponsePrivate *d) + : d_ptr(d) +{ + setHeader(QHttpServerLiterals::contentTypeHeader(), mimeType); +} + +QHttpServerResponse::QHttpServerResponse(QByteArray &&mimeType, + QHttpServerResponsePrivate *d) : d_ptr(d) { + setHeader(QHttpServerLiterals::contentTypeHeader(), + std::move(mimeType)); } +/*! + Returns response body. +*/ QByteArray QHttpServerResponse::data() const { Q_D(const QHttpServerResponse); return d->data; } +QHttpServerResponse::StatusCode QHttpServerResponse::statusCode() const +{ + Q_D(const QHttpServerResponse); + return d->statusCode; +} + +/*! + Returns HTTP "Content-Type" header. + + \note Default value is "text/html" +*/ QByteArray QHttpServerResponse::mimeType() const { Q_D(const QHttpServerResponse); - return d->mimeType; + const auto res = d->headers.find( + QHttpServerLiterals::contentTypeHeader()); + if (res == d->headers.end()) + return QHttpServerLiterals::contentTypeTextHtml(); + + return res->second; } -QHttpServerResponse::StatusCode QHttpServerResponse::statusCode() const +/*! + Adds the HTTP header with name \a name and value \a value, + does not override any previously set headers. +*/ +void QHttpServerResponse::addHeader(QByteArray &&name, QByteArray &&value) +{ + Q_D(QHttpServerResponse); + d->headers.emplace(std::move(name), std::move(value)); +} + +/*! + Adds the HTTP header with name \a name and value \a value, + does not override any previously set headers. +*/ +void QHttpServerResponse::addHeader(QByteArray &&name, const QByteArray &value) +{ + Q_D(QHttpServerResponse); + d->headers.emplace(std::move(name), value); +} + +/*! + Adds the HTTP header with name \a name and value \a value, + does not override any previously set headers. +*/ +void QHttpServerResponse::addHeader(const QByteArray &name, QByteArray &&value) +{ + Q_D(QHttpServerResponse); + d->headers.emplace(name, std::move(value)); +} + +/*! + Adds the HTTP header with name \a name and value \a value, + does not override any previously set headers. +*/ +void QHttpServerResponse::addHeader(const QByteArray &name, const QByteArray &value) +{ + Q_D(QHttpServerResponse); + d->headers.emplace(name, value); +} + +void QHttpServerResponse::addHeaders(QHttpServerResponder::HeaderList headers) +{ + for (auto &&header : headers) + addHeader(header.first, header.second); +} + +/*! + Removes the HTTP header with name \a name. +*/ +void QHttpServerResponse::clearHeader(const QByteArray &name) +{ + Q_D(QHttpServerResponse); + d->headers.erase(name); +} + +/*! + Removes all HTTP headers. +*/ +void QHttpServerResponse::clearHeaders() +{ + Q_D(QHttpServerResponse); + d->headers.clear(); +} + +/*! + Sets the HTTP header with name \a name and value \a value, + overriding any previously set headers. +*/ +void QHttpServerResponse::setHeader(QByteArray &&name, QByteArray &&value) +{ + Q_D(QHttpServerResponse); + clearHeader(name); + addHeader(std::move(name), std::move(value)); +} + +/*! + Sets the HTTP header with name \a name and value \a value, + overriding any previously set headers. +*/ +void QHttpServerResponse::setHeader(QByteArray &&name, const QByteArray &value) +{ + Q_D(QHttpServerResponse); + clearHeader(name); + addHeader(std::move(name), value); +} + +/*! + Sets the HTTP header with name \a name and value \a value, + overriding any previously set headers. +*/ +void QHttpServerResponse::setHeader(const QByteArray &name, QByteArray &&value) +{ + Q_D(QHttpServerResponse); + clearHeader(name); + addHeader(name, std::move(value)); +} + +/*! + Sets the HTTP header with name \a name and value \a value, + overriding any previously set headers. +*/ +void QHttpServerResponse::setHeader(const QByteArray &name, const QByteArray &value) +{ + Q_D(QHttpServerResponse); + clearHeader(name); + addHeader(name, value); +} + +/*! + Sets the headers \a headers, overriding any previously set headers. +*/ +void QHttpServerResponse::setHeaders(QHttpServerResponder::HeaderList headers) +{ + for (auto &&header : headers) + setHeader(header.first, header.second); +} + +/*! + Returns true if the response contains an HTTP header with name \a name, + otherwise returns false. +*/ +bool QHttpServerResponse::hasHeader(const QByteArray &header) const { Q_D(const QHttpServerResponse); - return d->statusCode; + return d->headers.find(header) != d->headers.end(); +} + +/*! + Returns true if the response contains an HTTP header with name \a name and + with value \a value, otherwise returns false. +*/ +bool QHttpServerResponse::hasHeader(const QByteArray &name, + const QByteArray &value) const +{ + Q_D(const QHttpServerResponse); + auto range = d->headers.equal_range(name); + + auto condition = [&value] (const std::pair &pair) { + return pair.second == value; + }; + + return std::find_if(range.first, range.second, condition) != range.second; +} + +/*! + Returns values of the HTTP header with name \a name +*/ +QVector QHttpServerResponse::headers(const QByteArray &name) const +{ + Q_D(const QHttpServerResponse); + + QVector results; + auto range = d->headers.equal_range(name); + + for (auto it = range.first; it != range.second; ++it) + results.append(it->second); + + return results; +} + +/*! + Writes HTTP response into QHttpServerResponder \a responder. +*/ +void QHttpServerResponse::write(QHttpServerResponder &&responder) const +{ + Q_D(const QHttpServerResponse); + responder.writeStatusLine(d->statusCode); + + for (auto &&header : d->headers) + responder.writeHeader(header.first, header.second); + + responder.writeHeader(QHttpServerLiterals::contentLengthHeader(), + QByteArray::number(d->data.size())); + + responder.writeBody(d->data); } QT_END_NAMESPACE diff --git a/src/httpserver/qhttpserverresponse.h b/src/httpserver/qhttpserverresponse.h index 73fa52d..9f5d3dd 100644 --- a/src/httpserver/qhttpserverresponse.h +++ b/src/httpserver/qhttpserverresponse.h @@ -86,8 +86,44 @@ public: QByteArray mimeType() const; StatusCode statusCode() const; + + void addHeader(QByteArray &&name, QByteArray &&value); + void addHeader(QByteArray &&name, const QByteArray &value); + void addHeader(const QByteArray &name, QByteArray &&value); + void addHeader(const QByteArray &name, const QByteArray &value); + + void addHeaders(QHttpServerResponder::HeaderList headers); + + template + void addHeaders(const Container &headers) + { + for (const auto &header : headers) + addHeader(header.first, header.second); + } + + void clearHeader(const QByteArray &name); + void clearHeaders(); + + void setHeader(QByteArray &&name, QByteArray &&value); + void setHeader(QByteArray &&name, const QByteArray &value); + void setHeader(const QByteArray &name, QByteArray &&value); + void setHeader(const QByteArray &name, const QByteArray &value); + + void setHeaders(QHttpServerResponder::HeaderList headers); + + bool hasHeader(const QByteArray &name) const; + bool hasHeader(const QByteArray &name, const QByteArray &value) const; + + QVector headers(const QByteArray &name) const; + + virtual void write(QHttpServerResponder &&responder) const; + private: - QHttpServerResponse(QHttpServerResponsePrivate *d); + QHttpServerResponse(const QByteArray &mimeType, + QHttpServerResponsePrivate *d); + + QHttpServerResponse(QByteArray &&mimeType, + QHttpServerResponsePrivate *d); QScopedPointer d_ptr; }; diff --git a/src/httpserver/qhttpserverresponse_p.h b/src/httpserver/qhttpserverresponse_p.h index 021ed25..8b81ad0 100644 --- a/src/httpserver/qhttpserverresponse_p.h +++ b/src/httpserver/qhttpserverresponse_p.h @@ -44,14 +44,25 @@ #include +#include +#include + QT_BEGIN_NAMESPACE class QHttpServerResponsePrivate { + struct HashHelper { + std::size_t operator()(const QByteArray& key) const + { + return qHash(key.toLower()); + } + }; + public: - QByteArray mimeType; QByteArray data; QHttpServerResponse::StatusCode statusCode; + + std::unordered_multimap headers; }; QT_END_NAMESPACE -- cgit v1.2.3