add some tests and add documentation to httpreply
This commit is contained in:
parent
27a8db73d8
commit
478c832ec8
@ -18,82 +18,169 @@
|
|||||||
* *
|
* *
|
||||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class HttpReply
|
||||||
|
\brief Represents a reply of the guh webserver.
|
||||||
|
|
||||||
|
\ingroup types
|
||||||
|
\inmodule libguh
|
||||||
|
|
||||||
|
This class holds the header and the payload data of a network reply and represents a response
|
||||||
|
from the guh webserver.
|
||||||
|
|
||||||
|
\note RFC 7231 HTTP/1.1 Semantics and Content -> \l{http://tools.ietf.org/html/rfc7231}{http://tools.ietf.org/html/rfc7231}
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \enum HttpReply::HttpStatusCode
|
||||||
|
|
||||||
|
This enum type specifies the status code of a HTTP webserver reply.
|
||||||
|
|
||||||
|
\value Ok
|
||||||
|
The request was understood and everything is Ok.
|
||||||
|
\value Created
|
||||||
|
...
|
||||||
|
\value Accepted
|
||||||
|
...
|
||||||
|
\value NoContent
|
||||||
|
...
|
||||||
|
\value Found
|
||||||
|
...
|
||||||
|
\value BadRequest
|
||||||
|
...
|
||||||
|
\value Forbidden
|
||||||
|
...
|
||||||
|
\value NotFound
|
||||||
|
...
|
||||||
|
\value MethodNotAllowed
|
||||||
|
...
|
||||||
|
\value RequestTimeout
|
||||||
|
...
|
||||||
|
\value Conflict
|
||||||
|
...
|
||||||
|
\value InternalServerError
|
||||||
|
...
|
||||||
|
\value NotImplemented
|
||||||
|
...
|
||||||
|
\value BadGateway
|
||||||
|
...
|
||||||
|
\value ServiceUnavailable
|
||||||
|
...
|
||||||
|
\value GatewayTimeout
|
||||||
|
...
|
||||||
|
\value HttpVersionNotSupported
|
||||||
|
...
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \enum HttpReply::HttpHeaderType
|
||||||
|
|
||||||
|
This enum type specifies the known type of a header in a HTTP webserver reply.
|
||||||
|
|
||||||
|
\value ContentTypeHeader
|
||||||
|
The request was understood and everything is Ok.
|
||||||
|
\value ContentLenghtHeader
|
||||||
|
...
|
||||||
|
\value ConnectionHeader
|
||||||
|
...
|
||||||
|
\value LocationHeader
|
||||||
|
...
|
||||||
|
\value UserAgentHeader
|
||||||
|
...
|
||||||
|
\value CacheControlHeader
|
||||||
|
...
|
||||||
|
\value AllowHeader
|
||||||
|
...
|
||||||
|
\value DateHeader
|
||||||
|
...
|
||||||
|
\value ServerHeader
|
||||||
|
...
|
||||||
|
*/
|
||||||
|
|
||||||
#include "httpreply.h"
|
#include "httpreply.h"
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QPair>
|
#include <QPair>
|
||||||
|
|
||||||
// Note: RFC 7231 HTTP/1.1 Semantics and Content -> http://tools.ietf.org/html/rfc7231
|
/*! Construct a HttpReply with the given \a statusCode. */
|
||||||
|
|
||||||
HttpReply::HttpReply(const HttpStatusCode &statusCode) :
|
HttpReply::HttpReply(const HttpStatusCode &statusCode) :
|
||||||
m_statusCode(statusCode),
|
m_statusCode(statusCode),
|
||||||
m_payload(QByteArray())
|
m_payload(QByteArray())
|
||||||
{
|
{
|
||||||
// set known headers
|
// set known headers
|
||||||
//setHeader(HeaderType::ContentTypeHeader, "application/x-www-form-urlencoded; charset=\"utf-8\"");
|
setHeader(HttpHeaderType::ServerHeader, "guh/" + QByteArray(GUH_VERSION_STRING));
|
||||||
setHeader(HeaderType::ServerHeader, "guh/" + QByteArray(GUH_VERSION_STRING));
|
setHeader(HttpHeaderType::UserAgentHeader, "guh/" + QByteArray(REST_API_VERSION));
|
||||||
setHeader(HeaderType::UserAgentHeader, "guh/" + QByteArray(REST_API_VERSION));
|
setHeader(HttpHeaderType::DateHeader, QDateTime::currentDateTime().toString("ddd, dd MMM yyyy hh:mm:ss").toUtf8() + " GMT");
|
||||||
setHeader(HeaderType::DateHeader, QDateTime::currentDateTime().toString("ddd, dd MMM yyyy hh:mm:ss").toUtf8() + " GMT");
|
setHeader(HttpHeaderType::CacheControlHeader, "no-cache");
|
||||||
setHeader(HeaderType::CacheControlHeader, "no-cache");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Set the \a statusCode for this \l{HttpReply}.*/
|
||||||
void HttpReply::setHttpStatusCode(const HttpReply::HttpStatusCode &statusCode)
|
void HttpReply::setHttpStatusCode(const HttpReply::HttpStatusCode &statusCode)
|
||||||
{
|
{
|
||||||
m_statusCode = statusCode;
|
m_statusCode = statusCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Returns the status code of this \l{HttpReply}.*/
|
||||||
HttpReply::HttpStatusCode HttpReply::httpStatusCode() const
|
HttpReply::HttpStatusCode HttpReply::httpStatusCode() const
|
||||||
{
|
{
|
||||||
return m_statusCode;
|
return m_statusCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Set the payload of this \l{HttpReply} to the given \a data.*/
|
||||||
void HttpReply::setPayload(const QByteArray &data)
|
void HttpReply::setPayload(const QByteArray &data)
|
||||||
{
|
{
|
||||||
m_payload = data;
|
m_payload = data;
|
||||||
setHeader(HeaderType::ContentLenghtHeader, QByteArray::number(data.length()));
|
setHeader(HttpHeaderType::ContentLenghtHeader, QByteArray::number(data.length()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Returns the payload of this \l{HttpReply}.*/
|
||||||
QByteArray HttpReply::payload() const
|
QByteArray HttpReply::payload() const
|
||||||
{
|
{
|
||||||
return m_payload;
|
return m_payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! This method appends a raw header to the header list of this \l{HttpReply}.
|
||||||
|
The Header will be set to \a headerType : \a value.
|
||||||
|
*/
|
||||||
void HttpReply::setRawHeader(const QByteArray headerType, const QByteArray &value)
|
void HttpReply::setRawHeader(const QByteArray headerType, const QByteArray &value)
|
||||||
{
|
{
|
||||||
// if the header is already set, overwrite it
|
// if the header is already set, overwrite it
|
||||||
if (m_rawHeaderList.keys().contains(headerType)) {
|
if (m_rawHeaderList.keys().contains(headerType)) {
|
||||||
m_rawHeaderList.remove(headerType);
|
m_rawHeaderList.remove(headerType);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_rawHeaderList.insert(headerType, value);
|
m_rawHeaderList.insert(headerType, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpReply::setHeader(const HttpReply::HeaderType &headerType, const QByteArray &value)
|
/*! This method appends a known header to the header list of this \l{HttpReply}.
|
||||||
|
The Header will be set to \a headerType : \a value.
|
||||||
|
*/
|
||||||
|
void HttpReply::setHeader(const HttpHeaderType &headerType, const QByteArray &value)
|
||||||
{
|
{
|
||||||
setRawHeader(getHeaderType(headerType), value);
|
setRawHeader(getHeaderType(headerType), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Returns the list of all set headers in this \l{HttpReply}.*/
|
||||||
QHash<QByteArray, QByteArray> HttpReply::rawHeaderList() const
|
QHash<QByteArray, QByteArray> HttpReply::rawHeaderList() const
|
||||||
{
|
{
|
||||||
return m_rawHeaderList;
|
return m_rawHeaderList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Returns the raw headers of this \l{HttpReply}.
|
||||||
|
\note The header will be empty until the method \l{packReply()} was called.
|
||||||
|
\sa packReply()
|
||||||
|
*/
|
||||||
QByteArray HttpReply::rawHeader() const
|
QByteArray HttpReply::rawHeader() const
|
||||||
{
|
{
|
||||||
return m_rawHeader;
|
return m_rawHeader;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HttpReply::isValid() const
|
/*! Returns true if the raw header and the payload of this \l{HttpReply} is empty.*/
|
||||||
{
|
|
||||||
// TODO: verify if header is valid and payload is valid
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HttpReply::isEmpty() const
|
bool HttpReply::isEmpty() const
|
||||||
{
|
{
|
||||||
return m_rawHeader.isEmpty() && m_payload.isEmpty() && m_rawHeaderList.isEmpty();
|
return m_rawHeader.isEmpty() && m_payload.isEmpty() && m_rawHeaderList.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Clears all data of this \l{HttpReply}. */
|
||||||
void HttpReply::clear()
|
void HttpReply::clear()
|
||||||
{
|
{
|
||||||
m_rawHeader.clear();
|
m_rawHeader.clear();
|
||||||
@ -101,6 +188,7 @@ void HttpReply::clear()
|
|||||||
m_rawHeaderList.clear();
|
m_rawHeaderList.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Returns the whole reply data of this \l{HttpReply}. The data contain the HTTP header and the payload. */
|
||||||
QByteArray HttpReply::packReply()
|
QByteArray HttpReply::packReply()
|
||||||
{
|
{
|
||||||
// set status code
|
// set status code
|
||||||
@ -159,7 +247,7 @@ QByteArray HttpReply::getHttpReasonPhrase(const HttpReply::HttpStatusCode &statu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray HttpReply::getHeaderType(const HttpReply::HeaderType &headerType)
|
QByteArray HttpReply::getHeaderType(const HttpReply::HttpHeaderType &headerType)
|
||||||
{
|
{
|
||||||
switch (headerType) {
|
switch (headerType) {
|
||||||
case ContentTypeHeader:
|
case ContentTypeHeader:
|
||||||
|
|||||||
@ -50,7 +50,7 @@ public:
|
|||||||
HttpVersionNotSupported = 505
|
HttpVersionNotSupported = 505
|
||||||
};
|
};
|
||||||
|
|
||||||
enum HeaderType {
|
enum HttpHeaderType {
|
||||||
ContentTypeHeader,
|
ContentTypeHeader,
|
||||||
ContentLenghtHeader,
|
ContentLenghtHeader,
|
||||||
ConnectionHeader,
|
ConnectionHeader,
|
||||||
@ -71,13 +71,11 @@ public:
|
|||||||
QByteArray payload() const;
|
QByteArray payload() const;
|
||||||
|
|
||||||
void setRawHeader(const QByteArray headerType, const QByteArray &value);
|
void setRawHeader(const QByteArray headerType, const QByteArray &value);
|
||||||
void setHeader(const HeaderType &headerType, const QByteArray &value);
|
void setHeader(const HttpHeaderType &headerType, const QByteArray &value);
|
||||||
QHash<QByteArray, QByteArray> rawHeaderList() const;
|
QHash<QByteArray, QByteArray> rawHeaderList() const;
|
||||||
QByteArray rawHeader() const;
|
QByteArray rawHeader() const;
|
||||||
|
|
||||||
bool isValid() const;
|
|
||||||
bool isEmpty() const;
|
bool isEmpty() const;
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
QByteArray packReply();
|
QByteArray packReply();
|
||||||
@ -90,7 +88,7 @@ private:
|
|||||||
QHash<QByteArray, QByteArray> m_rawHeaderList;
|
QHash<QByteArray, QByteArray> m_rawHeaderList;
|
||||||
|
|
||||||
QByteArray getHttpReasonPhrase(const HttpStatusCode &statusCode);
|
QByteArray getHttpReasonPhrase(const HttpStatusCode &statusCode);
|
||||||
QByteArray getHeaderType(const HeaderType &headerType);
|
QByteArray getHeaderType(const HttpHeaderType &headerType);
|
||||||
|
|
||||||
QByteArray packHeader() const;
|
QByteArray packHeader() const;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -157,7 +157,6 @@ void JsonRPCServer::setup()
|
|||||||
void JsonRPCServer::processData(const QUuid &clientId, const QString &targetNamespace, const QString &method, const QVariantMap &message)
|
void JsonRPCServer::processData(const QUuid &clientId, const QString &targetNamespace, const QString &method, const QVariantMap &message)
|
||||||
{
|
{
|
||||||
// Note: id, targetNamespace and method already checked in TcpServer
|
// Note: id, targetNamespace and method already checked in TcpServer
|
||||||
|
|
||||||
int commandId = message.value("id").toInt();
|
int commandId = message.value("id").toInt();
|
||||||
QVariantMap params = message.value("params").toMap();
|
QVariantMap params = message.value("params").toMap();
|
||||||
|
|
||||||
|
|||||||
@ -46,7 +46,8 @@ private slots:
|
|||||||
void checkAllowedMethodCall_data();
|
void checkAllowedMethodCall_data();
|
||||||
void checkAllowedMethodCall();
|
void checkAllowedMethodCall();
|
||||||
|
|
||||||
|
void getFiles_data();
|
||||||
|
void getFiles();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// for debugging
|
// for debugging
|
||||||
@ -113,7 +114,6 @@ void TestWebserver::checkAllowedMethodCall()
|
|||||||
QFETCH(QString, method);
|
QFETCH(QString, method);
|
||||||
QFETCH(int, expectedStatusCode);
|
QFETCH(int, expectedStatusCode);
|
||||||
|
|
||||||
|
|
||||||
QNetworkAccessManager *nam = new QNetworkAccessManager(this);
|
QNetworkAccessManager *nam = new QNetworkAccessManager(this);
|
||||||
QSignalSpy clientSpy(nam, SIGNAL(finished(QNetworkReply*)));
|
QSignalSpy clientSpy(nam, SIGNAL(finished(QNetworkReply*)));
|
||||||
|
|
||||||
@ -149,10 +149,50 @@ void TestWebserver::checkAllowedMethodCall()
|
|||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestWebserver::getFiles_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("query");
|
||||||
|
QTest::addColumn<int>("expectedStatusCode");
|
||||||
|
|
||||||
|
QTest::newRow("get /etc/passwd") << "/etc/passwd" << 404;
|
||||||
|
QTest::newRow("get /etc/guh/guhd.conf") << "/etc/guh/guhd.conf" << 404;
|
||||||
|
QTest::newRow("get /etc/sudoers") << "/etc/sudoers" << 404;
|
||||||
|
QTest::newRow("get /root/.ssh/id_rsa.pub") << "/root/.ssh/id_rsa.pub" << 404;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestWebserver::getFiles()
|
||||||
|
{
|
||||||
|
QFETCH(QString, query);
|
||||||
|
QFETCH(int, expectedStatusCode);
|
||||||
|
|
||||||
|
QNetworkAccessManager *nam = new QNetworkAccessManager(this);
|
||||||
|
QSignalSpy clientSpy(nam, SIGNAL(finished(QNetworkReply*)));
|
||||||
|
|
||||||
|
QNetworkRequest request;
|
||||||
|
request.setUrl(QUrl("http://localhost:3000" + query));
|
||||||
|
QNetworkReply *reply = nam->get(request);
|
||||||
|
|
||||||
|
clientSpy.wait(200);
|
||||||
|
QVERIFY2(clientSpy.count() == 1, "expected exactly 1 response from webserver");
|
||||||
|
|
||||||
|
printResponse(reply);
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
|
qDebug() << reply->readAll();
|
||||||
|
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(&ok);
|
||||||
|
QVERIFY2(ok, "Could not convert statuscode from response to int");
|
||||||
|
QCOMPARE(statusCode, expectedStatusCode);
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
void TestWebserver::printResponse(QNetworkReply *reply)
|
void TestWebserver::printResponse(QNetworkReply *reply)
|
||||||
{
|
{
|
||||||
qDebug() << "-------------------------------";
|
qDebug() << "-------------------------------";
|
||||||
qDebug() << "Response header:";
|
qDebug() << "Response header:";
|
||||||
|
qDebug() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
|
||||||
foreach (const QNetworkReply::RawHeaderPair &headerPair, reply->rawHeaderPairs()) {
|
foreach (const QNetworkReply::RawHeaderPair &headerPair, reply->rawHeaderPairs()) {
|
||||||
qDebug() << headerPair.first << ":" << headerPair.second;
|
qDebug() << headerPair.first << ":" << headerPair.second;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user