From c1ebbf6b1c659104928714ad43b526922325c4c8 Mon Sep 17 00:00:00 2001 From: Daniel Brunner <0xFEEDC0DE64@gmail.com> Date: Thu, 4 Oct 2018 23:34:08 +0200 Subject: [PATCH] Implemented content type header and better file handling --- plugins/fileserverplugin | 2 +- plugins/wifilampplugin | 2 +- webserverlib/httpclientconnection.cpp | 59 ++++++++++++++++++++++---- webserverlib/httpclientconnection.h | 4 ++ webserverlib/httpexception.cpp | 21 +++++++++ webserverlib/httpexception.h | 22 ++++++++++ webserverlib/httpnotfoundexception.cpp | 6 +++ webserverlib/httpnotfoundexception.h | 11 +++++ webserverlib/webapplication.cpp | 1 - webserverlib/weblistener.cpp | 53 ++++++++++++----------- webserverlib/webserverlib.pro | 8 +++- 11 files changed, 151 insertions(+), 38 deletions(-) create mode 100644 webserverlib/httpexception.cpp create mode 100644 webserverlib/httpexception.h create mode 100644 webserverlib/httpnotfoundexception.cpp create mode 100644 webserverlib/httpnotfoundexception.h diff --git a/plugins/fileserverplugin b/plugins/fileserverplugin index 2d33897..7f5df33 160000 --- a/plugins/fileserverplugin +++ b/plugins/fileserverplugin @@ -1 +1 @@ -Subproject commit 2d33897c2aa60fa4640de040b19951043bba1edc +Subproject commit 7f5df336e01f082ca312f7d2d013eb685115ff39 diff --git a/plugins/wifilampplugin b/plugins/wifilampplugin index e9e18f2..ec4396f 160000 --- a/plugins/wifilampplugin +++ b/plugins/wifilampplugin @@ -1 +1 @@ -Subproject commit e9e18f227f721a2374762b301c3e582e9d1e91d5 +Subproject commit ec4396f7a0740e68cd59512cfc29ee80b291381d diff --git a/webserverlib/httpclientconnection.cpp b/webserverlib/httpclientconnection.cpp index 63574a9..26f3869 100644 --- a/webserverlib/httpclientconnection.cpp +++ b/webserverlib/httpclientconnection.cpp @@ -2,10 +2,16 @@ #include #include +#include +#include +#include #include +#include #include "weblistener.h" +const int HttpClientConnection::m_bufferSize(1024*1024*4); + HttpClientConnection::HttpClientConnection(QTcpSocket &socket, WebListener &webServer) : QObject(&webServer), m_socket(socket), @@ -44,7 +50,7 @@ void HttpClientConnection::sendResponse(HttpResponse response, const QByteArray return; } - response.headers.insert(QStringLiteral("Content-Length"), QString::number(byteArray.length())); + response.headers.insert(HttpResponse::HEADER_CONTENTLENGTH, QString::number(byteArray.length())); sendResponse(response); m_socket.write(byteArray); m_state = RequestLine; @@ -73,9 +79,41 @@ void HttpClientConnection::sendResponse(HttpResponse response, std::unique_ptrisSequential()) throw std::runtime_error("sequental device not supported yet"); + response.headers.insert(HttpResponse::HEADER_CONTENTLENGTH, QString::number(device->size())); + m_sendingDeivce = std::move(device); - response.headers.insert(QStringLiteral("Content-Length"), QString::number(m_sendingDeivce->size())); + sendResponse(response); + + connect(&m_socket, &QIODevice::bytesWritten, this, &HttpClientConnection::bytesWritten); + bytesWritten(); + + m_state = SendingResponse; +} + +void HttpClientConnection::sendResponse(HttpResponse response, std::unique_ptr &&device) +{ + if(m_state != WaitingForResponse) + throw std::runtime_error("sending a response now is not allowed!"); + + if(!device->isReadable()) + throw std::runtime_error("device is not readable"); + + if(device->isSequential()) + throw std::runtime_error("sequental device not supported yet"); + + const QFileInfo fileInfo(device->fileName()); + + response.headers.insert(HttpResponse::HEADER_CONTENTLENGTH, QString::number(fileInfo.size() - device->pos())); + + { + static const QMimeDatabase mimeDatabse; + const auto mimeType = mimeDatabse.mimeTypeForFile(fileInfo); + if(mimeType.isValid()) + response.headers.insert(HttpResponse::HEADER_CONTENTTYPE, mimeType.name()); + } + + m_sendingDeivce = std::move(device); sendResponse(response); @@ -104,16 +142,16 @@ void HttpClientConnection::readyRead() { case RequestLine: { - auto parts = line.split(' '); + auto parts = line.split(QLatin1Char(' ')); if(parts.count() != 3) { m_socket.close(); return; } - m_request.method = parts.at(0); - m_request.path = parts.at(1); - m_request.protocol = parts.at(2); + m_request.method = parts.at(0).toUtf8(); + m_request.path = QUrl::fromPercentEncoding(parts.at(1).toUtf8()); + m_request.protocol = parts.at(2).toUtf8(); m_state = Headers; continue; @@ -148,7 +186,9 @@ void HttpClientConnection::readyRead() clearRequest(); } } + break; } + default: qt_noop(); } } return; @@ -169,13 +209,16 @@ void HttpClientConnection::readyRead() m_webListener.handleRequest(this, m_request); clearRequest(); } + + break; } + default: qt_noop(); } } void HttpClientConnection::bytesWritten() { - if(m_socket.bytesToWrite() >= 1024*1024*4) + if(m_socket.bytesToWrite() >= m_bufferSize) return; if(m_socket.bytesToWrite() == 0 && m_sendingDeivce->bytesAvailable() == 0) @@ -186,7 +229,7 @@ void HttpClientConnection::bytesWritten() return; } - auto buffer = m_sendingDeivce->read(1024*1024*4); + auto buffer = m_sendingDeivce->read(m_bufferSize); m_socket.write(buffer); } diff --git a/webserverlib/httpclientconnection.h b/webserverlib/httpclientconnection.h index 90c2649..3f69efe 100644 --- a/webserverlib/httpclientconnection.h +++ b/webserverlib/httpclientconnection.h @@ -11,6 +11,7 @@ #include "httprequest.h" class QTcpSocket; +class QFileDevice; class WebListener; @@ -18,6 +19,8 @@ class WEBSERVERLIB_EXPORT HttpClientConnection : public QObject { Q_OBJECT + static const int m_bufferSize; + public: explicit HttpClientConnection(QTcpSocket &socket, WebListener &webListener); @@ -25,6 +28,7 @@ public: void sendResponse(HttpResponse response, const QByteArray &byteArray); void sendResponse(HttpResponse response, const QString &string); void sendResponse(HttpResponse response, std::unique_ptr &&device); + void sendResponse(HttpResponse response, std::unique_ptr &&device); private Q_SLOTS: void readyRead(); diff --git a/webserverlib/httpexception.cpp b/webserverlib/httpexception.cpp new file mode 100644 index 0000000..fbbfd29 --- /dev/null +++ b/webserverlib/httpexception.cpp @@ -0,0 +1,21 @@ +#include "httpexception.h" + +HttpException::HttpException(const HttpRequest &httpRequest, const QString &what_arg) : + std::runtime_error(what_arg.toStdString()), m_httpRequest(httpRequest) +{ +} + +HttpException::HttpException(const HttpRequest &httpRequest, const std::string &what_arg) : + std::runtime_error(what_arg), m_httpRequest(httpRequest) +{ +} + +HttpException::HttpException(const HttpRequest &httpRequest, const char *what_arg) : + std::runtime_error(what_arg), m_httpRequest(httpRequest) +{ +} + +const HttpRequest &HttpException::httpRequest() const +{ + return m_httpRequest; +} diff --git a/webserverlib/httpexception.h b/webserverlib/httpexception.h new file mode 100644 index 0000000..fd8b745 --- /dev/null +++ b/webserverlib/httpexception.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include + +#include + +#include "httprequest.h" + +class HttpException : public std::runtime_error +{ +public: + explicit HttpException(const HttpRequest &httpRequest, const QString &what_arg); + explicit HttpException(const HttpRequest &httpRequest, const std::string &what_arg); + explicit HttpException(const HttpRequest &httpRequest, const char *what_arg); + + const HttpRequest &httpRequest() const; + +private: + HttpRequest m_httpRequest; +}; diff --git a/webserverlib/httpnotfoundexception.cpp b/webserverlib/httpnotfoundexception.cpp new file mode 100644 index 0000000..ad964ac --- /dev/null +++ b/webserverlib/httpnotfoundexception.cpp @@ -0,0 +1,6 @@ +#include "httpnotfoundexception.h" + +HttpNotFoundException::HttpNotFoundException(const HttpRequest &httpRequest) : + HttpException(httpRequest, QString("File not found: %0").arg(httpRequest.path)) +{ +} diff --git a/webserverlib/httpnotfoundexception.h b/webserverlib/httpnotfoundexception.h new file mode 100644 index 0000000..e0fad0a --- /dev/null +++ b/webserverlib/httpnotfoundexception.h @@ -0,0 +1,11 @@ +#pragma once + +#include "httpexception.h" + +#include "httprequest.h" + +class HttpNotFoundException : public HttpException +{ +public: + explicit HttpNotFoundException(const HttpRequest &httpRequest); +}; diff --git a/webserverlib/webapplication.cpp b/webserverlib/webapplication.cpp index 2a17369..90d90ab 100644 --- a/webserverlib/webapplication.cpp +++ b/webserverlib/webapplication.cpp @@ -3,5 +3,4 @@ WebApplication::WebApplication(QObject *parent) : QObject(parent) { - } diff --git a/webserverlib/weblistener.cpp b/webserverlib/weblistener.cpp index fd305f8..c809061 100644 --- a/webserverlib/weblistener.cpp +++ b/webserverlib/weblistener.cpp @@ -12,6 +12,8 @@ #include "webserver.h" #include "httpclientconnection.h" #include "webapplication.h" +#include "httpnotfoundexception.h" +#include "httpexception.h" WebListener::WebListener(const QJsonObject &config, WebServer &webServer) : QObject(&webServer), m_webServer(webServer) @@ -82,37 +84,38 @@ void WebListener::handleRequest(HttpClientConnection *connection, const HttpRequ host = hostHeaderIter.value(); } - if(host.isEmpty()) - { - const auto iter = m_hosts.find(QStringLiteral("*")); + try { + if(host.isEmpty()) + { + const auto iter = m_hosts.find(QStringLiteral("*")); + if(iter == m_hosts.constEnd()) + throw HttpException(request, tr("Your request didn't contain a Host header and there is no fallback host configured!")); + + iter.value()->handleRequest(connection, request); + return; + } + + auto iter = m_hosts.find(host); 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 = m_hosts.find(QStringLiteral("*")); + if(iter == m_hosts.constEnd()) + throw HttpException(request, tr("Your requested Host \"%0\" is unknown and there is no fallback host configured!").arg(host)); } iter.value()->handleRequest(connection, request); - return; + } catch (const HttpException &ex) { + HttpResponse response; + response.protocol = ex.httpRequest().protocol; + response.statusCode = HttpResponse::StatusCode::BadRequest; + connection->sendResponse(response, QByteArray(ex.what())); + // this could cause endless loop with exceptions in HttpClientConnection::sendResponse + //} catch (const std::exception &ex) { + // HttpResponse response; + // response.protocol = QStringLiteral("HTTP/1.1"); + // response.statusCode = HttpResponse::StatusCode::BadRequest; + // connection->sendResponse(response, QByteArray(ex.what())); } - - 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/webserverlib.pro b/webserverlib/webserverlib.pro index 2d42e76..aa951be 100644 --- a/webserverlib/webserverlib.pro +++ b/webserverlib/webserverlib.pro @@ -14,7 +14,9 @@ SOURCES += \ httpclientconnection.cpp \ httprequest.cpp \ httpresponse.cpp \ - webserver.cpp + webserver.cpp \ + httpexception.cpp \ + httpnotfoundexception.cpp HEADERS += webserverlib_global.h \ weblistener.h \ @@ -23,7 +25,9 @@ HEADERS += webserverlib_global.h \ httpclientconnection.h \ httprequest.h \ httpresponse.h \ - webserver.h + webserver.h \ + httpexception.h \ + httpnotfoundexception.h FORMS +=