mirror of https://github.com/qt/qthttpserver.git
Introduce Qt HttpServer framework
Small, Qt integrated framework for creating specialized http server. Goals of the project: - Create a framework allowing creation of a specialized web server running in non public networks (home and company networks, stealth or hidden services) - Create an easy tool for developers to embed http servers in their apps. - Playground to narrow down problems in Qt, related to network stack, but also to explore general usability. - Potentially reduce code duplication in Qt. Not goals: - Standalone server, in particular not Apache or nginx replacement Change-Id: I0d8b83e50992b9a95c88f4735539329279cf5425 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
parent
050abe1f33
commit
b24745265d
|
@ -0,0 +1,3 @@
|
|||
[submodule "src/3rdparty/http-parser"]
|
||||
path = src/3rdparty/http-parser
|
||||
url = https://github.com/nodejs/http-parser.git
|
|
@ -0,0 +1,3 @@
|
|||
load(qt_build_config)
|
||||
|
||||
MODULE_VERSION = 5.12.0
|
|
@ -0,0 +1 @@
|
|||
load(qt_parts)
|
|
@ -0,0 +1 @@
|
|||
Subproject commit edeedb1b4d2f34e4c7d8045ac8b92adbc35e7ed7
|
|
@ -0,0 +1,6 @@
|
|||
NODEJS_HTTP_PARSER_PATH = $$PWD/http-parser
|
||||
|
||||
INCLUDEPATH += $$NODEJS_HTTP_PARSER_PATH
|
||||
|
||||
HEADERS += $$NODEJS_HTTP_PARSER_PATH/http_parser.h
|
||||
SOURCES += $$NODEJS_HTTP_PARSER_PATH/http_parser.c
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"Id": "http-parser",
|
||||
"Name": "HTTP request/response parser for C",
|
||||
"QDocModule": "qthttpserver",
|
||||
"QtUsage": "Used in Qt HttpServer.",
|
||||
|
||||
"License": "MIT License",
|
||||
"LicenseId": "MIT",
|
||||
"LicenseFile": "http-parser/LICENSE-MIT",
|
||||
"Copyright": "Copyright Joyent, Inc. and other Node contributors. All rights reserved."
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
TARGET = QtHttpServer
|
||||
INCLUDEPATH += .
|
||||
|
||||
QT += network core-private
|
||||
|
||||
qtHaveModule(websockets): QT += websockets-private
|
||||
|
||||
HEADERS += \
|
||||
qthttpserverglobal.h \
|
||||
qabstracthttpserver.h \
|
||||
qabstracthttpserver_p.h \
|
||||
qhttpserverrequest.h \
|
||||
qhttpserverrequest_p.h
|
||||
|
||||
SOURCES += \
|
||||
qabstracthttpserver.cpp \
|
||||
qhttpserverrequest.cpp
|
||||
|
||||
include(../3rdparty/http-parser.pri)
|
||||
|
||||
load(qt_module)
|
|
@ -0,0 +1,265 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtHttpServer/qabstracthttpserver.h>
|
||||
|
||||
#include <QtHttpServer/qhttpserverrequest.h>
|
||||
#include <private/qabstracthttpserver_p.h>
|
||||
#include <private/qhttpserverrequest_p.h>
|
||||
|
||||
#include <QtCore/qloggingcategory.h>
|
||||
#include <QtCore/qmetaobject.h>
|
||||
#include <QtNetwork/qtcpserver.h>
|
||||
|
||||
#include "http_parser.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
Q_LOGGING_CATEGORY(lcHttpServer, "qt.httpserver")
|
||||
|
||||
#if !defined(QT_NO_USERDATA)
|
||||
QAtomicInt QAbstractHttpServerPrivate::userDataId = -1;
|
||||
#endif
|
||||
|
||||
QAbstractHttpServerPrivate::QAbstractHttpServerPrivate()
|
||||
{
|
||||
#if !defined(QT_NO_USERDATA)
|
||||
userDataId.testAndSetRelaxed(-1, int(QObject::registerUserData()));
|
||||
#endif
|
||||
}
|
||||
|
||||
void QAbstractHttpServerPrivate::handleNewConnections()
|
||||
{
|
||||
auto tcpServer = qobject_cast<QTcpServer *>(currentSender->sender);
|
||||
Q_ASSERT(tcpServer);
|
||||
while (auto *socket = tcpServer->nextPendingConnection()) {
|
||||
auto request = new QHttpServerRequest; // TODO own tcp server could pre-allocate it
|
||||
http_parser_init(&request->d->httpParser, HTTP_REQUEST);
|
||||
connect(socket, &QTcpSocket::readyRead, this, &QAbstractHttpServerPrivate::handleReadyRead);
|
||||
socket->connect(socket, &QTcpSocket::disconnected, &QObject::deleteLater);
|
||||
#if !defined(QT_NO_USERDATA)
|
||||
socket->setUserData(uint(userDataId), request);
|
||||
#else
|
||||
QObject::connect(socket, &QTcpSocket::destroyed, [&]() {
|
||||
requests.remove(socket);
|
||||
});
|
||||
requests.insert(socket, request);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void QAbstractHttpServerPrivate::handleReadyRead()
|
||||
{
|
||||
Q_Q(QAbstractHttpServer);
|
||||
auto socket = qobject_cast<QTcpSocket *>(currentSender->sender);
|
||||
#if !defined(QT_NO_USERDATA)
|
||||
auto request = static_cast<QHttpServerRequest *>(socket->userData(uint(userDataId)));
|
||||
#else
|
||||
auto request = requests[socket];
|
||||
#endif
|
||||
auto &requestPrivate = request->d;
|
||||
|
||||
if (!socket->isTransactionStarted())
|
||||
socket->startTransaction();
|
||||
|
||||
if (!requestPrivate->parse(socket)) {
|
||||
socket->disconnect();
|
||||
return;
|
||||
}
|
||||
if (!requestPrivate->httpParser.upgrade &&
|
||||
requestPrivate->state != QHttpServerRequestPrivate::State::OnMessageComplete)
|
||||
return; // Partial read
|
||||
|
||||
if (requestPrivate->httpParser.upgrade) { // Upgrade
|
||||
const auto &headers = requestPrivate->headers;
|
||||
const auto upgradeHash = requestPrivate->headerHash(QStringLiteral("upgrade"));
|
||||
const auto it = headers.find(upgradeHash);
|
||||
if (it != headers.end()) {
|
||||
#if defined(QT_WEBSOCKETS_LIB)
|
||||
if (it.value().second.compare(QLatin1String("websocket"), Qt::CaseInsensitive) == 0) {
|
||||
static const auto signal = QMetaMethod::fromSignal(
|
||||
&QAbstractHttpServer::newWebSocketConnection);
|
||||
if (q->isSignalConnected(signal)) {
|
||||
disconnect(socket, &QTcpSocket::readyRead,
|
||||
this, &QAbstractHttpServerPrivate::handleReadyRead);
|
||||
socket->rollbackTransaction();
|
||||
websocketServer.handleConnection(socket);
|
||||
Q_EMIT socket->readyRead();
|
||||
} else {
|
||||
qWarning(lcHttpServer, "WebSocket received but no slots connected to "
|
||||
"QWebSocketServer::newConnection");
|
||||
socket->disconnectFromHost();
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
qCWarning(lcHttpServer, "Upgrade to %s not supported", qPrintable(it.value().second));
|
||||
socket->disconnectFromHost();
|
||||
} else {
|
||||
socket->commitTransaction();
|
||||
if (!q->handleRequest(*request, socket))
|
||||
Q_EMIT q->missingHandler(*request, socket);
|
||||
}
|
||||
}
|
||||
|
||||
QAbstractHttpServer::QAbstractHttpServer(QObject *parent)
|
||||
: QObject(*new QAbstractHttpServerPrivate, parent)
|
||||
{
|
||||
#if defined(QT_WEBSOCKETS_LIB)
|
||||
Q_D(QAbstractHttpServer);
|
||||
connect(&d->websocketServer, &QWebSocketServer::newConnection,
|
||||
this, &QAbstractHttpServer::newWebSocketConnection);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
Tries to bind a \c QTcpServer to \a address and \a port.
|
||||
|
||||
Returns \c true upon success, false otherwise.
|
||||
*/
|
||||
bool QAbstractHttpServer::listen(const QHostAddress &address, quint16 port)
|
||||
{
|
||||
auto tcpServer = new QTcpServer(this);
|
||||
const auto listening = tcpServer->listen(address, port);
|
||||
if (listening)
|
||||
bind(tcpServer);
|
||||
else
|
||||
delete tcpServer;
|
||||
return listening;
|
||||
}
|
||||
|
||||
/*!
|
||||
Bind the HTTP server to given TCP \a server over which
|
||||
the transmission happens. It is possible to call this function
|
||||
multiple times with different instances of TCP \a server to
|
||||
handle multiple connections and ports, for example both SSL and
|
||||
non-encrypted connections.
|
||||
|
||||
After calling this function, every _new_ connection will be
|
||||
handled and forwarded by the HTTP server.
|
||||
|
||||
It is the user's responsibility to call QTcpServer::listen() on
|
||||
the \a server.
|
||||
|
||||
If the \a server is null, then a new, default-constructed TCP
|
||||
server will be constructed, which will be listening on a random
|
||||
port and all interfaces.
|
||||
|
||||
The \a server will be parented to this HTTP server.
|
||||
|
||||
\sa QTcpServer, QTcpServer::listen()
|
||||
*/
|
||||
void QAbstractHttpServer::bind(QTcpServer *server)
|
||||
{
|
||||
Q_D(QAbstractHttpServer);
|
||||
if (!server) {
|
||||
server = new QTcpServer(this);
|
||||
if (!server->listen()) {
|
||||
qCCritical(lcHttpServer, "QTcpServer listen failed (%s)",
|
||||
qPrintable(server->errorString()));
|
||||
}
|
||||
} else {
|
||||
if (!server->isListening())
|
||||
qCWarning(lcHttpServer) << "The TCP server" << server << "is not listening.";
|
||||
server->setParent(this);
|
||||
}
|
||||
QObjectPrivate::connect(server, &QTcpServer::newConnection,
|
||||
d, &QAbstractHttpServerPrivate::handleNewConnections,
|
||||
Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns list of child TCP servers of this HTTP server.
|
||||
*/
|
||||
QVector<QTcpServer *> QAbstractHttpServer::servers() const
|
||||
{
|
||||
auto c = children();
|
||||
QVector<QTcpServer *> result;
|
||||
result.reserve(c.size());
|
||||
for (auto i = c.constBegin(); i != c.constEnd(); ++i) {
|
||||
if ((*i)->inherits("QTcpServer"))
|
||||
result.append(static_cast<QTcpServer*>(*i));
|
||||
}
|
||||
result.squeeze();
|
||||
return result;
|
||||
}
|
||||
|
||||
#if defined(QT_WEBSOCKETS_LIB)
|
||||
/*!
|
||||
\fn QAbstractHttpServer::newConnection
|
||||
This signal is emitted every time a new WebSocket connection is
|
||||
available.
|
||||
|
||||
\sa hasPendingWebSocketConnections(), nextPendingWebSocketConnection()
|
||||
*/
|
||||
|
||||
/*!
|
||||
Returns \c true if the server has pending WebSocket connections;
|
||||
otherwise returns \c false.
|
||||
|
||||
\sa newWebSocketConnection(), nextPendingWebSocketConnection()
|
||||
*/
|
||||
bool QAbstractHttpServer::hasPendingWebSocketConnections() const
|
||||
{
|
||||
Q_D(const QAbstractHttpServer);
|
||||
return d->websocketServer.hasPendingConnections();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the next pending connection as a connected QWebSocket
|
||||
object. QAbstractHttpServer does not take ownership of the
|
||||
returned QWebSocket object. It is up to the caller to delete the
|
||||
object explicitly when it will no longer be used, otherwise a
|
||||
memory leak will occur. \c nullptr is returned if this function
|
||||
is called when there are no pending connections.
|
||||
|
||||
\note The returned QWebSocket object cannot be used from another
|
||||
thread.
|
||||
|
||||
\sa newWebSocketConnection(), hasPendingWebSocketConnections()
|
||||
*/
|
||||
QWebSocket *QAbstractHttpServer::nextPendingWebSocketConnection()
|
||||
{
|
||||
Q_D(QAbstractHttpServer);
|
||||
return d->websocketServer.nextPendingConnection();
|
||||
}
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -0,0 +1,89 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QABSTRACTHTTPSERVER_H
|
||||
#define QABSTRACTHTTPSERVER_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
|
||||
#include <QtHttpServer/qthttpserverglobal.h>
|
||||
|
||||
#include <QtNetwork/qhostaddress.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QHttpServerRequest;
|
||||
class QTcpServer;
|
||||
class QTcpSocket;
|
||||
class QWebSocket;
|
||||
|
||||
class QAbstractHttpServerPrivate;
|
||||
class Q_HTTPSERVER_EXPORT QAbstractHttpServer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QAbstractHttpServer(QObject *parent = nullptr);
|
||||
|
||||
bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);
|
||||
|
||||
void bind(QTcpServer *server = nullptr);
|
||||
QVector<QTcpServer *> servers() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
void missingHandler(const QHttpServerRequest &request, QTcpSocket *socket);
|
||||
|
||||
#if defined(QT_WEBSOCKETS_LIB)
|
||||
void newWebSocketConnection();
|
||||
|
||||
public:
|
||||
bool hasPendingWebSocketConnections() const;
|
||||
QWebSocket *nextPendingWebSocketConnection();
|
||||
#endif // defined(QT_WEBSOCKETS_LIB)
|
||||
|
||||
protected:
|
||||
virtual bool handleRequest(const QHttpServerRequest &request, QTcpSocket *socket) = 0;
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(QAbstractHttpServer)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QABSTRACTHTTPSERVER_H
|
|
@ -0,0 +1,85 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QABSTRACTHTTPSERVER_P_H
|
||||
#define QABSTRACTHTTPSERVER_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists for the convenience
|
||||
// of QHttpServer. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
|
||||
#include <private/qobject_p.h>
|
||||
|
||||
#if defined(QT_WEBSOCKETS_LIB)
|
||||
#include <QtWebSockets/qwebsocketserver.h>
|
||||
#endif // defined(QT_WEBSOCKETS_LIB)
|
||||
|
||||
class QHttpServerRequest;
|
||||
|
||||
class QAbstractHttpServerPrivate: public QObjectPrivate
|
||||
{
|
||||
Q_DECLARE_PUBLIC(QAbstractHttpServer)
|
||||
|
||||
public:
|
||||
QAbstractHttpServerPrivate();
|
||||
|
||||
#if defined(QT_WEBSOCKETS_LIB)
|
||||
QWebSocketServer websocketServer {
|
||||
QLatin1String("QtHttpServer"),
|
||||
QWebSocketServer::NonSecureMode
|
||||
};
|
||||
#endif // defined(QT_WEBSOCKETS_LIB)
|
||||
|
||||
#if !defined(QT_NO_USERDATA)
|
||||
static QAtomicInt userDataId;
|
||||
#else
|
||||
QHash<QTcpSocket *, QHttpServerRequest *> requests;
|
||||
#endif
|
||||
|
||||
void handleNewConnections();
|
||||
void handleReadyRead();
|
||||
};
|
||||
|
||||
#endif // QABSTRACTHTTPSERVER_P_H
|
|
@ -0,0 +1,304 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qhttpserverrequest_p.h"
|
||||
|
||||
#include <QtHttpServer/qhttpserverrequest.h>
|
||||
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qloggingcategory.h>
|
||||
#include <QtNetwork/qtcpsocket.h>
|
||||
#if defined(QT_FEATURE_ssl)
|
||||
#include <QtNetwork/qsslsocket.h>
|
||||
#endif
|
||||
|
||||
Q_LOGGING_CATEGORY(lc, "qt.httpserver.request")
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#if !defined(QT_NO_DEBUG_STREAM)
|
||||
Q_HTTPSERVER_EXPORT QDebug operator<<(QDebug debug, const QHttpServerRequest &request)
|
||||
{
|
||||
const auto oldSetting = debug.autoInsertSpaces();
|
||||
debug.nospace() << "QHttpServerRequest(";
|
||||
debug << "(Url: " << request.url() << ")";
|
||||
debug << "(Headers: " << request.headers() << ")";
|
||||
debug << ')';
|
||||
debug.setAutoInsertSpaces(oldSetting);
|
||||
return debug.maybeSpace();
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const http_parser *const httpParser)
|
||||
{
|
||||
const auto oldSetting = debug.autoInsertSpaces();
|
||||
debug.nospace() << "http_parser(" << static_cast<const void * const>(httpParser) << ": ";
|
||||
debug << "HTTP " << httpParser->http_major << "." << httpParser->http_minor << " "
|
||||
<< http_method_str(http_method(httpParser->method)) << ')';
|
||||
debug.setAutoInsertSpaces(oldSetting);
|
||||
return debug.maybeSpace();
|
||||
}
|
||||
#endif
|
||||
|
||||
http_parser_settings QHttpServerRequestPrivate::httpParserSettings {
|
||||
&QHttpServerRequestPrivate::onMessageBegin,
|
||||
&QHttpServerRequestPrivate::onUrl,
|
||||
&QHttpServerRequestPrivate::onStatus,
|
||||
&QHttpServerRequestPrivate::onHeaderField,
|
||||
&QHttpServerRequestPrivate::onHeaderValue,
|
||||
&QHttpServerRequestPrivate::onHeadersComplete,
|
||||
&QHttpServerRequestPrivate::onBody,
|
||||
&QHttpServerRequestPrivate::onMessageComplete,
|
||||
&QHttpServerRequestPrivate::onChunkHeader,
|
||||
&QHttpServerRequestPrivate::onChunkComplete
|
||||
};
|
||||
|
||||
QHttpServerRequestPrivate::QHttpServerRequestPrivate()
|
||||
{
|
||||
httpParser.data = this;
|
||||
}
|
||||
|
||||
QString QHttpServerRequestPrivate::header(const QString &key) const
|
||||
{
|
||||
return headers.value(headerHash(key)).second;
|
||||
}
|
||||
|
||||
bool QHttpServerRequestPrivate::parse(QIODevice *socket)
|
||||
{
|
||||
const auto fragment = socket->readAll();
|
||||
if (fragment.size()) {
|
||||
#if defined(QT_FEATURE_ssl)
|
||||
auto sslSocket = qobject_cast<QSslSocket *>(socket);
|
||||
url.setScheme(sslSocket && sslSocket->isEncrypted() ? QStringLiteral("https")
|
||||
: QStringLiteral("http"));
|
||||
#else
|
||||
url.setScheme(QStringLiteral("http"));
|
||||
#endif
|
||||
const auto parsed = http_parser_execute(&httpParser,
|
||||
&httpParserSettings,
|
||||
fragment.constData(),
|
||||
size_t(fragment.size()));
|
||||
if (int(parsed) < fragment.size()) {
|
||||
qCDebug(lc, "Parse error: %d", httpParser.http_errno);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint QHttpServerRequestPrivate::headerHash(const QString &key) const
|
||||
{
|
||||
return qHash(key.toLower(), headersSeed);
|
||||
}
|
||||
|
||||
bool QHttpServerRequestPrivate::parseUrl(const char *at, size_t length, bool connect, QUrl *url)
|
||||
{
|
||||
static const std::map<std::size_t, std::function<void(const QString &, QUrl *)>> functions {
|
||||
{ UF_SCHEMA, [](const QString &string, QUrl *url) { url->setScheme(string); } },
|
||||
{ UF_HOST, [](const QString &string, QUrl *url) { url->setHost(string); } },
|
||||
{ UF_PORT, [](const QString &string, QUrl *url) { url->setPort(string.toInt()); } },
|
||||
{ UF_PATH, [](const QString &string, QUrl *url) { url->setPath(string); } },
|
||||
{ UF_QUERY, [](const QString &string, QUrl *url) { url->setQuery(string); } },
|
||||
{ UF_FRAGMENT, [](const QString &string, QUrl *url) { url->setFragment(string); } },
|
||||
{ UF_USERINFO, [](const QString &string, QUrl *url) { url->setUserInfo(string); } },
|
||||
};
|
||||
struct http_parser_url u;
|
||||
if (http_parser_parse_url(at, length, connect ? 1 : 0, &u) == 0) {
|
||||
for (auto i = 0u; i < UF_MAX; i++) {
|
||||
if (u.field_set & (1 << i)) {
|
||||
functions.find(i)->second(QString::fromUtf8(at + u.field_data[i].off,
|
||||
u.field_data[i].len),
|
||||
url);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QHttpServerRequestPrivate *QHttpServerRequestPrivate::instance(http_parser *httpParser)
|
||||
{
|
||||
return static_cast<QHttpServerRequestPrivate *>(httpParser->data);
|
||||
}
|
||||
|
||||
int QHttpServerRequestPrivate::onMessageBegin(http_parser *httpParser)
|
||||
{
|
||||
qCDebug(lc) << static_cast<void *>(httpParser);
|
||||
instance(httpParser)->state = State::OnMessageBegin;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpServerRequestPrivate::onUrl(http_parser *httpParser, const char *at, size_t length)
|
||||
{
|
||||
qCDebug(lc) << httpParser << QString::fromUtf8(at, int(length));
|
||||
auto instance = static_cast<QHttpServerRequestPrivate *>(httpParser->data);
|
||||
instance->state = State::OnUrl;
|
||||
parseUrl(at, length, false, &instance->url);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpServerRequestPrivate::onStatus(http_parser *httpParser, const char *at, size_t length)
|
||||
{
|
||||
qCDebug(lc) << httpParser << QString::fromUtf8(at, int(length));
|
||||
instance(httpParser)->state = State::OnStatus;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpServerRequestPrivate::onHeaderField(http_parser *httpParser, const char *at, size_t length)
|
||||
{
|
||||
qCDebug(lc) << httpParser << QString::fromUtf8(at, int(length));
|
||||
auto i = instance(httpParser);
|
||||
i->state = State::OnHeaders;
|
||||
const auto key = QString::fromUtf8(at, int(length));
|
||||
i->headers.insert(i->headerHash(key), qMakePair(key, QString()));
|
||||
i->lastHeader = key;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpServerRequestPrivate::onHeaderValue(http_parser *httpParser, const char *at, size_t length)
|
||||
{
|
||||
qCDebug(lc) << httpParser << QString::fromUtf8(at, int(length));
|
||||
auto i = instance(httpParser);
|
||||
i->state = State::OnHeaders;
|
||||
Q_ASSERT(!i->lastHeader.isEmpty());
|
||||
const auto value = QString::fromUtf8(at, int(length));
|
||||
i->headers[i->headerHash(i->lastHeader)] = qMakePair(i->lastHeader, value);
|
||||
if (i->lastHeader.compare(QStringLiteral("host"), Qt::CaseInsensitive) == 0)
|
||||
parseUrl(at, length, true, &i->url);
|
||||
#if defined(QT_DEBUG)
|
||||
i->lastHeader.clear();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpServerRequestPrivate::onHeadersComplete(http_parser *httpParser)
|
||||
{
|
||||
qCDebug(lc) << httpParser;
|
||||
instance(httpParser)->state = State::OnHeadersComplete;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpServerRequestPrivate::onBody(http_parser *httpParser, const char *at, size_t length)
|
||||
{
|
||||
qCDebug(lc) << httpParser << QString::fromUtf8(at, int(length));
|
||||
auto i = instance(httpParser);
|
||||
i->state = State::OnBody;
|
||||
i->body = QByteArray(at, int(length));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpServerRequestPrivate::onMessageComplete(http_parser *httpParser)
|
||||
{
|
||||
qCDebug(lc) << httpParser;
|
||||
instance(httpParser)->state = State::OnMessageComplete;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpServerRequestPrivate::onChunkHeader(http_parser *httpParser)
|
||||
{
|
||||
qCDebug(lc) << httpParser;
|
||||
instance(httpParser)->state = State::OnChunkHeader;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int QHttpServerRequestPrivate::onChunkComplete(http_parser *httpParser)
|
||||
{
|
||||
qCDebug(lc) << httpParser;
|
||||
instance(httpParser)->state = State::OnChunkComplete;
|
||||
return 0;
|
||||
}
|
||||
|
||||
QHttpServerRequest::QHttpServerRequest() :
|
||||
QObjectUserData(),
|
||||
d(new QHttpServerRequestPrivate)
|
||||
{}
|
||||
|
||||
QHttpServerRequest::QHttpServerRequest(const QHttpServerRequest &other) :
|
||||
QObjectUserData(),
|
||||
d(other.d)
|
||||
{}
|
||||
|
||||
QHttpServerRequest::~QHttpServerRequest()
|
||||
{}
|
||||
|
||||
QString QHttpServerRequest::value(const QString &key) const
|
||||
{
|
||||
return d->headers.value(d->headerHash(key)).second;
|
||||
}
|
||||
|
||||
QUrl QHttpServerRequest::url() const
|
||||
{
|
||||
return d->url;
|
||||
}
|
||||
|
||||
QHttpServerRequest::Method QHttpServerRequest::method() const
|
||||
{
|
||||
switch (d->httpParser.method) {
|
||||
case HTTP_GET:
|
||||
return QHttpServerRequest::Method::Get;
|
||||
case HTTP_PUT:
|
||||
return QHttpServerRequest::Method::Put;
|
||||
case HTTP_DELETE:
|
||||
return QHttpServerRequest::Method::Delete;
|
||||
case HTTP_POST:
|
||||
return QHttpServerRequest::Method::Post;
|
||||
case HTTP_HEAD:
|
||||
return QHttpServerRequest::Method::Head;
|
||||
case HTTP_OPTIONS:
|
||||
return QHttpServerRequest::Method::Options;
|
||||
case HTTP_PATCH:
|
||||
return QHttpServerRequest::Method::Patch;
|
||||
default:
|
||||
return QHttpServerRequest::Method::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
QVariantMap QHttpServerRequest::headers() const
|
||||
{
|
||||
QVariantMap ret;
|
||||
for (auto it = d->headers.cbegin(), end = d->headers.cend(); it != end; ++it)
|
||||
ret.insert(it.value().first, it.value().second);
|
||||
return ret;
|
||||
}
|
||||
|
||||
QByteArray QHttpServerRequest::body() const
|
||||
{
|
||||
return d->body;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
|
@ -0,0 +1,101 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPSERVERREQUEST_H
|
||||
#define QHTTPSERVERREQUEST_H
|
||||
|
||||
#include <QtHttpServer/qthttpserverglobal.h>
|
||||
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtCore/qshareddata.h>
|
||||
#include <QtCore/qurl.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QRegularExpression;
|
||||
class QString;
|
||||
class QTcpSocket;
|
||||
|
||||
class QHttpServerRequestPrivate;
|
||||
class Q_HTTPSERVER_EXPORT QHttpServerRequest : public QObjectUserData
|
||||
{
|
||||
friend class QAbstractHttpServerPrivate;
|
||||
friend class QHttpServerResponse;
|
||||
|
||||
public:
|
||||
~QHttpServerRequest() override;
|
||||
|
||||
enum class Method
|
||||
{
|
||||
Unknown = -1,
|
||||
|
||||
Get,
|
||||
Put,
|
||||
Delete,
|
||||
Post,
|
||||
Head,
|
||||
Options,
|
||||
Patch
|
||||
};
|
||||
|
||||
QString value(const QString &key) const;
|
||||
QUrl url() const;
|
||||
Method method() const;
|
||||
QVariantMap headers() const;
|
||||
QByteArray body() const;
|
||||
|
||||
protected:
|
||||
QHttpServerRequest(const QHttpServerRequest &other);
|
||||
|
||||
private:
|
||||
#if !defined(QT_NO_DEBUG_STREAM)
|
||||
friend QDebug operator<<(QDebug debug, const QHttpServerRequest &request);
|
||||
#endif
|
||||
|
||||
QHttpServerRequest();
|
||||
|
||||
QExplicitlySharedDataPointer<QHttpServerRequestPrivate> d;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
Q_DECLARE_METATYPE(QHttpServerRequest::Method)
|
||||
|
||||
#endif // QHTTPSERVERREQUEST_H
|
|
@ -0,0 +1,118 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtHttpServer module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPSERVERREQUEST_P_H
|
||||
#define QHTTPSERVERREQUEST_P_H
|
||||
|
||||
#include <QtHttpServer/qhttpserverrequest.h>
|
||||
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qpair.h>
|
||||
#include <QtCore/qregularexpression.h>
|
||||
#include <QtCore/qshareddata.h>
|
||||
#include <QtCore/qstring.h>
|
||||
#include <QtCore/qurl.h>
|
||||
|
||||
#include "../3rdparty/http-parser/http_parser.h"
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists for the convenience
|
||||
// of QHttpServer. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QHttpServerRequestPrivate : public QSharedData
|
||||
{
|
||||
public:
|
||||
QHttpServerRequestPrivate();
|
||||
|
||||
quint16 port = 0;
|
||||
enum class State {
|
||||
NotStarted,
|
||||
OnMessageBegin,
|
||||
OnUrl,
|
||||
OnStatus,
|
||||
OnHeaders,
|
||||
OnHeadersComplete,
|
||||
OnBody,
|
||||
OnMessageComplete,
|
||||
OnChunkHeader,
|
||||
OnChunkComplete
|
||||
} state = State::NotStarted;
|
||||
QByteArray body;
|
||||
|
||||
QUrl url;
|
||||
|
||||
http_parser httpParser;
|
||||
|
||||
QString header(const QString &key) const;
|
||||
bool parse(QIODevice *socket);
|
||||
|
||||
QString lastHeader;
|
||||
QHash<uint, QPair<QString, QString>> headers;
|
||||
const uint headersSeed = uint(qGlobalQHashSeed());
|
||||
uint headerHash(const QString &key) const;
|
||||
|
||||
private:
|
||||
static http_parser_settings httpParserSettings;
|
||||
static bool parseUrl(const char *at, size_t length, bool connect, QUrl *url);
|
||||
|
||||
static QHttpServerRequestPrivate *instance(http_parser *httpParser);
|
||||
|
||||
static int onMessageBegin(http_parser *httpParser);
|
||||
static int onUrl(http_parser *httpParser, const char *at, size_t length);
|
||||
static int onStatus(http_parser *httpParser, const char *at, size_t length);
|
||||
static int onHeaderField(http_parser *httpParser, const char *at, size_t length);
|
||||
static int onHeaderValue(http_parser *httpParser, const char *at, size_t length);
|
||||
static int onHeadersComplete(http_parser *httpParser);
|
||||
static int onBody(http_parser *httpParser, const char *at, size_t length);
|
||||
static int onMessageComplete(http_parser *httpParser);
|
||||
static int onChunkHeader(http_parser *httpParser);
|
||||
static int onChunkComplete(http_parser *httpParser);
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QHTTPSERVERREQUEST_P_H
|
|
@ -0,0 +1,58 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QTHTTPSERVERGLOBAL_H
|
||||
#define QTHTTPSERVERGLOBAL_H
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#ifndef QT_STATIC
|
||||
# if defined(QT_BUILD_HTTPSERVER_LIB)
|
||||
# define Q_HTTPSERVER_EXPORT Q_DECL_EXPORT
|
||||
# else
|
||||
# define Q_HTTPSERVER_EXPORT Q_DECL_IMPORT
|
||||
# endif
|
||||
#else
|
||||
# define Q_HTTPSERVER_EXPORT
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QTHTTPSERVERGLOBAL_H
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS = \
|
||||
httpserver
|
|
@ -0,0 +1,3 @@
|
|||
%modules = ( # path to module name map
|
||||
"QtHttpServer" => "$basedir/src/httpserver",
|
||||
);
|
|
@ -0,0 +1,6 @@
|
|||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS = \
|
||||
cmake \
|
||||
qabstracthttpserver
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
project(qmake_cmake_files)
|
||||
|
||||
enable_testing()
|
||||
|
||||
find_package(Qt5Core REQUIRED)
|
||||
|
||||
include("${_Qt5CTestMacros}")
|
||||
|
||||
test_module_includes(
|
||||
HttpServer QHttpServer
|
||||
)
|
||||
|
||||
expect_pass(test_plugins)
|
|
@ -0,0 +1,4 @@
|
|||
# Cause make to do nothing.
|
||||
TEMPLATE = subdirs
|
||||
CMAKE_QT_MODULES_UNDER_TEST = httpserver
|
||||
CONFIG += ctest_testcase
|
|
@ -0,0 +1,8 @@
|
|||
CONFIG += testcase
|
||||
TARGET = tst_qabstracthttpserver
|
||||
SOURCES += tst_qabstracthttpserver.cpp
|
||||
|
||||
requires(qtConfig(private_tests))
|
||||
QT = core network network-private testlib httpserver
|
||||
|
||||
qtHaveModule(websockets): QT += websockets
|
|
@ -0,0 +1,159 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtHttpServer/qabstracthttpserver.h>
|
||||
|
||||
#if defined(QT_WEBSOCKETS_LIB)
|
||||
#include <QtWebSockets/qwebsocket.h>
|
||||
#endif
|
||||
|
||||
#include <QtTest/qsignalspy.h>
|
||||
#include <QtTest/qtest.h>
|
||||
|
||||
#include <QtCore/qregularexpression.h>
|
||||
#include <QtCore/qurl.h>
|
||||
#include <QtNetwork/qnetworkaccessmanager.h>
|
||||
#include <QtNetwork/qnetworkreply.h>
|
||||
#include <QtNetwork/qnetworkrequest.h>
|
||||
#include <QtNetwork/qtcpserver.h>
|
||||
#include <QtHttpServer/qhttpserverrequest.h>
|
||||
|
||||
#if defined(Q_OS_UNIX)
|
||||
# include <signal.h>
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class tst_QAbstractHttpServer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void request_data();
|
||||
void request();
|
||||
void checkListenWarns();
|
||||
void websocket();
|
||||
};
|
||||
|
||||
void tst_QAbstractHttpServer::request_data()
|
||||
{
|
||||
QTest::addColumn<QString>("host");
|
||||
QTest::addColumn<QString>("path");
|
||||
QTest::addColumn<QString>("query");
|
||||
|
||||
QTest::addRow("127.0.0.1") << "127.0.0.1" << "/" << QString();
|
||||
QTest::addRow("0.0.0.0") << "0.0.0.0" << "/" << QString();
|
||||
QTest::addRow("localhost") << "localhost" << "/" << QString();
|
||||
QTest::addRow("localhost with query") << "localhost" << "/" << QString("key=value");
|
||||
}
|
||||
|
||||
void tst_QAbstractHttpServer::request()
|
||||
{
|
||||
QFETCH(QString, host);
|
||||
QFETCH(QString, path);
|
||||
QFETCH(QString, query);
|
||||
|
||||
struct HttpServer : QAbstractHttpServer
|
||||
{
|
||||
QUrl url;
|
||||
QByteArray body;
|
||||
QHttpServerRequest::Method method;
|
||||
quint8 padding[4];
|
||||
|
||||
bool handleRequest(const QHttpServerRequest &request, QTcpSocket *) override
|
||||
{
|
||||
method = request.method();
|
||||
url = request.url();
|
||||
body = request.body();
|
||||
return true;
|
||||
}
|
||||
} server;
|
||||
auto tcpServer = new QTcpServer;
|
||||
tcpServer->listen();
|
||||
server.bind(tcpServer);
|
||||
QNetworkAccessManager networkAccessManager;
|
||||
QUrl url(QStringLiteral("http://%1:%2%3")
|
||||
.arg(host)
|
||||
.arg(tcpServer->serverPort())
|
||||
.arg(path));
|
||||
if (!query.isEmpty())
|
||||
url.setQuery(query);
|
||||
const QNetworkRequest request(url);
|
||||
auto reply = networkAccessManager.get(request);
|
||||
QTRY_COMPARE(server.method, QHttpServerRequest::Method::Get);
|
||||
QCOMPARE(server.url, url);
|
||||
QCOMPARE(server.body, QByteArray());
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void tst_QAbstractHttpServer::checkListenWarns()
|
||||
{
|
||||
struct HttpServer : QAbstractHttpServer
|
||||
{
|
||||
bool handleRequest(const QHttpServerRequest &, QTcpSocket *) override { return true; }
|
||||
} server;
|
||||
auto tcpServer = new QTcpServer;
|
||||
QTest::ignoreMessage(QtWarningMsg, QRegularExpression("The TCP server .* is not listening."));
|
||||
server.bind(tcpServer);
|
||||
}
|
||||
|
||||
void tst_QAbstractHttpServer::websocket()
|
||||
{
|
||||
#if !defined(QT_WEBSOCKETS_LIB)
|
||||
QSKIP("This test requires WebSocket support");
|
||||
#endif // defined(QT_WEBSOCKETS_LIB)
|
||||
struct HttpServer : QAbstractHttpServer
|
||||
{
|
||||
bool handleRequest(const QHttpServerRequest &, QTcpSocket *) override { return false; }
|
||||
} server;
|
||||
auto tcpServer = new QTcpServer;
|
||||
tcpServer->listen();
|
||||
server.bind(tcpServer);
|
||||
QWebSocket socket;
|
||||
const QUrl url(QString::fromLatin1("ws://localhost:%1").arg(tcpServer->serverPort()));
|
||||
socket.open(url);
|
||||
QSignalSpy newConnectionSpy(&server, &HttpServer::newWebSocketConnection);
|
||||
QTRY_COMPARE(newConnectionSpy.count(), 1);
|
||||
delete server.nextPendingWebSocketConnection();
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QTEST_MAIN(tst_QAbstractHttpServer)
|
||||
|
||||
#include "tst_qabstracthttpserver.moc"
|
|
@ -0,0 +1,4 @@
|
|||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS = \
|
||||
auto
|
Loading…
Reference in New Issue