Continues migration with DDS, Common

This commit is contained in:
torsten committed 2026-07-17 12:41:42 +02:00
1 parent 9b15cee841
commit fa48db111e
668 files changed
+111841 -2

No files matched your search

+91
View File
@@ -0,0 +1,91 @@
#include <httq/AbstractServer.h>
#include <httq/HttpRequest.h>
#include <httq/Logger.h>
#include <QTcpServer>
#include <QTcpSocket>
#include <QWebSocketServer>
namespace httq
{
AbstractServer::AbstractServer(QObject *parent)
: QObject(parent)
, mSvr(new QTcpServer(this))
, mLoggerFactory(nullptr)
, mLogger(nullptr)
{}
LoggerFactory *AbstractServer::createLoggerFactory()
{
return new LoggerFactory(this);
}
LoggerFactory *AbstractServer::getLoggerFactory()
{
if (mLoggerFactory == nullptr)
mLoggerFactory = createLoggerFactory();
return mLoggerFactory;
}
void AbstractServer::slotNewConnection()
{
mLogger->debug(QStringLiteral("newConnection"));
while (QTcpSocket *cli = mSvr->nextPendingConnection())
{
mLogger->debug(QStringLiteral("nextPendingConnection"));
if (cli == nullptr)
{
mLogger->debug(QStringLiteral("invalid tcp client"));
return;
}
HttpRequest *req = new HttpRequest(cli, this);
connect(req, &HttpRequest::signalUpgrade,
this, [this, req](QTcpSocket *sock)
{
mLogger->debug(QStringLiteral("signalUpgrade"));
QWebSocketServer *wsSvr = webSocketServer();
if (wsSvr == nullptr)
{
mLogger->debug(QStringLiteral("no websocket server available for websocket upgrade"));
sock->close();
sock->deleteLater();
return;
}
wsSvr->handleConnection(sock);
emit sock->readyRead();
});
connect(req, &HttpRequest::signalReady,
this, [this, req]()
{
mLogger->debug(QStringLiteral("signalReady"));
newHttpConnection(req);
});
}
}
bool AbstractServer::listen(qint16 port, const QHostAddress &host)
{
mLoggerFactory = createLoggerFactory();
mLogger = mLoggerFactory->createLogger(this);
mLogger->debug(QStringLiteral("listen"));
connect(mSvr, &QTcpServer::newConnection,
this, &AbstractServer::slotNewConnection);
return mSvr->listen(host, port);
}
}
+32
View File
@@ -0,0 +1,32 @@
# Modified CMakeLists.txt from github.com/wille-io/httq
set(src
include/http_parser.h
include/http_parser.c # TODO: move out of include dir
include/httq/AbstractServer.h
include/httq/DataStream.h
include/httq/HttpRequest.h
include/httq/Logger.h
AbstractServer.cpp
DataStream.cpp
HttpRequest.cpp
Logger.cpp
)
NGL_ADD_LIBRARY2(SV WS httq SHARED ${src})
NGL_TARGET_INCLUDE_DIRECTORIES(httq
PUBLIC
${CMAKE_CURRENT_BINARY_DIR}/../ # export file
${CMAKE_CURRENT_SOURCE_DIR}/include/
)
NGL_TARGET_LINK_LIBRARIES(httq
Qt5::Core
Qt5::Network
Qt5::WebSockets
)
NGL_INSTALL_TARGET(httq)
+262
View File
@@ -0,0 +1,262 @@
#include <httq/DataStream.h>
#include <httq/HttpRequest.h>
#include <QIODevice>
#include <QTcpSocket>
#include <QTimer>
#include <QDataStream>
#include <QTemporaryFile>
#include <QFile>
#ifdef Q_OS_UNIX
#include <unistd.h>
#endif
namespace httq
{
DataStream::DataStream(QIODevice *from, QIODevice *to, const QByteArray &bodyPartial,
qint64 fileLength, size_t bufferSize, LoggerFactory *loggerFactory,
QObject *parent)
: QObject(parent)
, mBodyPartial(bodyPartial)
, mFrom(from)
, mTo(to)
, mBufferSize(bufferSize)
, mFileLength(fileLength)
, mLogger(loggerFactory->createLogger(this))
{
#ifdef SAVE_PARTIAL_BODY
{
QTemporaryFile f;
f.open();
f.write(mBodyPartial);
f.setAutoRemove(false);
qWarning() << "partial:" << f.fileName();
}
#endif
if (mFileLength == 0)
{
mLogger->warning(QStringLiteral("file size = 0 - done"));
QTimer::singleShot(1, this, [this]() { emit signalDone(); deleteLater(); } );
return;
}
mLogger->debug(QStringLiteral("DataStream c_tor"));
mBuffer.reserve(bufferSize);
mFromSupportsSignals = (qobject_cast<QFileDevice *>(mFrom) == nullptr);
mToSupportsSignals = (qobject_cast<QFileDevice *>(mTo) == nullptr);
mLogger->debug(QStringLiteral("signal support - from: %1 - to: %2").arg(mFromSupportsSignals).arg(mToSupportsSignals));
auto sock = qobject_cast<QTcpSocket *>(mFrom);
qWarning() << "######################### SOCK" << sock;
connect(sock, &QTcpSocket::readyRead,
this, [this]()
{
mLogger->debug(QStringLiteral(">> DataStream readyRead"));
QTimer::singleShot(1, this, [this]() { readNext(); } );
});
if (mToSupportsSignals)
connect(mTo, &QIODevice::bytesWritten,
this, [this]()
{
mLogger->debug(QStringLiteral(">> DataStream write"));
mReadyWrite = true;
QTimer::singleShot(1, this, [this]() { readNext(); } );
});
QTimer::singleShot(1, this, [this]() { readNext(); } );
}
DataStream::~DataStream()
{
mLogger->debug(QStringLiteral("~DataStream"));
}
void DataStream::readNext()
{
#if defined(DEBUG_SLOW) && defined(Q_OS_UNIX)
mLogger->debug(QStringLiteral("read: slowing down....."));
usleep(2 * 1000 * 1000);
#endif
mLogger->debug(QStringLiteral("read: readNext: %1").arg(mBuffer.size()));
if (mBuffer.size() > 0)
{
mLogger->debug(QStringLiteral("read: ignoring read until buffer was written"));
return;
}
if (mToSupportsSignals && !mReadyWrite)
{
mLogger->debug(QStringLiteral("read: won't read until write is ready!"));
return;
}
if (!mBodyPartial.isEmpty())
{
mBuffer = mBodyPartial;
alreadyRead += mBuffer.size();
mLogger->debug(QStringLiteral("read: prebody size: %1").arg(mBuffer.size()));
mBodyPartial.clear();
mLogger->debug(QStringLiteral("read: queueing writeNext after prebody read"));
QTimer::singleShot(1, this, [this]() { writeNext(); } );
return;
}
if ( (mFrom->bytesAvailable() < 1 ))
{
mLogger->debug(QStringLiteral("read: no bytes available and not ready read"));
QTimer::singleShot(1000, this, [this]() { readNext(); } );
return;
}
mBuffer = mFrom->read(mBufferSize);
mLogger->debug(QStringLiteral("read: mPos is now %1").arg(mBuffer.size()));
alreadyRead += mBuffer.size();
mLogger->debug(QStringLiteral("read: alreadyRead = %1").arg(alreadyRead));
if (mBuffer.size() < 1)
{
mLogger->debug(QStringLiteral("read: cannot read anything"));
deleteLater();
return;
}
mLogger->debug(QStringLiteral("read: queueing writeNext"));
QTimer::singleShot(1, this, [this]() { writeNext(); } );
}
void DataStream::writeNext()
{
mLogger->debug(QStringLiteral("write: writeNext: %1").arg(mBuffer.size()));
if (mBuffer.size() < 1)
{
mLogger->debug(QStringLiteral("write: nothing to write"));
QTimer::singleShot(1, this, [this]() { readNext(); } );
return;
}
if (!mTo->isOpen() || !mTo->isWritable())
{
mLogger->debug(QStringLiteral("write: file error: open? %1 - writable? %2").arg(mTo->isOpen()).arg(mTo->isWritable()));
deleteLater();
return;
}
if (mToSupportsSignals && !mReadyWrite )
{
mLogger->debug(QStringLiteral("write: !mReadyWrite"));
return;
}
if (mToSupportsSignals)
mReadyWrite = false;
const qint64 size = mTo->write(mBuffer);
mLogger->debug(QStringLiteral("write: wrote to buffer %1 bytes").arg(size));
mLogger->debug(QStringLiteral("write: %1 / %2 bytes done (%3 bytes left until DataStream done)").arg(alreadyRead).arg(mFileLength).arg(mFileLength - alreadyRead));
if (size < mBuffer.size())
{
mLogger->warning(QStringLiteral("write: cannot write all bytes: %1 / %2 bytes").arg(mBuffer.size()).arg(size));
deleteLater();
return;
}
mLogger->debug(QStringLiteral("write: mPos is now %1 and was set to zero now!!").arg(mBuffer.size()));
mBuffer.clear();
if (alreadyRead == mFileLength)
{
mLogger->debug(QStringLiteral("write: DONE!"));
emit signalDone();
deleteLater();
return;
}
if (alreadyRead > mFileLength)
{
mLogger->debug(QStringLiteral("write: oops... we read too much!! data may be corrupt!! already read %1 bytes, but content length is only %2 (%1 / %2)").arg(alreadyRead).arg(mFileLength));
emit signalDone();
deleteLater();
return;
}
if (mToSupportsSignals)
mLogger->debug(QStringLiteral("write: waiting for bytes written to be triggered..."));
mLogger->debug(QStringLiteral("write: queueing writeNext again"));
QTimer::singleShot(1, this, [this]() { writeNext(); } );
}
}
+415
View File
@@ -0,0 +1,415 @@
#include <httq/AbstractServer.h>
#include <httq/HttpRequest.h>
#include <httq/DataStream.h>
#include <http_parser.h>
#include <httq/Logger.h>
#include <QTcpSocket>
#include <QUrl>
#include <QUrlQuery>
#include <QJsonValue>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
namespace httq
{
HttpRequest::HttpRequest(QTcpSocket *cli, AbstractServer *parent)
: QObject(parent)
, mCli(cli)
, mLogger(parent->getLoggerFactory()->createLogger(this))
{
mLogger->debug(QStringLiteral("HttpRequest c_tor"));
http_parser_init(&mParser, HTTP_REQUEST);
mData.mHttpRquest = this;
mParser.data = static_cast<void *>(&mData);
connect(cli, &QTcpSocket::readyRead,
this, &HttpRequest::slotReadyRead);
connect(cli, &QTcpSocket::disconnected,
this, [this]()
{
qWarning() << "Client disconnected himself!!";
});
connect(cli, &QTcpSocket::disconnected,
this, &HttpRequest::deleteLater);
mParserSettings.on_message_begin = &HttpRequest::on_message_begin;
mParserSettings.on_url = &HttpRequest::on_url;
mParserSettings.on_status = &HttpRequest::on_status;
mParserSettings.on_header_field = &HttpRequest::on_header_field;
mParserSettings.on_header_value = &HttpRequest::on_header_value;
mParserSettings.on_headers_complete = &HttpRequest::on_headers_complete;
mParserSettings.on_body = &HttpRequest::on_body;
mParserSettings.on_message_complete = &HttpRequest::on_message_complete;
mParserSettings.on_chunk_header = &HttpRequest::on_chunk_header;
mParserSettings.on_chunk_complete = &HttpRequest::on_chunk_complete;
}
HttpRequest::~HttpRequest()
{
mLogger->debug(QStringLiteral("~HttpRequest"));
if (mCli == nullptr)
return;
mLogger->debug(QStringLiteral("mCli flush & close"));
mCli->flush();
}
QString HttpRequest::toString() const
{
return QStringLiteral("HttpRequest(url = '%1', method = %2)").arg(url().toString()).arg(methodString());
}
int HttpRequest::on_message_begin(http_parser *parser)
{
dataLogger(parser)->debug(QStringLiteral("on_message_begin"));
return 0;
}
int HttpRequest::on_url(http_parser *parser, const char *at, size_t length)
{
dataLogger(parser)->debug(QStringLiteral("on_url '%1'").arg(QString::fromUtf8(at, length)));
http_parser_url parserUrl;
if (http_parser_parse_url(at, length, false, &parserUrl) != 0)
{
dataLogger(parser)->debug(QStringLiteral("could not determine url! %1").arg(QString::fromUtf8(at, length)));
return 1;
}
QUrl url;
#define isUrlFieldSet(field) parserUrl.field_set & (1 << field)
if (isUrlFieldSet(UF_SCHEMA))
url.setScheme(QString::fromUtf8(at + parserUrl.field_data[UF_SCHEMA].off, parserUrl.field_data[UF_SCHEMA].len));
if (parserUrl.field_set & (1 << UF_HOST))
url.setHost(QString::fromUtf8(at + parserUrl.field_data[UF_HOST].off, parserUrl.field_data[UF_HOST].len));
if (parserUrl.field_set & (1 << UF_PORT))
{
bool k = false;
QString portString(QString::fromUtf8(at + parserUrl.field_data[UF_PORT].off, parserUrl.field_data[UF_PORT].len));
qint16 port = portString.toShort(&k);
if (!k)
{
dataLogger(parser)->warning(QStringLiteral("could not determine port! %1").arg(portString));
return 1;
}
url.setPort(static_cast<int>(port));
}
if (parserUrl.field_set & (1 << UF_PATH))
url.setPath(QString::fromUtf8(at + parserUrl.field_data[UF_PATH].off, parserUrl.field_data[UF_PATH].len));
HttpRequestData *reqData = data(parser);
if (isUrlFieldSet(UF_QUERY))
{
QUrlQuery q(QString::fromUtf8(at + parserUrl.field_data[UF_QUERY].off, parserUrl.field_data[UF_QUERY].len));
url.setQuery(q);
reqData->mQuery = q;
}
if (isUrlFieldSet(UF_USERINFO))
url.setUserInfo(QString::fromUtf8(at + parserUrl.field_data[UF_USERINFO].off, parserUrl.field_data[UF_USERINFO].len));
#undef isUrlFieldSet
dataLogger(parser)->debug(QStringLiteral("final url = %1").arg(url.toString()));
reqData->mUrl = url;
return 0;
}
int HttpRequest::on_status(http_parser *parser, const char *, size_t )
{
dataLogger(parser)->debug(QStringLiteral("on_status"));
return 0;
}
int HttpRequest::on_header_field(http_parser *parser, const char *at, size_t length)
{
dataLogger(parser)->debug(QStringLiteral("on_header_field"));
data(parser)->mCurrentHeaderField = QString::fromUtf8(at, length);
return 0;
}
int HttpRequest::on_header_value(http_parser *parser, const char *at, size_t length)
{
dataLogger(parser)->debug(QStringLiteral("on_header_value"));
QString value(QString::fromUtf8(at, length));
HttpRequestData *reqdata = data(parser);
reqdata->mHeaders.insert(reqdata->mCurrentHeaderField, value);
dataLogger(parser)->debug(QStringLiteral("added header: %1 = %2").arg(reqdata->mCurrentHeaderField).arg(value));
reqdata->mCurrentHeaderField.clear();
return 0;
}
int HttpRequest::on_headers_complete(http_parser *parser)
{
dataLogger(parser)->debug(QStringLiteral("on_headers_complete"));
return 0;
}
int HttpRequest::on_body(http_parser *parser, const char *at, size_t length)
{
dataLogger(parser)->debug(QStringLiteral("on_body"));
data(parser)->mBodyPartial = QByteArray(at, length);
data(parser)->mDone = true;
return 0;
}
int HttpRequest::on_message_complete(http_parser *parser)
{
dataLogger(parser)->debug(QStringLiteral("on_message_complete"));
data(parser)->mDone = true;
return 0;
}
int HttpRequest::on_chunk_header(http_parser *parser)
{
dataLogger(parser)->debug(QStringLiteral("on_chunk_header"));
return 0;
}
int HttpRequest::on_chunk_complete(http_parser *parser)
{
dataLogger(parser)->debug(QStringLiteral("on_chunk_complete"));
return 0;
}
qint64 HttpRequest::availableBytes() const
{
return (mContentLength - alreadyRead);
}
DataStream *HttpRequest::createDataStreamToClient(int status, const QString &contentType, QIODevice *from, qint64 fileSize, qint64 bufferSize)
{
mLogger->debug(QStringLiteral("createDataStreamToClient"));
writeStatusHeader(status);
addHeader(QStringLiteral("Content-Type"), contentType);
addHeader(QStringLiteral("Content-Length"), QString::number(fileSize));
writeHeaders();
mCli->write("\r\n");
return new DataStream(from, mCli, {}, fileSize, bufferSize, mLogger->getLoggerFactory(), this);
}
DataStream *HttpRequest::createDataStreamFromClient(QIODevice *to, qint64 fileSize, qint64 bufferSize)
{
mLogger->debug(QStringLiteral("createDataStreamFromClient"));
return new DataStream(mCli, to, mData.mBodyPartial, fileSize, bufferSize, mLogger->getLoggerFactory(), this);
}
void HttpRequest::write(int status, const QJsonValue &jsonValue)
{
QJsonDocument doc;
if (jsonValue.isArray())
doc = QJsonDocument(jsonValue.toArray());
else
doc = QJsonDocument(jsonValue.toObject());
write(status, doc.toJson(), QStringLiteral("application/json"));
}
void HttpRequest::write(int status, const QByteArray &data, const QString &contentType)
{
mLogger->debug(QStringLiteral("write to client: status = %1, data (size) = %2, contentType = %3").arg(status).arg(data.size()).arg(contentType));
writeStatusHeader(status);
addHeader(QStringLiteral("Content-Type"), contentType);
addHeader(QStringLiteral("Content-Length"), QString::number(data.size()));
writeHeaders();
mCli->write("\r\n");
mCli->write(data);
}
void HttpRequest::writeStatusHeader(int status)
{
QString header(QStringLiteral("HTTP/1.1 %1 %2\r\n").arg(status).arg(QStringLiteral("OK") ));
mLogger->debug(QStringLiteral("write to client: header = %1").arg(header));
mCli->write(header.toUtf8());
}
void HttpRequest::addHeader(const QString &key, const QString &value)
{
mHeaders.insert(key, value);
}
void HttpRequest::writeHeaders()
{
QString headers;
QMapIterator<QString, QString> it(mHeaders);
while (it.hasNext())
{
it.next();
headers += QStringLiteral("%1: %2\r\n").arg(it.key(), it.value());
}
mLogger->debug(QStringLiteral("write to client: headers = %1").arg(headers));
mCli->write(headers.toUtf8());
}
void HttpRequest::slotReadyRead()
{
mLogger->debug(QStringLiteral("slotReadyRead"));
if (mDone)
{
mLogger->debug(QStringLiteral("slotReadyRead: already marked as done"));
return;
}
mCli->startTransaction();
while (mCli->bytesAvailable() && !mData.mDone)
{
mLogger->debug(QStringLiteral("slotReadyRead: bytes available"));
const QByteArray data(mCli->read(10000));
int dataSize = data.size();
size_t size = http_parser_execute(&mParser, &mParserSettings, data.constData(), static_cast<size_t>(dataSize));
if (size < static_cast<size_t>(dataSize))
{
mLogger->error(QStringLiteral("http parser error: %1").arg(QString::fromUtf8(http_errno_name(HTTP_PARSER_ERRNO(&mParser)))));
mCli->commitTransaction();
deleteLater();
return;
}
}
if (mData.mDone)
{
mLogger->debug(QStringLiteral("slotReadyRead: now marked as done"));
mDone = true;
mLogger->debug(QStringLiteral("slotReadyRead: method = %1").arg(methodString()));
auto it = mData.mHeaders.constFind(QStringLiteral("Upgrade"));
if (it != mData.mHeaders.constEnd() && (*it).toLower() == QStringLiteral("websocket"))
{
mLogger->debug(QStringLiteral("slotReadyRead: Upgrade: %1").arg(*it));
disconnect(mCli, &QTcpSocket::readyRead, nullptr, nullptr);
mCli->rollbackTransaction();
mCli->setParent(nullptr);
auto cli = mCli;
mCli = nullptr;
emit signalUpgrade(cli);
deleteLater();
return;
}
it = mData.mHeaders.constFind(QStringLiteral("Content-Length"));
if (it != mData.mHeaders.constEnd())
{
bool k = false;
mContentLength = (*it).toLongLong(&k);
if (!k)
{
mLogger->error(QStringLiteral("slotReadyRead: invalid content length: ").arg(*it));
deleteLater();
return;
}
}
alreadyRead = mData.mBodyPartial.size();
mLogger->debug(QStringLiteral("slotReadyRead: already read %1 / %2 bytes").arg(alreadyRead).arg(contentLength()));
mCli->commitTransaction();
emit signalReady();
return;
}
mLogger->debug(QStringLiteral("slotReadyRead: commit transaction"));
mCli->commitTransaction();
}
}
+19
View File
@@ -0,0 +1,19 @@
Copyright Joyent, Inc. and other Node contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Mike Wille <mike@wille.io>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+71
View File
@@ -0,0 +1,71 @@
#include <httq/Logger.h>
#include <QCoreApplication>
#include <QDebug>
namespace httq
{
Logger *LoggerFactory::createLogger(QObject *parent)
{
return new Logger(this, parent);
}
Logger::Logger(LoggerFactory *loggerFactory, QObject *parent)
: QObject(parent)
, mLoggerFactory(loggerFactory)
{
if (parent == nullptr)
{
mPath = QStringLiteral("Global");
}
else
{
mPath = QString::fromUtf8(parent->metaObject()->className());
}
}
void Logger::debug(const QString &message)
{
qtLogger(QStringLiteral("DEBUG"), qWarning()) << message;
}
void Logger::warning(const QString &message)
{
qtLogger(QStringLiteral("WARNING"), qWarning()) << message;
}
void Logger::error(const QString &message)
{
qtLogger(QStringLiteral("ERROR"), qCritical()) << message;
}
QDebug Logger::qtLogger(const QString &type, QDebug logger)
{
QDebug d(logger.noquote());
d << type << mPath << ":";
return d;
}
}
+6
View File
@@ -0,0 +1,6 @@
# httq
Proof of Concept. Do not use in production.
HTTP Server for Qt. Doesn't clog the memory like The Other HTTP Server™.
Based on NodeJS's wonderful http-parser.
Can download a message's body in chunks, instead of reading it all into memory, allowing an easy DoS, like The Other HTTP Server™.
+1
View File
@@ -0,0 +1 @@
nodejs/http-parser/v2.9.4
+2480
View File
File diff suppressed because it is too large. Load diff
+380
View File
@@ -0,0 +1,380 @@
#ifndef http_parser_h
#define http_parser_h
#ifdef __cplusplus
extern "C" {
#endif
#define HTTP_PARSER_VERSION_MAJOR 2
#define HTTP_PARSER_VERSION_MINOR 9
#define HTTP_PARSER_VERSION_PATCH 4
#include <stddef.h>
#if defined(_WIN32) && !defined(__MINGW32__) && \
(!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
#include <BaseTsd.h>
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif
#ifndef HTTP_PARSER_STRICT
# define HTTP_PARSER_STRICT 1
#endif
#ifndef HTTP_MAX_HEADER_SIZE
# define HTTP_MAX_HEADER_SIZE (80*1024)
#endif
typedef struct http_parser http_parser;
typedef struct http_parser_settings http_parser_settings;
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
typedef int (*http_cb) (http_parser*);
#define HTTP_STATUS_MAP(XX) \
XX(100, CONTINUE, Continue) \
XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \
XX(102, PROCESSING, Processing) \
XX(200, OK, OK) \
XX(201, CREATED, Created) \
XX(202, ACCEPTED, Accepted) \
XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \
XX(204, NO_CONTENT, No Content) \
XX(205, RESET_CONTENT, Reset Content) \
XX(206, PARTIAL_CONTENT, Partial Content) \
XX(207, MULTI_STATUS, Multi-Status) \
XX(208, ALREADY_REPORTED, Already Reported) \
XX(226, IM_USED, IM Used) \
XX(300, MULTIPLE_CHOICES, Multiple Choices) \
XX(301, MOVED_PERMANENTLY, Moved Permanently) \
XX(302, FOUND, Found) \
XX(303, SEE_OTHER, See Other) \
XX(304, NOT_MODIFIED, Not Modified) \
XX(305, USE_PROXY, Use Proxy) \
XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \
XX(308, PERMANENT_REDIRECT, Permanent Redirect) \
XX(400, BAD_REQUEST, Bad Request) \
XX(401, UNAUTHORIZED, Unauthorized) \
XX(402, PAYMENT_REQUIRED, Payment Required) \
XX(403, FORBIDDEN, Forbidden) \
XX(404, NOT_FOUND, Not Found) \
XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \
XX(406, NOT_ACCEPTABLE, Not Acceptable) \
XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \
XX(408, REQUEST_TIMEOUT, Request Timeout) \
XX(409, CONFLICT, Conflict) \
XX(410, GONE, Gone) \
XX(411, LENGTH_REQUIRED, Length Required) \
XX(412, PRECONDITION_FAILED, Precondition Failed) \
XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \
XX(414, URI_TOO_LONG, URI Too Long) \
XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \
XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \
XX(417, EXPECTATION_FAILED, Expectation Failed) \
XX(421, MISDIRECTED_REQUEST, Misdirected Request) \
XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \
XX(423, LOCKED, Locked) \
XX(424, FAILED_DEPENDENCY, Failed Dependency) \
XX(426, UPGRADE_REQUIRED, Upgrade Required) \
XX(428, PRECONDITION_REQUIRED, Precondition Required) \
XX(429, TOO_MANY_REQUESTS, Too Many Requests) \
XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \
XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \
XX(501, NOT_IMPLEMENTED, Not Implemented) \
XX(502, BAD_GATEWAY, Bad Gateway) \
XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \
XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \
XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \
XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \
XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \
XX(508, LOOP_DETECTED, Loop Detected) \
XX(510, NOT_EXTENDED, Not Extended) \
XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \
enum http_status
{
#define XX(num, name, string) HTTP_STATUS_##name = num,
HTTP_STATUS_MAP(XX)
#undef XX
};
#define HTTP_METHOD_MAP(XX) \
XX(0, DELETE, DELETE) \
XX(1, GET, GET) \
XX(2, HEAD, HEAD) \
XX(3, POST, POST) \
XX(4, PUT, PUT) \
\
XX(5, CONNECT, CONNECT) \
XX(6, OPTIONS, OPTIONS) \
XX(7, TRACE, TRACE) \
\
XX(8, COPY, COPY) \
XX(9, LOCK, LOCK) \
XX(10, MKCOL, MKCOL) \
XX(11, MOVE, MOVE) \
XX(12, PROPFIND, PROPFIND) \
XX(13, PROPPATCH, PROPPATCH) \
XX(14, SEARCH, SEARCH) \
XX(15, UNLOCK, UNLOCK) \
XX(16, BIND, BIND) \
XX(17, REBIND, REBIND) \
XX(18, UNBIND, UNBIND) \
XX(19, ACL, ACL) \
\
XX(20, REPORT, REPORT) \
XX(21, MKACTIVITY, MKACTIVITY) \
XX(22, CHECKOUT, CHECKOUT) \
XX(23, MERGE, MERGE) \
\
XX(24, MSEARCH, M-SEARCH) \
XX(25, NOTIFY, NOTIFY) \
XX(26, SUBSCRIBE, SUBSCRIBE) \
XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
\
XX(28, PATCH, PATCH) \
XX(29, PURGE, PURGE) \
\
XX(30, MKCALENDAR, MKCALENDAR) \
\
XX(31, LINK, LINK) \
XX(32, UNLINK, UNLINK) \
\
XX(33, SOURCE, SOURCE) \
enum http_method
{
#define XX(num, name, string) HTTP_##name = num,
HTTP_METHOD_MAP(XX)
#undef XX
};
enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
enum flags
{ F_CHUNKED = 1 << 0
, F_CONNECTION_KEEP_ALIVE = 1 << 1
, F_CONNECTION_CLOSE = 1 << 2
, F_CONNECTION_UPGRADE = 1 << 3
, F_TRAILING = 1 << 4
, F_UPGRADE = 1 << 5
, F_SKIPBODY = 1 << 6
, F_CONTENTLENGTH = 1 << 7
, F_TRANSFER_ENCODING = 1 << 8
};
#define HTTP_ERRNO_MAP(XX) \
\
XX(OK, "success") \
\
\
XX(CB_message_begin, "the on_message_begin callback failed") \
XX(CB_url, "the on_url callback failed") \
XX(CB_header_field, "the on_header_field callback failed") \
XX(CB_header_value, "the on_header_value callback failed") \
XX(CB_headers_complete, "the on_headers_complete callback failed") \
XX(CB_body, "the on_body callback failed") \
XX(CB_message_complete, "the on_message_complete callback failed") \
XX(CB_status, "the on_status callback failed") \
XX(CB_chunk_header, "the on_chunk_header callback failed") \
XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
\
\
XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
XX(HEADER_OVERFLOW, \
"too many header bytes seen; overflow detected") \
XX(CLOSED_CONNECTION, \
"data received after completed connection: close message") \
XX(INVALID_VERSION, "invalid HTTP version") \
XX(INVALID_STATUS, "invalid HTTP status code") \
XX(INVALID_METHOD, "invalid HTTP method") \
XX(INVALID_URL, "invalid URL") \
XX(INVALID_HOST, "invalid host") \
XX(INVALID_PORT, "invalid port") \
XX(INVALID_PATH, "invalid path") \
XX(INVALID_QUERY_STRING, "invalid query string") \
XX(INVALID_FRAGMENT, "invalid fragment") \
XX(LF_EXPECTED, "LF character expected") \
XX(INVALID_HEADER_TOKEN, "invalid character in header") \
XX(INVALID_CONTENT_LENGTH, \
"invalid character in content-length header") \
XX(UNEXPECTED_CONTENT_LENGTH, \
"unexpected content-length header") \
XX(INVALID_CHUNK_SIZE, \
"invalid character in chunk size header") \
XX(INVALID_CONSTANT, "invalid constant string") \
XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
XX(STRICT, "strict mode assertion failed") \
XX(PAUSED, "parser is paused") \
XX(UNKNOWN, "an unknown error occurred") \
XX(INVALID_TRANSFER_ENCODING, \
"request has invalid transfer-encoding") \
#define HTTP_ERRNO_GEN(n, s) HPE_##n,
enum http_errno {
HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
};
#undef HTTP_ERRNO_GEN
#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
struct http_parser {
unsigned int type : 2;
unsigned int flags : 8;
unsigned int state : 7;
unsigned int header_state : 7;
unsigned int index : 5;
unsigned int extra_flags : 2;
unsigned int lenient_http_headers : 1;
uint32_t nread;
uint64_t content_length;
unsigned short http_major;
unsigned short http_minor;
unsigned int status_code : 16;
unsigned int method : 8;
unsigned int http_errno : 7;
unsigned int upgrade : 1;
void *data;
};
struct http_parser_settings {
http_cb on_message_begin;
http_data_cb on_url;
http_data_cb on_status;
http_data_cb on_header_field;
http_data_cb on_header_value;
http_cb on_headers_complete;
http_data_cb on_body;
http_cb on_message_complete;
http_cb on_chunk_header;
http_cb on_chunk_complete;
};
enum http_parser_url_fields
{ UF_SCHEMA = 0
, UF_HOST = 1
, UF_PORT = 2
, UF_PATH = 3
, UF_QUERY = 4
, UF_FRAGMENT = 5
, UF_USERINFO = 6
, UF_MAX = 7
};
struct http_parser_url {
uint16_t field_set;
uint16_t port;
struct {
uint16_t off;
uint16_t len;
} field_data[UF_MAX];
};
unsigned long http_parser_version(void);
void http_parser_init(http_parser *parser, enum http_parser_type type);
void http_parser_settings_init(http_parser_settings *settings);
size_t http_parser_execute(http_parser *parser,
const http_parser_settings *settings,
const char *data,
size_t len);
int http_should_keep_alive(const http_parser *parser);
const char *http_method_str(enum http_method m);
const char *http_status_str(enum http_status s);
const char *http_errno_name(enum http_errno err);
const char *http_errno_description(enum http_errno err);
void http_parser_url_init(struct http_parser_url *u);
int http_parser_parse_url(const char *buf, size_t buflen,
int is_connect,
struct http_parser_url *u);
void http_parser_pause(http_parser *parser, int paused);
int http_body_is_final(const http_parser *parser);
void http_parser_set_max_header_size(uint32_t size);
#ifdef __cplusplus
}
#endif
#endif
+40
View File
@@ -0,0 +1,40 @@
#pragma once
#include <httq/httq_export.h>
#include <QObject>
#include <QHostAddress>
class QTcpServer;
class QWebSocketServer;
namespace httq
{
class HttpRequest;
class LoggerFactory;
class Logger;
class HTTQ_EXPORT AbstractServer : public QObject
{
Q_OBJECT
public:
explicit AbstractServer(QObject *parent = nullptr);
virtual bool newHttpConnection(HttpRequest *request) = 0;
virtual QWebSocketServer *webSocketServer() const { return nullptr; }
virtual LoggerFactory *createLoggerFactory();
LoggerFactory *getLoggerFactory();
bool listen(qint16 port, const QHostAddress &host = QHostAddress::Any);
private slots:
void slotNewConnection();
private:
QTcpServer *mSvr;
LoggerFactory *mLoggerFactory { nullptr };
Logger *mLogger;
};
}
+53
View File
@@ -0,0 +1,53 @@
#pragma once
#include <httq/httq_export.h>
#include <httq/Logger.h>
#include <QObject>
#include <QScopedPointer>
class QIODevice;
namespace httq
{
class HttpRequest;
class HTTQ_EXPORT DataStream : public QObject
{
Q_OBJECT
private:
explicit DataStream(QIODevice *from, QIODevice *to, const QByteArray &bodyPartial,
qint64 fileLength, size_t bufferSize, LoggerFactory *loggerFactory,
QObject *parent = nullptr);
public:
~DataStream();
private:
friend class HttpRequest;
void readNext();
void writeNext();
bool mReadyWrite { true };
QByteArray mBuffer;
QByteArray mBodyPartial;
QIODevice *mFrom;
QIODevice *mTo;
qint64 mBufferSize;
qint64 alreadyRead { 0 };
qint64 mFileLength;
bool mFromSupportsSignals { false };
bool mToSupportsSignals { false };
QScopedPointer<Logger> mLogger;
signals:
void signalDone();
};
}
+112
View File
@@ -0,0 +1,112 @@
#pragma once
#include <httq/httq_export.h>
#include <http_parser.h>
#include <QObject>
#include <QByteArray>
#include <QUrl>
#include <QMap>
#include <QString>
#include <QUrlQuery>
class QTcpSocket;
class QIODevice;
namespace httq
{
class AbstractServer;
class Logger;
class DataStream;
class HttpRequest;
struct HttpRequestData
{
QByteArray mData;
QUrl mUrl;
QString mCurrentHeaderField;
QMap<QString, QString> mHeaders;
bool mDone { false };
QByteArray mBodyPartial;
QUrlQuery mQuery;
HttpRequest *mHttpRquest;
};
class HTTQ_EXPORT HttpRequest : public QObject
{
Q_OBJECT
private:
explicit HttpRequest(QTcpSocket *cli, AbstractServer *parent = nullptr);
public:
~HttpRequest();
qint64 availableBytes() const;
http_method method() const { return (enum http_method)mParser.method; }
QString methodString() const { return QString::fromUtf8(http_method_str(method())); }
const QUrl &url() const { return mData.mUrl; }
const QUrlQuery &query() const { return mData.mQuery; }
qint64 contentLength() const { return mContentLength; }
const QMap<QString, QString> &requestHeaders() const { return mData.mHeaders; }
Logger *logger() { return mLogger; }
QString toString() const;
void addHeader(const QString &key, const QString &value);
void write(int status, const QByteArray &data, const QString &contentType);
void write(int status, const QJsonValue &jsonValue);
DataStream *createDataStreamFromClient(QIODevice *to, qint64 fileSize, qint64 bufferSize = 1024 * 1024);
DataStream *createDataStreamToClient(int status, const QString &contentType, QIODevice *from, qint64 fileSize, qint64 bufferSize = 1024 * 1024);
friend class DataStream;
private:
friend class AbstractServer;
void writeStatusHeader(int status);
void writeHeaders();
static int on_message_begin(http_parser *parser);
static int on_url(http_parser *parser, const char *at, size_t length);
static int on_status(http_parser *parser, const char *at, size_t length);
static int on_header_field(http_parser *parser, const char *at, size_t length);
static int on_header_value(http_parser *parser, const char *at, size_t length);
static int on_headers_complete(http_parser *parser);
static int on_body(http_parser *parser, const char *at, size_t length);
static int on_message_complete(http_parser *parser);
static int on_chunk_header(http_parser *parser);
static int on_chunk_complete(http_parser *parser);
static HttpRequestData *data(http_parser *parser) { return static_cast<HttpRequestData *>(parser->data); }
static Logger *dataLogger(http_parser *parser) { return static_cast<HttpRequestData *>(parser->data)->mHttpRquest->logger(); }
QTcpSocket *mCli;
http_parser mParser;
http_parser_settings mParserSettings;
HttpRequestData mData;
bool mDone { false };
qint64 mContentLength { 0 };
qint64 alreadyRead { 0 };
QMap<QString, QString> mHeaders;
Logger *mLogger;
private slots:
void slotReadyRead();
signals:
void signalUpgrade(QTcpSocket *sock);
void signalReady();
};
}
+51
View File
@@ -0,0 +1,51 @@
#pragma once
#include <httq/httq_export.h>
#include <QScopedPointer>
#include <QPointer>
namespace httq
{
class Logger;
class HTTQ_EXPORT LoggerFactory : public QObject
{
Q_OBJECT
public:
LoggerFactory(QObject *parent)
: QObject(parent)
{}
virtual Logger *createLogger(QObject *parent);
};
class HTTQ_EXPORT Logger : public QObject
{
Q_OBJECT
public:
Logger(LoggerFactory *loggerFactory, QObject *parent);
virtual void debug(const QString &message);
virtual void warning(const QString &message);
virtual void error(const QString &message);
virtual LoggerFactory *getLoggerFactory() const
{
Q_ASSERT(mLoggerFactory);
return mLoggerFactory;
}
protected:
QPointer<LoggerFactory> mLoggerFactory;
private:
QDebug qtLogger(const QString &type, QDebug debug);
QString mPath;
};
}