From 478c832ec804303a67f9b25d97bb6c66a4710f00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 17 Jul 2015 14:17:41 +0200 Subject: [PATCH] add some tests and add documentation to httpreply --- libguh/network/httpreply.cpp | 122 +++++++++++++++++++++---- libguh/network/httpreply.h | 8 +- server/jsonrpc/jsonrpcserver.cpp | 1 - tests/auto/webserver/testwebserver.cpp | 44 ++++++++- 4 files changed, 150 insertions(+), 25 deletions(-) diff --git a/libguh/network/httpreply.cpp b/libguh/network/httpreply.cpp index 856cea86..8d4a90dc 100644 --- a/libguh/network/httpreply.cpp +++ b/libguh/network/httpreply.cpp @@ -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 #include -// 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 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: diff --git a/libguh/network/httpreply.h b/libguh/network/httpreply.h index 7513f257..027f8551 100644 --- a/libguh/network/httpreply.h +++ b/libguh/network/httpreply.h @@ -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 rawHeaderList() const; QByteArray rawHeader() const; - bool isValid() const; bool isEmpty() const; - void clear(); QByteArray packReply(); @@ -90,7 +88,7 @@ private: QHash m_rawHeaderList; QByteArray getHttpReasonPhrase(const HttpStatusCode &statusCode); - QByteArray getHeaderType(const HeaderType &headerType); + QByteArray getHeaderType(const HttpHeaderType &headerType); QByteArray packHeader() const; }; diff --git a/server/jsonrpc/jsonrpcserver.cpp b/server/jsonrpc/jsonrpcserver.cpp index 39f390fd..d2c3eaed 100644 --- a/server/jsonrpc/jsonrpcserver.cpp +++ b/server/jsonrpc/jsonrpcserver.cpp @@ -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(); diff --git a/tests/auto/webserver/testwebserver.cpp b/tests/auto/webserver/testwebserver.cpp index 8989a978..06d99f14 100644 --- a/tests/auto/webserver/testwebserver.cpp +++ b/tests/auto/webserver/testwebserver.cpp @@ -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("query"); + QTest::addColumn("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; }