diff --git a/webserverlib/httpclientconnection.h b/webserverlib/httpclientconnection.h index 2147003..e94a402 100644 --- a/webserverlib/httpclientconnection.h +++ b/webserverlib/httpclientconnection.h @@ -1,17 +1,20 @@ #pragma once +#include "webserverlib_global.h" #include + #include #include -#include "httpcontainers.h" +#include "httpresponse.h" +#include "httprequest.h" class QTcpSocket; class WebListener; -class HttpClientConnection : public QObject +class WEBSERVERLIB_EXPORT HttpClientConnection : public QObject { Q_OBJECT diff --git a/webserverlib/httprequest.cpp b/webserverlib/httprequest.cpp new file mode 100644 index 0000000..048cb22 --- /dev/null +++ b/webserverlib/httprequest.cpp @@ -0,0 +1,34 @@ +#include "httprequest.h" + +const QString HttpRequest::HEADER_ACCEPT(QStringLiteral("Accept")); +const QString HttpRequest::HEADER_ACCEPTCHARSET(QStringLiteral("Accept-Charset")); +const QString HttpRequest::HEADER_ACCEPTENCODING(QStringLiteral("Accept-Encoding")); +const QString HttpRequest::HEADER_ACCEPTLANGUAGE(QStringLiteral("Accept-Language")); +const QString HttpRequest::HEADER_AUTHORIZATION(QStringLiteral("Authorization")); +const QString HttpRequest::HEADER_CACHECONTROL(QStringLiteral("Cache-Control")); +const QString HttpRequest::HEADER_CONNECTION(QStringLiteral("Connection")); +const QString HttpRequest::HEADER_COOKIE(QStringLiteral("Cookie")); +const QString HttpRequest::HEADER_CONTENTLENGTH(QStringLiteral("Content-Length")); +const QString HttpRequest::HEADER_CONTENTMD5(QStringLiteral("Content-MD5")); +const QString HttpRequest::HEADER_CONTENTTYPE(QStringLiteral("Content-Type")); +const QString HttpRequest::HEADER_DATE(QStringLiteral("Date")); +const QString HttpRequest::HEADER_EXPECT(QStringLiteral("Expect")); +const QString HttpRequest::HEADER_FORWARDED(QStringLiteral("Forwarded")); +const QString HttpRequest::HEADER_FROM(QStringLiteral("From")); +const QString HttpRequest::HEADER_HOST(QStringLiteral("Host")); +const QString HttpRequest::HEADER_IFMATCH(QStringLiteral("If-Match")); +const QString HttpRequest::HEADER_IFMODIFIEDSINCE(QStringLiteral("If-Modified-Since")); +const QString HttpRequest::HEADER_IFNONEMATCH(QStringLiteral("If-None-Match")); +const QString HttpRequest::HEADER_IFRANGE(QStringLiteral("If-Range")); +const QString HttpRequest::HEADER_IFUNMODIFIEDSINCE(QStringLiteral("If-Unmodified-Since")); +const QString HttpRequest::HEADER_MAXFORWARDS(QStringLiteral("Max-Forwards")); +const QString HttpRequest::HEADER_PRAGMA(QStringLiteral("Pragma")); +const QString HttpRequest::HEADER_PROXYAUTHORIZATION(QStringLiteral("Proxy-Authorization")); +const QString HttpRequest::HEADER_RANGE(QStringLiteral("Range")); +const QString HttpRequest::HEADER_REFERER(QStringLiteral("Referer")); +const QString HttpRequest::HEADER_TE(QStringLiteral("TE")); +const QString HttpRequest::HEADER_TRANSFERENCODING(QStringLiteral("Transfer-Encoding")); +const QString HttpRequest::HEADER_UPGRADE(QStringLiteral("Upgrade")); +const QString HttpRequest::HEADER_USERAGENT(QStringLiteral("User-Agent")); +const QString HttpRequest::HEADER_VIA(QStringLiteral("Via")); +const QString HttpRequest::HEADER_WARNING(QStringLiteral("Warning")); diff --git a/webserverlib/httprequest.h b/webserverlib/httprequest.h new file mode 100644 index 0000000..2144264 --- /dev/null +++ b/webserverlib/httprequest.h @@ -0,0 +1,48 @@ +#pragma once + +#include "webserverlib_global.h" + +#include +#include +#include + +struct WEBSERVERLIB_EXPORT HttpRequest { + QString method; + QString path; + QString protocol; + QHash headers; + QByteArray body; + + static const QString HEADER_ACCEPT; + static const QString HEADER_ACCEPTCHARSET; + static const QString HEADER_ACCEPTENCODING; + static const QString HEADER_ACCEPTLANGUAGE; + static const QString HEADER_AUTHORIZATION; + static const QString HEADER_CACHECONTROL; + static const QString HEADER_CONNECTION; + static const QString HEADER_COOKIE; + static const QString HEADER_CONTENTLENGTH; + static const QString HEADER_CONTENTMD5; + static const QString HEADER_CONTENTTYPE; + static const QString HEADER_DATE; + static const QString HEADER_EXPECT; + static const QString HEADER_FORWARDED; + static const QString HEADER_FROM; + static const QString HEADER_HOST; + static const QString HEADER_IFMATCH; + static const QString HEADER_IFMODIFIEDSINCE; + static const QString HEADER_IFNONEMATCH; + static const QString HEADER_IFRANGE; + static const QString HEADER_IFUNMODIFIEDSINCE; + static const QString HEADER_MAXFORWARDS; + static const QString HEADER_PRAGMA; + static const QString HEADER_PROXYAUTHORIZATION; + static const QString HEADER_RANGE; + static const QString HEADER_REFERER; + static const QString HEADER_TE; + static const QString HEADER_TRANSFERENCODING; + static const QString HEADER_UPGRADE; + static const QString HEADER_USERAGENT; + static const QString HEADER_VIA; + static const QString HEADER_WARNING; +}; diff --git a/webserverlib/httpcontainers.cpp b/webserverlib/httpresponse.cpp similarity index 71% rename from webserverlib/httpcontainers.cpp rename to webserverlib/httpresponse.cpp index 273513c..5a81be1 100644 --- a/webserverlib/httpcontainers.cpp +++ b/webserverlib/httpresponse.cpp @@ -1,6 +1,11 @@ -#include "httpcontainers.h" +#include "httpresponse.h" QString HttpResponse::statusString() const +{ + return statusString(statusCode); +} + +QString HttpResponse::statusString(HttpResponse::StatusCode statusCode) { switch(statusCode) { case HttpResponse::StatusCode::Continue: return QStringLiteral("Continue"); @@ -73,3 +78,37 @@ QString HttpResponse::statusString() const case HttpResponse::StatusCode::NetworkAuthenticationRequired: return QStringLiteral("Network Authentication Required"); } } + +const QString HttpResponse::HEADER_ACCEPTRANGES(QStringLiteral("Accept-Ranges")); +const QString HttpResponse::HEADER_AGE(QStringLiteral("Age")); +const QString HttpResponse::HEADER_ALLOW(QStringLiteral("Allow")); +const QString HttpResponse::HEADER_CACHECONTROL(QStringLiteral("Cache-Control")); +const QString HttpResponse::HEADER_CONNECTION(QStringLiteral("Connection")); +const QString HttpResponse::HEADER_CONTENTENCODING(QStringLiteral("Content-Encoding")); +const QString HttpResponse::HEADER_CONTENTLANGUAGE(QStringLiteral("Content-Language")); +const QString HttpResponse::HEADER_CONTENTLENGTH(QStringLiteral("Content-Length")); +const QString HttpResponse::HEADER_CONTENTLOCATION(QStringLiteral("Content-Location")); +const QString HttpResponse::HEADER_CONTENTMD5(QStringLiteral("Content-MD5")); +const QString HttpResponse::HEADER_CONTENTDISPOSITION(QStringLiteral("Content-Disposition")); +const QString HttpResponse::HEADER_CONTENTRANGE(QStringLiteral("Content-Range")); +const QString HttpResponse::HEADER_CONTENTSECURITYPOLICY(QStringLiteral("Content-Security-Policy")); +const QString HttpResponse::HEADER_CONTENTTYPE(QStringLiteral("Content-Type")); +const QString HttpResponse::HEADER_DATE(QStringLiteral("Date")); +const QString HttpResponse::HEADER_ETAG(QStringLiteral("ETag")); +const QString HttpResponse::HEADER_EXPIRES(QStringLiteral("Expires")); +const QString HttpResponse::HEADER_LASTMODIFIED(QStringLiteral("Last-Modified")); +const QString HttpResponse::HEADER_LINK(QStringLiteral("Link")); +const QString HttpResponse::HEADER_LOCATION(QStringLiteral("Location")); +const QString HttpResponse::HEADER_P3P(QStringLiteral("P3P")); +const QString HttpResponse::HEADER_PRAGMA(QStringLiteral("Pragma")); +const QString HttpResponse::HEADER_PROXYAUTHENTICATE(QStringLiteral("Proxy-Authenticate")); +const QString HttpResponse::HEADER_REFRESH(QStringLiteral("Refresh")); +const QString HttpResponse::HEADER_RETRYAFTER(QStringLiteral("Retry-After")); +const QString HttpResponse::HEADER_SERVER(QStringLiteral("Server")); +const QString HttpResponse::HEADER_SETCOOKIE(QStringLiteral("Set-Cookie")); +const QString HttpResponse::HEADER_TRAILER(QStringLiteral("Trailer")); +const QString HttpResponse::HEADER_TRANSFERENCODING(QStringLiteral("Transfer-Encoding")); +const QString HttpResponse::HEADER_VARY(QStringLiteral("Vary")); +const QString HttpResponse::HEADER_VIA(QStringLiteral("Via")); +const QString HttpResponse::HEADER_WARNING(QStringLiteral("Warning")); +const QString HttpResponse::HEADER_WWWAUTHENTICATE(QStringLiteral("WWW-Authenticate")); diff --git a/webserverlib/httpcontainers.h b/webserverlib/httpresponse.h similarity index 80% rename from webserverlib/httpcontainers.h rename to webserverlib/httpresponse.h index 615349f..e6919bc 100644 --- a/webserverlib/httpcontainers.h +++ b/webserverlib/httpresponse.h @@ -1,18 +1,11 @@ #pragma once +#include "webserverlib_global.h" + #include #include -#include -struct HttpRequest { - QString method; - QString path; - QString protocol; - QHash headers; - QByteArray body; -}; - -struct HttpResponse { +struct WEBSERVERLIB_EXPORT HttpResponse { enum class StatusCode { // 1xx Informational responses Continue = 100, // Continue @@ -98,4 +91,39 @@ struct HttpResponse { QHash headers; QString statusString() const; + static QString statusString(StatusCode statusCode); + + static const QString HEADER_ACCEPTRANGES; + static const QString HEADER_AGE; + static const QString HEADER_ALLOW; + static const QString HEADER_CACHECONTROL; + static const QString HEADER_CONNECTION; + static const QString HEADER_CONTENTENCODING; + static const QString HEADER_CONTENTLANGUAGE; + static const QString HEADER_CONTENTLENGTH; + static const QString HEADER_CONTENTLOCATION; + static const QString HEADER_CONTENTMD5; + static const QString HEADER_CONTENTDISPOSITION; + static const QString HEADER_CONTENTRANGE; + static const QString HEADER_CONTENTSECURITYPOLICY; + static const QString HEADER_CONTENTTYPE; + static const QString HEADER_DATE; + static const QString HEADER_ETAG; + static const QString HEADER_EXPIRES; + static const QString HEADER_LASTMODIFIED; + static const QString HEADER_LINK; + static const QString HEADER_LOCATION; + static const QString HEADER_P3P; + static const QString HEADER_PRAGMA; + static const QString HEADER_PROXYAUTHENTICATE; + static const QString HEADER_REFRESH; + static const QString HEADER_RETRYAFTER; + static const QString HEADER_SERVER; + static const QString HEADER_SETCOOKIE; + static const QString HEADER_TRAILER; + static const QString HEADER_TRANSFERENCODING; + static const QString HEADER_VARY; + static const QString HEADER_VIA; + static const QString HEADER_WARNING; + static const QString HEADER_WWWAUTHENTICATE; }; diff --git a/webserverlib/webapplication.h b/webserverlib/webapplication.h index 0751176..5d06184 100644 --- a/webserverlib/webapplication.h +++ b/webserverlib/webapplication.h @@ -3,6 +3,9 @@ #include #include "webserverlib_global.h" +class HttpClientConnection; +class HttpRequest; + class WEBSERVERLIB_EXPORT WebApplication : public QObject { Q_OBJECT @@ -11,4 +14,6 @@ public: WebApplication(QObject *parent = Q_NULLPTR); virtual void start() = 0; + + virtual void handleRequest(HttpClientConnection *connection, const HttpRequest &request) = 0; }; diff --git a/webserverlib/weblistener.cpp b/webserverlib/weblistener.cpp index a923f0f..52da686 100644 --- a/webserverlib/weblistener.cpp +++ b/webserverlib/weblistener.cpp @@ -9,6 +9,7 @@ #include "utils/netutils.h" #include "httpclientconnection.h" +#include "webapplication.h" WebListener::WebListener(const QJsonObject &config, const QHash &applications, QObject *parent) : QObject(parent) @@ -55,7 +56,7 @@ WebListener::WebListener(const QJsonObject &config, const QHashsendResponse(response, request.path); + QString host; + + { + const auto hostHeaderIter = qAsConst(request.headers).find(HttpRequest::HEADER_HOST); + if(hostHeaderIter != request.headers.constEnd()) + host = hostHeaderIter.value(); + } + + if(host.isEmpty()) + { + const auto iter = m_hosts.find(QStringLiteral("*")); + if(iter == m_hosts.constEnd()) + { + HttpResponse response; + response.protocol = request.protocol; + response.statusCode = HttpResponse::StatusCode::BadRequest; + connection->sendResponse(response, tr("Your request didn't contain a Host header and there is no fallback host configured!")); + return; + } + + iter.value()->handleRequest(connection, request); + return; + } + + auto iter = m_hosts.find(host); + if(iter == m_hosts.constEnd()) + { + iter = m_hosts.find(QStringLiteral("*")); + if(iter == m_hosts.constEnd()) + { + HttpResponse response; + response.protocol = request.protocol; + response.statusCode = HttpResponse::StatusCode::BadRequest; + connection->sendResponse(response, tr("Your requested Host \"%0\" is unknown and there is no fallback host configured!")); + return; + } + } + + iter.value()->handleRequest(connection, request); } void WebListener::acceptError(QAbstractSocket::SocketError socketError) diff --git a/webserverlib/weblistener.h b/webserverlib/weblistener.h index 223216c..8d9c4de 100644 --- a/webserverlib/weblistener.h +++ b/webserverlib/weblistener.h @@ -5,9 +5,9 @@ #include #include +#include class QJsonObject; -template class QHash; class QTcpServer; class WebApplication; @@ -29,7 +29,10 @@ private Q_SLOTS: void newConnection(); private: + using HostsContainer = QHash; + QTcpServer *m_tcpServer; QHostAddress m_address; quint16 m_port; + HostsContainer m_hosts; }; diff --git a/webserverlib/webserverlib.pro b/webserverlib/webserverlib.pro index a5b2d68..0b0ab3f 100644 --- a/webserverlib/webserverlib.pro +++ b/webserverlib/webserverlib.pro @@ -12,14 +12,16 @@ SOURCES += \ webapplication.cpp \ webplugin.cpp \ httpclientconnection.cpp \ - httpcontainers.cpp + httprequest.cpp \ + httpresponse.cpp HEADERS += webserverlib_global.h \ weblistener.h \ webapplication.h \ webplugin.h \ httpclientconnection.h \ - httpcontainers.h + httprequest.h \ + httpresponse.h FORMS +=