Implemented content type header and better file handling
This commit is contained in:
Submodule plugins/fileserverplugin updated: 2d33897c2a...7f5df336e0
Submodule plugins/wifilampplugin updated: e9e18f227f...ec4396f7a0
@@ -2,10 +2,16 @@
|
||||
|
||||
#include <QTcpSocket>
|
||||
#include <QTextStream>
|
||||
#include <QFileDevice>
|
||||
#include <QFileInfo>
|
||||
#include <QMimeDatabase>
|
||||
#include <QRegularExpression>
|
||||
#include <QUrl>
|
||||
|
||||
#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_ptr<Q
|
||||
if(device->isSequential())
|
||||
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<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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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<QIODevice> &&device);
|
||||
void sendResponse(HttpResponse response, std::unique_ptr<QFileDevice> &&device);
|
||||
|
||||
private Q_SLOTS:
|
||||
void readyRead();
|
||||
|
21
webserverlib/httpexception.cpp
Normal file
21
webserverlib/httpexception.cpp
Normal 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;
|
||||
}
|
22
webserverlib/httpexception.h
Normal file
22
webserverlib/httpexception.h
Normal 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;
|
||||
};
|
6
webserverlib/httpnotfoundexception.cpp
Normal file
6
webserverlib/httpnotfoundexception.cpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "httpnotfoundexception.h"
|
||||
|
||||
HttpNotFoundException::HttpNotFoundException(const HttpRequest &httpRequest) :
|
||||
HttpException(httpRequest, QString("File not found: %0").arg(httpRequest.path))
|
||||
{
|
||||
}
|
11
webserverlib/httpnotfoundexception.h
Normal file
11
webserverlib/httpnotfoundexception.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "httpexception.h"
|
||||
|
||||
#include "httprequest.h"
|
||||
|
||||
class HttpNotFoundException : public HttpException
|
||||
{
|
||||
public:
|
||||
explicit HttpNotFoundException(const HttpRequest &httpRequest);
|
||||
};
|
@@ -3,5 +3,4 @@
|
||||
WebApplication::WebApplication(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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 +=
|
||||
|
||||
|
Reference in New Issue
Block a user