Implemented content type header and better file handling

This commit is contained in:
Daniel Brunner
2018-10-04 23:34:08 +02:00
parent 8e12883e61
commit c1ebbf6b1c
11 changed files with 151 additions and 38 deletions

View File

@@ -2,10 +2,16 @@
#include <QTcpSocket> #include <QTcpSocket>
#include <QTextStream> #include <QTextStream>
#include <QFileDevice>
#include <QFileInfo>
#include <QMimeDatabase>
#include <QRegularExpression> #include <QRegularExpression>
#include <QUrl>
#include "weblistener.h" #include "weblistener.h"
const int HttpClientConnection::m_bufferSize(1024*1024*4);
HttpClientConnection::HttpClientConnection(QTcpSocket &socket, WebListener &webServer) : HttpClientConnection::HttpClientConnection(QTcpSocket &socket, WebListener &webServer) :
QObject(&webServer), QObject(&webServer),
m_socket(socket), m_socket(socket),
@@ -44,7 +50,7 @@ void HttpClientConnection::sendResponse(HttpResponse response, const QByteArray
return; return;
} }
response.headers.insert(QStringLiteral("Content-Length"), QString::number(byteArray.length())); response.headers.insert(HttpResponse::HEADER_CONTENTLENGTH, QString::number(byteArray.length()));
sendResponse(response); sendResponse(response);
m_socket.write(byteArray); m_socket.write(byteArray);
m_state = RequestLine; m_state = RequestLine;
@@ -73,9 +79,41 @@ void HttpClientConnection::sendResponse(HttpResponse response, std::unique_ptr<Q
if(device->isSequential()) if(device->isSequential())
throw std::runtime_error("sequental device not supported yet"); throw std::runtime_error("sequental device not supported yet");
response.headers.insert(HttpResponse::HEADER_CONTENTLENGTH, QString::number(device->size()));
m_sendingDeivce = std::move(device); 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<QFileDevice> &&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); sendResponse(response);
@@ -104,16 +142,16 @@ void HttpClientConnection::readyRead()
{ {
case RequestLine: case RequestLine:
{ {
auto parts = line.split(' '); auto parts = line.split(QLatin1Char(' '));
if(parts.count() != 3) if(parts.count() != 3)
{ {
m_socket.close(); m_socket.close();
return; return;
} }
m_request.method = parts.at(0); m_request.method = parts.at(0).toUtf8();
m_request.path = parts.at(1); m_request.path = QUrl::fromPercentEncoding(parts.at(1).toUtf8());
m_request.protocol = parts.at(2); m_request.protocol = parts.at(2).toUtf8();
m_state = Headers; m_state = Headers;
continue; continue;
@@ -148,7 +186,9 @@ void HttpClientConnection::readyRead()
clearRequest(); clearRequest();
} }
} }
break;
} }
default: qt_noop();
} }
} }
return; return;
@@ -169,13 +209,16 @@ void HttpClientConnection::readyRead()
m_webListener.handleRequest(this, m_request); m_webListener.handleRequest(this, m_request);
clearRequest(); clearRequest();
} }
break;
} }
default: qt_noop();
} }
} }
void HttpClientConnection::bytesWritten() void HttpClientConnection::bytesWritten()
{ {
if(m_socket.bytesToWrite() >= 1024*1024*4) if(m_socket.bytesToWrite() >= m_bufferSize)
return; return;
if(m_socket.bytesToWrite() == 0 && m_sendingDeivce->bytesAvailable() == 0) if(m_socket.bytesToWrite() == 0 && m_sendingDeivce->bytesAvailable() == 0)
@@ -186,7 +229,7 @@ void HttpClientConnection::bytesWritten()
return; return;
} }
auto buffer = m_sendingDeivce->read(1024*1024*4); auto buffer = m_sendingDeivce->read(m_bufferSize);
m_socket.write(buffer); m_socket.write(buffer);
} }

View File

@@ -11,6 +11,7 @@
#include "httprequest.h" #include "httprequest.h"
class QTcpSocket; class QTcpSocket;
class QFileDevice;
class WebListener; class WebListener;
@@ -18,6 +19,8 @@ class WEBSERVERLIB_EXPORT HttpClientConnection : public QObject
{ {
Q_OBJECT Q_OBJECT
static const int m_bufferSize;
public: public:
explicit HttpClientConnection(QTcpSocket &socket, WebListener &webListener); explicit HttpClientConnection(QTcpSocket &socket, WebListener &webListener);
@@ -25,6 +28,7 @@ public:
void sendResponse(HttpResponse response, const QByteArray &byteArray); void sendResponse(HttpResponse response, const QByteArray &byteArray);
void sendResponse(HttpResponse response, const QString &string); void sendResponse(HttpResponse response, const QString &string);
void sendResponse(HttpResponse response, std::unique_ptr<QIODevice> &&device); void sendResponse(HttpResponse response, std::unique_ptr<QIODevice> &&device);
void sendResponse(HttpResponse response, std::unique_ptr<QFileDevice> &&device);
private Q_SLOTS: private Q_SLOTS:
void readyRead(); void readyRead();

View File

@@ -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;
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include <stdexcept>
#include <QString>
#include <string>
#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;
};

View File

@@ -0,0 +1,6 @@
#include "httpnotfoundexception.h"
HttpNotFoundException::HttpNotFoundException(const HttpRequest &httpRequest) :
HttpException(httpRequest, QString("File not found: %0").arg(httpRequest.path))
{
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include "httpexception.h"
#include "httprequest.h"
class HttpNotFoundException : public HttpException
{
public:
explicit HttpNotFoundException(const HttpRequest &httpRequest);
};

View File

@@ -3,5 +3,4 @@
WebApplication::WebApplication(QObject *parent) : WebApplication::WebApplication(QObject *parent) :
QObject(parent) QObject(parent)
{ {
} }

View File

@@ -12,6 +12,8 @@
#include "webserver.h" #include "webserver.h"
#include "httpclientconnection.h" #include "httpclientconnection.h"
#include "webapplication.h" #include "webapplication.h"
#include "httpnotfoundexception.h"
#include "httpexception.h"
WebListener::WebListener(const QJsonObject &config, WebServer &webServer) : WebListener::WebListener(const QJsonObject &config, WebServer &webServer) :
QObject(&webServer), m_webServer(webServer) QObject(&webServer), m_webServer(webServer)
@@ -82,37 +84,38 @@ void WebListener::handleRequest(HttpClientConnection *connection, const HttpRequ
host = hostHeaderIter.value(); host = hostHeaderIter.value();
} }
if(host.isEmpty()) try {
{ if(host.isEmpty())
const auto iter = m_hosts.find(QStringLiteral("*")); {
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()) if(iter == m_hosts.constEnd())
{ {
HttpResponse response; iter = m_hosts.find(QStringLiteral("*"));
response.protocol = request.protocol; if(iter == m_hosts.constEnd())
response.statusCode = HttpResponse::StatusCode::BadRequest; throw HttpException(request, tr("Your requested Host \"%0\" is unknown and there is no fallback host configured!").arg(host));
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); 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) void WebListener::acceptError(QAbstractSocket::SocketError socketError)

View File

@@ -14,7 +14,9 @@ SOURCES += \
httpclientconnection.cpp \ httpclientconnection.cpp \
httprequest.cpp \ httprequest.cpp \
httpresponse.cpp \ httpresponse.cpp \
webserver.cpp webserver.cpp \
httpexception.cpp \
httpnotfoundexception.cpp
HEADERS += webserverlib_global.h \ HEADERS += webserverlib_global.h \
weblistener.h \ weblistener.h \
@@ -23,7 +25,9 @@ HEADERS += webserverlib_global.h \
httpclientconnection.h \ httpclientconnection.h \
httprequest.h \ httprequest.h \
httpresponse.h \ httpresponse.h \
webserver.h webserver.h \
httpexception.h \
httpnotfoundexception.h
FORMS += FORMS +=