add some tests and add documentation to httpreply

pull/135/head
Simon Stürz 2015-07-17 14:17:41 +02:00 committed by Michael Zanetti
parent 27a8db73d8
commit 478c832ec8
4 changed files with 150 additions and 25 deletions

View File

@ -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 <QDateTime>
#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) :
m_statusCode(statusCode),
m_payload(QByteArray())
{
// set known headers
//setHeader(HeaderType::ContentTypeHeader, "application/x-www-form-urlencoded; charset=\"utf-8\"");
setHeader(HeaderType::ServerHeader, "guh/" + QByteArray(GUH_VERSION_STRING));
setHeader(HeaderType::UserAgentHeader, "guh/" + QByteArray(REST_API_VERSION));
setHeader(HeaderType::DateHeader, QDateTime::currentDateTime().toString("ddd, dd MMM yyyy hh:mm:ss").toUtf8() + " GMT");
setHeader(HeaderType::CacheControlHeader, "no-cache");
setHeader(HttpHeaderType::ServerHeader, "guh/" + QByteArray(GUH_VERSION_STRING));
setHeader(HttpHeaderType::UserAgentHeader, "guh/" + QByteArray(REST_API_VERSION));
setHeader(HttpHeaderType::DateHeader, QDateTime::currentDateTime().toString("ddd, dd MMM yyyy hh:mm:ss").toUtf8() + " GMT");
setHeader(HttpHeaderType::CacheControlHeader, "no-cache");
}
/*! Set the \a statusCode for this \l{HttpReply}.*/
void HttpReply::setHttpStatusCode(const HttpReply::HttpStatusCode &statusCode)
{
m_statusCode = statusCode;
}
/*! Returns the status code of this \l{HttpReply}.*/
HttpReply::HttpStatusCode HttpReply::httpStatusCode() const
{
return m_statusCode;
}
/*! Set the payload of this \l{HttpReply} to the given \a data.*/
void HttpReply::setPayload(const QByteArray &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
{
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)
{
// if the header is already set, overwrite it
if (m_rawHeaderList.keys().contains(headerType)) {
m_rawHeaderList.remove(headerType);
}
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);
}
/*! Returns the list of all set headers in this \l{HttpReply}.*/
QHash<QByteArray, QByteArray> HttpReply::rawHeaderList() const
{
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
{
return m_rawHeader;
}
bool HttpReply::isValid() const
{
// TODO: verify if header is valid and payload is valid
return true;
}
/*! Returns true if the raw header and the payload of this \l{HttpReply} is empty.*/
bool HttpReply::isEmpty() const
{
return m_rawHeader.isEmpty() && m_payload.isEmpty() && m_rawHeaderList.isEmpty();
}
/*! Clears all data of this \l{HttpReply}. */
void HttpReply::clear()
{
m_rawHeader.clear();
@ -101,6 +188,7 @@ void HttpReply::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()
{
// 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) {
case ContentTypeHeader:

View File

@ -50,7 +50,7 @@ public:
HttpVersionNotSupported = 505
};
enum HeaderType {
enum HttpHeaderType {
ContentTypeHeader,
ContentLenghtHeader,
ConnectionHeader,
@ -71,13 +71,11 @@ public:
QByteArray payload() const;
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;
QByteArray rawHeader() const;
bool isValid() const;
bool isEmpty() const;
void clear();
QByteArray packReply();
@ -90,7 +88,7 @@ private:
QHash<QByteArray, QByteArray> m_rawHeaderList;
QByteArray getHttpReasonPhrase(const HttpStatusCode &statusCode);
QByteArray getHeaderType(const HeaderType &headerType);
QByteArray getHeaderType(const HttpHeaderType &headerType);
QByteArray packHeader() const;
};

View File

@ -157,7 +157,6 @@ void JsonRPCServer::setup()
void JsonRPCServer::processData(const QUuid &clientId, const QString &targetNamespace, const QString &method, const QVariantMap &message)
{
// Note: id, targetNamespace and method already checked in TcpServer
int commandId = message.value("id").toInt();
QVariantMap params = message.value("params").toMap();

View File

@ -46,7 +46,8 @@ private slots:
void checkAllowedMethodCall_data();
void checkAllowedMethodCall();
void getFiles_data();
void getFiles();
private:
// for debugging
@ -113,7 +114,6 @@ void TestWebserver::checkAllowedMethodCall()
QFETCH(QString, method);
QFETCH(int, expectedStatusCode);
QNetworkAccessManager *nam = new QNetworkAccessManager(this);
QSignalSpy clientSpy(nam, SIGNAL(finished(QNetworkReply*)));
@ -149,10 +149,50 @@ void TestWebserver::checkAllowedMethodCall()
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)
{
qDebug() << "-------------------------------";
qDebug() << "Response header:";
qDebug() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
foreach (const QNetworkReply::RawHeaderPair &headerPair, reply->rawHeaderPairs()) {
qDebug() << headerPair.first << ":" << headerPair.second;
}