mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-23 10:22:48 +00:00
kutils: rewrite KHTTP and move it to kio library
same functionality but without extra dependencies and better throughput Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
parent
0c2fa57bdd
commit
969ddd830f
13 changed files with 520 additions and 592 deletions
|
@ -290,15 +290,6 @@ set_package_properties(OpenSSL PROPERTIES
|
|||
PURPOSE "Store password securely"
|
||||
)
|
||||
|
||||
# v0.9.71+ required for MHD_Result
|
||||
kde4_optional_find_package(Libmicrohttpd 0.9.71)
|
||||
set_package_properties(Libmicrohttpd PROPERTIES
|
||||
DESCRIPTION "Small C library that is supposed to make it easy to run an HTTP server as part of another application"
|
||||
URL "https://www.gnu.org/software/libmicrohttpd/"
|
||||
TYPE RECOMMENDED
|
||||
PURPOSE "HTTP(S) server"
|
||||
)
|
||||
|
||||
kde4_optional_find_package(LibArchive 3.0.3)
|
||||
set_package_properties(LibArchive PROPERTIES
|
||||
DESCRIPTION "Multi-format archive and compression library"
|
||||
|
|
|
@ -21,7 +21,6 @@ kde4_bool_to_01(BZIP2_FOUND HAVE_BZIP2) # karchive
|
|||
kde4_bool_to_01(LIBLZMA_FOUND HAVE_LIBLZMA) # karchive
|
||||
kde4_bool_to_01(AVAHI_FOUND HAVE_AVAHI) # kdnssd
|
||||
kde4_bool_to_01(EXIV2_FOUND HAVE_EXIV2) # kexiv2
|
||||
kde4_bool_to_01(LIBMICROHTTPD_FOUND HAVE_LIBMICROHTTPD) # khttp
|
||||
kde4_bool_to_01(MPV_FOUND HAVE_MPV) # kmediaplayer
|
||||
kde4_bool_to_01(OPENSSL_FOUND HAVE_OPENSSL) # kpasswdstore
|
||||
kde4_bool_to_01(ACL_FOUND HAVE_POSIX_ACL) # kio
|
||||
|
|
|
@ -17,7 +17,6 @@ set(cmakeFilesDontInstall
|
|||
FindLibRaw.cmake
|
||||
FindLibJPEG.cmake
|
||||
FindOpenJPEG.cmake
|
||||
FindLibmicrohttpd.cmake
|
||||
FindWebP.cmake
|
||||
)
|
||||
|
||||
|
|
|
@ -61,7 +61,6 @@
|
|||
# KDE4_KPASSWDSTORE_LIBS - the kpasswdstore library and all depending libraries
|
||||
# KDE4_KPOWERMANAGER_LIBS - the kpowermanager library and all depending libraries
|
||||
# KDE4_KDNSSD_LIBS - the kdnssd library and all depending libraries
|
||||
# KDE4_KHTTP_LIBS - the khttp library and all depending libraries
|
||||
# KDE4_KARCHIVE_LIBS - the karchive library and all depending libraries
|
||||
#
|
||||
# The variable INSTALL_TARGETS_DEFAULT_ARGS can be used when installing libraries
|
||||
|
@ -241,7 +240,6 @@ set(_kde_libraries
|
|||
kpasswdstore
|
||||
kpowermanager
|
||||
kdnssd
|
||||
khttp
|
||||
karchive
|
||||
kemail
|
||||
kfile
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
# Try to find Libmicrohttpd library, once done this will define:
|
||||
#
|
||||
# LIBMICROHTTPD_FOUND - system has Libmicrohttpd
|
||||
# LIBMICROHTTPD_INCLUDE_DIR - the Libmicrohttpd include directory
|
||||
# LIBMICROHTTPD_LIBRARIES - the libraries needed to use Libmicrohttpd
|
||||
#
|
||||
# Copyright (c) 2022 Ivailo Monev <xakepa10@gmail.com>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(PC_LIBMICROHTTPD QUIET libmicrohttpd)
|
||||
|
||||
set(LIBMICROHTTPD_INCLUDE_DIR ${PC_LIBMICROHTTPD_INCLUDE_DIRS})
|
||||
set(LIBMICROHTTPD_LIBRARIES ${PC_LIBMICROHTTPD_LIBRARIES})
|
||||
set(LIBMICROHTTPD_VERSION ${PC_LIBMICROHTTPD_VERSION})
|
||||
set(LIBMICROHTTPD_DEFINITIONS ${PC_LIBMICROHTTPD_CFLAGS_OTHER})
|
||||
|
||||
if(NOT LIBMICROHTTPD_INCLUDE_DIR OR NOT LIBMICROHTTPD_LIBRARIES)
|
||||
find_path(LIBMICROHTTPD_INCLUDE_DIR
|
||||
NAMES microhttpd.h
|
||||
HINTS $ENV{LIBMICROHTTPDDIR}/include
|
||||
)
|
||||
|
||||
find_library(LIBMICROHTTPD_LIBRARIES
|
||||
NAMES microhttpd
|
||||
HINTS $ENV{LIBMICROHTTPDDIR}/lib
|
||||
)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Libmicrohttpd
|
||||
VERSION_VAR LIBMICROHTTPD_VERSION
|
||||
REQUIRED_VARS LIBMICROHTTPD_LIBRARIES LIBMICROHTTPD_INCLUDE_DIR
|
||||
)
|
||||
|
||||
mark_as_advanced(LIBMICROHTTPD_INCLUDE_DIR LIBMICROHTTPD_LIBRARIES)
|
|
@ -56,9 +56,6 @@
|
|||
/* Define to 1 if you have Exiv2 */
|
||||
#cmakedefine HAVE_EXIV2 1
|
||||
|
||||
/* Define to 1 if you have Libmicrohttpd */
|
||||
#cmakedefine HAVE_LIBMICROHTTPD 1
|
||||
|
||||
/* Define to 1 if you have MPV */
|
||||
#cmakedefine HAVE_MPV 1
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@
|
|||
7021 kbuildsycoca4
|
||||
7022 kurifilter
|
||||
7043 kio (bookmarks)
|
||||
7050 kio (KHTTP)
|
||||
7101 kio_file
|
||||
7102 kio_ftp
|
||||
7103 kio_http
|
||||
|
@ -69,7 +70,6 @@
|
|||
51004 kpasswdstore
|
||||
51005 kpowermanager
|
||||
51006 kdnssd
|
||||
51007 khttp
|
||||
51009 karchive
|
||||
51010 kemail
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ set(kiocore_STAT_SRCS
|
|||
kio/krun.cpp
|
||||
kio/kurifilter.cpp
|
||||
kio/kurlcompletion.cpp
|
||||
kio/khttp.cpp
|
||||
kio/netaccess.cpp
|
||||
kio/paste.cpp
|
||||
kio/pastedialog.cpp
|
||||
|
@ -233,6 +234,7 @@ install(
|
|||
kio/krun.h
|
||||
kio/kurifilter.h
|
||||
kio/kurlcompletion.h
|
||||
kio/khttp.h
|
||||
kfile/kabstractfilemodule.h
|
||||
kfile/kabstractfilewidget.h
|
||||
kfile/kdiskfreespaceinfo.h
|
||||
|
|
515
kio/kio/khttp.cpp
Normal file
515
kio/kio/khttp.cpp
Normal file
|
@ -0,0 +1,515 @@
|
|||
/* This file is part of the KDE libraries
|
||||
Copyright (C) 2022 Ivailo Monev <xakepa10@gmail.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License version 2, as published by the Free Software Foundation.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "khttp.h"
|
||||
#include "krandom.h"
|
||||
#include "kdebug.h"
|
||||
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
#include <QDateTime>
|
||||
#include <QCoreApplication>
|
||||
#include <QThread>
|
||||
#include <QFile>
|
||||
#include <qplatformdefs.h>
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#define KHTTP_TIMEOUT 250
|
||||
#define KHTTP_SLEEPTIME 50
|
||||
#define KHTTP_BUFFSIZE 1024 * 1000 // 1MB
|
||||
|
||||
// see kdebug.areas
|
||||
static const int s_khttpdebugarea = 7050;
|
||||
|
||||
// for reference:
|
||||
// https://datatracker.ietf.org/doc/html/rfc7230
|
||||
// https://datatracker.ietf.org/doc/html/rfc7235
|
||||
// https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
|
||||
|
||||
static QByteArray HTTPStatusToBytes(const ushort httpstatus)
|
||||
{
|
||||
switch (httpstatus) {
|
||||
case 100: {
|
||||
return QByteArray("Continue");
|
||||
}
|
||||
case 101: {
|
||||
return QByteArray("Switching Protocols");
|
||||
}
|
||||
case 102: {
|
||||
return QByteArray("Processing");
|
||||
}
|
||||
case 103: {
|
||||
return QByteArray("Early Hints");
|
||||
}
|
||||
case 200: {
|
||||
return QByteArray("OK");
|
||||
}
|
||||
case 201: {
|
||||
return QByteArray("Created");
|
||||
}
|
||||
case 202: {
|
||||
return QByteArray("Accepted");
|
||||
}
|
||||
case 203: {
|
||||
return QByteArray("Non-Authoritative Information");
|
||||
}
|
||||
case 204: {
|
||||
return QByteArray("No Content");
|
||||
}
|
||||
case 205: {
|
||||
return QByteArray("Reset Content");
|
||||
}
|
||||
case 206: {
|
||||
return QByteArray("Partial Content");
|
||||
}
|
||||
case 207: {
|
||||
return QByteArray("Multi-Status");
|
||||
}
|
||||
case 208: {
|
||||
return QByteArray("Already Reported");
|
||||
}
|
||||
case 226: {
|
||||
return QByteArray("IM Used");
|
||||
}
|
||||
case 300: {
|
||||
return QByteArray("Multiple Choices");
|
||||
}
|
||||
case 301: {
|
||||
return QByteArray("Moved Permanently");
|
||||
}
|
||||
case 302: {
|
||||
return QByteArray("Found");
|
||||
}
|
||||
case 303: {
|
||||
return QByteArray("See Other");
|
||||
}
|
||||
case 304: {
|
||||
return QByteArray("Not Modified");
|
||||
}
|
||||
case 305: {
|
||||
return QByteArray("Use Proxy");
|
||||
}
|
||||
case 306: {
|
||||
return QByteArray("Switch Proxy");
|
||||
}
|
||||
case 307: {
|
||||
return QByteArray("Temporary Redirect");
|
||||
}
|
||||
case 308: {
|
||||
return QByteArray("Permanent Redirect");
|
||||
}
|
||||
case 400: {
|
||||
return QByteArray("Bad Request");
|
||||
}
|
||||
case 401: {
|
||||
return QByteArray("Unauthorized");
|
||||
}
|
||||
case 402: {
|
||||
return QByteArray("Payment Required");
|
||||
}
|
||||
case 403: {
|
||||
return QByteArray("Forbidden");
|
||||
}
|
||||
case 404: {
|
||||
return QByteArray("Not Found");
|
||||
}
|
||||
case 405: {
|
||||
return QByteArray("Method Not Allowed");
|
||||
}
|
||||
case 406: {
|
||||
return QByteArray("Not Acceptable");
|
||||
}
|
||||
case 407: {
|
||||
return QByteArray("Proxy Authentication Required");
|
||||
}
|
||||
case 408: {
|
||||
return QByteArray("Request Timeout");
|
||||
}
|
||||
case 409: {
|
||||
return QByteArray("Conflict");
|
||||
}
|
||||
case 410: {
|
||||
return QByteArray("Gone");
|
||||
}
|
||||
case 411: {
|
||||
return QByteArray("Length Required");
|
||||
}
|
||||
case 412: {
|
||||
return QByteArray("Precondition Failed");
|
||||
}
|
||||
case 413: {
|
||||
return QByteArray("Payload Too Large");
|
||||
}
|
||||
case 414: {
|
||||
return QByteArray("URI Too Long");
|
||||
}
|
||||
case 415: {
|
||||
return QByteArray("Unsupported Media Type");
|
||||
}
|
||||
case 416: {
|
||||
return QByteArray("Range Not Satisfiable");
|
||||
}
|
||||
case 417: {
|
||||
return QByteArray("Expectation Failed");
|
||||
}
|
||||
case 418: {
|
||||
return QByteArray("I'm a teapot");
|
||||
}
|
||||
case 421: {
|
||||
return QByteArray("Misdirected Request");
|
||||
}
|
||||
case 422: {
|
||||
return QByteArray("Unprocessable Entity");
|
||||
}
|
||||
case 423: {
|
||||
return QByteArray("Locked");
|
||||
}
|
||||
case 424: {
|
||||
return QByteArray("Failed Dependency");
|
||||
}
|
||||
case 425: {
|
||||
return QByteArray("Too Early");
|
||||
}
|
||||
case 426: {
|
||||
return QByteArray("Upgrade Required");
|
||||
}
|
||||
case 428: {
|
||||
return QByteArray("Precondition Required");
|
||||
}
|
||||
case 429: {
|
||||
return QByteArray("Too Many Requests");
|
||||
}
|
||||
case 431: {
|
||||
return QByteArray("Request Header Fields Too Large");
|
||||
}
|
||||
case 451: {
|
||||
return QByteArray("Unavailable For Legal Reasons");
|
||||
}
|
||||
case 500: {
|
||||
return QByteArray("Internal Server Error");
|
||||
}
|
||||
case 501: {
|
||||
return QByteArray("Not Implemented");
|
||||
}
|
||||
case 502: {
|
||||
return QByteArray("Bad Gateway");
|
||||
}
|
||||
case 503: {
|
||||
return QByteArray("Service Unavailable");
|
||||
}
|
||||
case 504: {
|
||||
return QByteArray("Gateway Timeout");
|
||||
}
|
||||
case 505: {
|
||||
return QByteArray("HTTP Version Not Supported");
|
||||
}
|
||||
case 506: {
|
||||
return QByteArray("Variant Also Negotiates");
|
||||
}
|
||||
case 507: {
|
||||
return QByteArray("Insufficient Storage");
|
||||
}
|
||||
case 508: {
|
||||
return QByteArray("Loop Detected");
|
||||
}
|
||||
case 510: {
|
||||
return QByteArray("Not Extended");
|
||||
}
|
||||
case 511: {
|
||||
return QByteArray("Network Authentication Required");
|
||||
}
|
||||
}
|
||||
kWarning(s_khttpdebugarea) << "unknown HTTP status code" << httpstatus;
|
||||
return QByteArray("OK");
|
||||
}
|
||||
|
||||
static QByteArray HTTPStatusToContent(const ushort httpstatus)
|
||||
{
|
||||
QByteArray httpdata("<html>");
|
||||
httpdata.append(HTTPStatusToBytes(httpstatus));
|
||||
httpdata.append("</html>");
|
||||
return httpdata;
|
||||
}
|
||||
|
||||
static KHTTPHeaders responseHeaders(const bool authenticate)
|
||||
{
|
||||
KHTTPHeaders khttpheaders;
|
||||
const QString httpserver = QCoreApplication::applicationName();
|
||||
khttpheaders.insert("Server", httpserver.toAscii());
|
||||
const QString httpdate = QDateTime::currentDateTimeUtc().toString("ddd, dd MMM yyyy hh:mm:ss") + QLatin1String(" GMT");
|
||||
khttpheaders.insert("Date", httpdate.toAscii());
|
||||
if (authenticate) {
|
||||
const QString httpauthenticate = QString::fromLatin1("Basic realm=") + httpserver;
|
||||
khttpheaders.insert("WWW-Authenticate", httpauthenticate.toAscii());
|
||||
}
|
||||
return khttpheaders;
|
||||
}
|
||||
|
||||
static QByteArray responseData(const ushort httpstatus, const KHTTPHeaders &httpheaders, const qint64 datasize)
|
||||
{
|
||||
QByteArray httpdata("HTTP/1.1 ");
|
||||
httpdata.append(QByteArray::number(httpstatus));
|
||||
httpdata.append(" ");
|
||||
httpdata.append(HTTPStatusToBytes(httpstatus));
|
||||
httpdata.append("\r\n");
|
||||
|
||||
bool hascontenttype = false;
|
||||
foreach (const QByteArray &httpkey, httpheaders.keys()) {
|
||||
if (qstricmp(httpkey.constData(), "Content-Type") == 0) {
|
||||
hascontenttype = true;
|
||||
}
|
||||
httpdata.append(httpkey);
|
||||
httpdata.append(": ");
|
||||
httpdata.append(httpheaders.value(httpkey));
|
||||
httpdata.append("\r\n");
|
||||
}
|
||||
|
||||
if (!hascontenttype) {
|
||||
kDebug(s_khttpdebugarea) << "adding Content-Type";
|
||||
httpdata.append("Content-Type: text/html\r\n");
|
||||
}
|
||||
|
||||
httpdata.append("Content-Length: ");
|
||||
httpdata.append(QByteArray::number(datasize));
|
||||
httpdata.append("\r\n\r\n");
|
||||
|
||||
return httpdata;
|
||||
}
|
||||
|
||||
class KHTTPHeadersParser
|
||||
{
|
||||
public:
|
||||
void parseHeaders(const QByteArray &header, const bool authenticate);
|
||||
|
||||
QByteArray path() const { return m_path; }
|
||||
QByteArray authUser() const { return m_authuser; }
|
||||
QByteArray authPass() const { return m_authpass; }
|
||||
|
||||
private:
|
||||
QByteArray m_path;
|
||||
QByteArray m_authuser;
|
||||
QByteArray m_authpass;
|
||||
};
|
||||
|
||||
void KHTTPHeadersParser::parseHeaders(const QByteArray &header, const bool authenticate)
|
||||
{
|
||||
bool firstline = true;
|
||||
foreach (const QByteArray &line, header.split('\n')) {
|
||||
if (line.isEmpty()) {
|
||||
firstline = false;
|
||||
continue;
|
||||
}
|
||||
if (firstline) {
|
||||
const QList<QByteArray> splitline = line.split(' ');
|
||||
if (splitline.size() >= 3) {
|
||||
m_path = splitline.at(1).trimmed();
|
||||
}
|
||||
} else if (authenticate && qstrnicmp(line.constData(), "Authorization", 13) == 0) {
|
||||
const QList<QByteArray> splitline = line.split(':');
|
||||
// qDebug() << Q_FUNC_INFO << "auth" << splitline;
|
||||
if (splitline.size() == 2) {
|
||||
const QByteArray authdata = splitline.at(1).trimmed();
|
||||
const QList<QByteArray> splitauth = authdata.split(' ');
|
||||
if (splitauth.size() == 2) {
|
||||
const QByteArray authbase64 = QByteArray::fromBase64(splitauth.at(1).trimmed());
|
||||
const QList<QByteArray> splitbase64 = authbase64.split(':');
|
||||
if (splitbase64.size() == 2) {
|
||||
m_authuser = splitbase64.at(0);
|
||||
m_authpass = splitbase64.at(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
firstline = false;
|
||||
}
|
||||
// qDebug() << Q_FUNC_INFO << m_path << m_authuser << m_authpass;
|
||||
}
|
||||
|
||||
class KHTTPPrivate : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KHTTPPrivate(QObject *parent);
|
||||
|
||||
private Q_SLOTS:
|
||||
void slotNewConnection();
|
||||
|
||||
public:
|
||||
QByteArray authusername;
|
||||
QByteArray authpassword;
|
||||
QByteArray authmessage;
|
||||
QString errorstring;
|
||||
QTcpServer* tcpserver;
|
||||
};
|
||||
|
||||
KHTTPPrivate::KHTTPPrivate(QObject *parent)
|
||||
: QObject(parent),
|
||||
tcpserver(nullptr)
|
||||
{
|
||||
// NOTE: the default maximum for pending connections is 30
|
||||
tcpserver = new QTcpServer(this);
|
||||
connect(tcpserver, SIGNAL(newConnection()), this, SLOT(slotNewConnection()));
|
||||
}
|
||||
|
||||
void KHTTPPrivate::slotNewConnection()
|
||||
{
|
||||
QTcpSocket *client = tcpserver->nextPendingConnection();
|
||||
kDebug(s_khttpdebugarea) << "new client" << client->peerAddress() << client->peerPort();
|
||||
|
||||
if (!client->waitForReadyRead()) {
|
||||
client->disconnectFromHost();
|
||||
client->deleteLater();
|
||||
kWarning(s_khttpdebugarea) << "client timed out";
|
||||
return;
|
||||
}
|
||||
|
||||
const QByteArray clientdata = client->readAll();
|
||||
// qDebug() << Q_FUNC_INFO << "request" << clientdata;
|
||||
|
||||
const bool requiresauthorization = (!authusername.isEmpty() && !authpassword.isEmpty());
|
||||
|
||||
KHTTPHeadersParser khttpheadersparser;
|
||||
khttpheadersparser.parseHeaders(clientdata, requiresauthorization);
|
||||
// qDebug() << Q_FUNC_INFO << "url" << khttpheadersparser.path();
|
||||
|
||||
KHTTPHeaders khttpheaders = responseHeaders(requiresauthorization);
|
||||
if (requiresauthorization &&
|
||||
(khttpheadersparser.authUser() != authusername || khttpheadersparser.authPass() != authpassword)) {
|
||||
kDebug(s_khttpdebugarea) << "sending unauthorized to client";
|
||||
const QByteArray httpdata = responseData(401, khttpheaders, authmessage.size());
|
||||
client->write(httpdata);
|
||||
client->flush();
|
||||
client->write(authmessage);
|
||||
client->flush();
|
||||
kDebug(s_khttpdebugarea) << "done with client" << client->peerAddress() << client->peerPort();
|
||||
client->disconnectFromHost();
|
||||
client->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
KHTTP* khttp = qobject_cast<KHTTP*>(parent());
|
||||
Q_ASSERT(khttp);
|
||||
const QByteArray responseurl = khttpheadersparser.path();
|
||||
QByteArray responsedata;
|
||||
ushort responsestatus = 404;
|
||||
QString responsefilepath;
|
||||
khttp->respond(responseurl, &responsedata, &responsestatus, &khttpheaders, &responsefilepath);
|
||||
|
||||
if (!responsefilepath.isEmpty()) {
|
||||
QFile httpfile(responsefilepath);
|
||||
if (httpfile.open(QFile::ReadOnly)) {
|
||||
kDebug(s_khttpdebugarea) << "sending file to client" << responsefilepath << khttpheaders;
|
||||
const QByteArray httpdata = responseData(responsestatus, khttpheaders, httpfile.size());
|
||||
client->write(httpdata);
|
||||
client->flush();
|
||||
|
||||
QByteArray httpbuffer(KHTTP_BUFFSIZE, '\0');
|
||||
qint64 httpfileresult = httpfile.read(httpbuffer.data(), httpbuffer.size());
|
||||
while (httpfileresult > 0) {
|
||||
client->write(httpbuffer.constData(), httpfileresult);
|
||||
client->flush();
|
||||
|
||||
// TODO: this check should be done before every write
|
||||
if (client->state() != QTcpSocket::ConnectedState) {
|
||||
kDebug(s_khttpdebugarea) << "client disconnected while writing file" << client->peerAddress() << client->peerPort();
|
||||
break;
|
||||
}
|
||||
|
||||
QCoreApplication::processEvents(QEventLoop::AllEvents, KHTTP_TIMEOUT);
|
||||
QThread::msleep(KHTTP_SLEEPTIME);
|
||||
|
||||
httpfileresult = httpfile.read(httpbuffer.data(), httpbuffer.size());
|
||||
}
|
||||
} else {
|
||||
kWarning(s_khttpdebugarea) << "could not open" << responsefilepath;
|
||||
khttpheaders = responseHeaders(false);
|
||||
const QByteArray data500 = HTTPStatusToContent(500);
|
||||
const QByteArray httpdata = responseData(500, khttpheaders, data500.size());
|
||||
client->write(httpdata);
|
||||
client->flush();
|
||||
client->write(data500);
|
||||
}
|
||||
client->flush();
|
||||
kDebug(s_khttpdebugarea) << "done with client" << client->peerAddress() << client->peerPort();
|
||||
client->disconnectFromHost();
|
||||
client->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
kDebug(s_khttpdebugarea) << "sending data to client";
|
||||
if (responsedata.isEmpty()) {
|
||||
responsedata = HTTPStatusToContent(responsestatus);
|
||||
}
|
||||
const QByteArray httpdata = responseData(responsestatus, khttpheaders, responsedata.size());
|
||||
client->write(httpdata);
|
||||
client->flush();
|
||||
client->write(responsedata);
|
||||
client->flush();
|
||||
kDebug(s_khttpdebugarea) << "done with client" << client->peerAddress() << client->peerPort();
|
||||
client->disconnectFromHost();
|
||||
client->deleteLater();
|
||||
}
|
||||
|
||||
KHTTP::KHTTP(QObject *parent)
|
||||
: QObject(parent),
|
||||
d(new KHTTPPrivate(this))
|
||||
{
|
||||
}
|
||||
|
||||
KHTTP::~KHTTP()
|
||||
{
|
||||
stop();
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool KHTTP::setAuthenticate(const QByteArray &username, const QByteArray &password, const QString &message)
|
||||
{
|
||||
d->errorstring.clear();
|
||||
if (username.isEmpty() || password.isEmpty()) {
|
||||
d->errorstring = QString::fromLatin1("User name or password is empty");
|
||||
d->authusername.clear();
|
||||
d->authpassword.clear();
|
||||
return false;
|
||||
}
|
||||
d->authusername = username;
|
||||
d->authpassword = password;
|
||||
d->authmessage = message.toAscii();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KHTTP::start(const QHostAddress &address, quint16 port)
|
||||
{
|
||||
return d->tcpserver->listen(address, port);
|
||||
}
|
||||
|
||||
bool KHTTP::stop()
|
||||
{
|
||||
d->tcpserver->close();
|
||||
return true;
|
||||
}
|
||||
|
||||
QString KHTTP::errorString() const
|
||||
{
|
||||
if (!d->errorstring.isEmpty()) {
|
||||
return d->errorstring;
|
||||
}
|
||||
return d->tcpserver->errorString();
|
||||
}
|
||||
|
||||
#include "khttp.moc"
|
|
@ -19,7 +19,7 @@
|
|||
#ifndef KHTTP_H
|
||||
#define KHTTP_H
|
||||
|
||||
#include "khttp_export.h"
|
||||
#include <kio/kio_export.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
|
@ -43,7 +43,7 @@ class KHTTPPrivate;
|
|||
@since 4.21
|
||||
@see KHTTPHeaders
|
||||
*/
|
||||
class KHTTP_EXPORT KHTTP : public QObject
|
||||
class KIO_EXPORT KHTTP : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
@ -53,17 +53,6 @@ public:
|
|||
KHTTP(QObject *parent = nullptr);
|
||||
~KHTTP();
|
||||
|
||||
/*!
|
||||
@brief Sets @p keydata and @p certdata to be used for TLS/SSL handshake, if the key
|
||||
requires password it must also be provided as @p password.
|
||||
@note HTTP requests to the server address will not be redirected, clients must request
|
||||
HTTPS address. For example if TLS/SSL certificate is set "http://foo.bar" will not be
|
||||
accessible (no data is send) however "https://foo.bar" will be, unless external means are
|
||||
used to redirect the request. This is the case only when non-standard ports are used, if
|
||||
HTTP server runs on port 80 and HTTPS server runs on port 443 then both are accessible but
|
||||
clients will most likely be making requests to the HTTP server on port 80.
|
||||
*/
|
||||
bool setCertificate(const QByteArray &keydata, const QByteArray &certdata, const QByteArray &password = QByteArray());
|
||||
/*!
|
||||
@brief Sets @p username and @p password to be used for authentication with @p message as
|
||||
content to be send to clients when authentication fails.
|
|
@ -12,7 +12,6 @@ add_subdirectory(kexiv2)
|
|||
add_subdirectory(kpasswdstore)
|
||||
add_subdirectory(kpowermanager)
|
||||
add_subdirectory(kdnssd)
|
||||
add_subdirectory(khttp)
|
||||
add_subdirectory(karchive)
|
||||
add_subdirectory(kemail)
|
||||
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
if(LIBMICROHTTPD_FOUND)
|
||||
include_directories(${LIBMICROHTTPD_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
add_definitions(-DKDE_DEFAULT_DEBUG_AREA=51007)
|
||||
|
||||
set(khttp_LIB_SRCS
|
||||
khttp.cpp
|
||||
)
|
||||
|
||||
add_library(khttp ${LIBRARY_TYPE} ${khttp_LIB_SRCS})
|
||||
|
||||
target_link_libraries(khttp PUBLIC
|
||||
${KDE4_KDECORE_LIBS}
|
||||
${QT_QTNETWORK_LIBRARY}
|
||||
)
|
||||
|
||||
if(LIBMICROHTTPD_FOUND)
|
||||
target_link_libraries(khttp PRIVATE ${LIBMICROHTTPD_LIBRARIES})
|
||||
endif()
|
||||
|
||||
set_target_properties(khttp PROPERTIES
|
||||
VERSION ${GENERIC_LIB_VERSION}
|
||||
SOVERSION ${GENERIC_LIB_SOVERSION}
|
||||
)
|
||||
|
||||
generate_export_header(khttp)
|
||||
|
||||
install(
|
||||
FILES
|
||||
${CMAKE_CURRENT_BINARY_DIR}/khttp_export.h
|
||||
khttp.h
|
||||
DESTINATION ${KDE4_INCLUDE_INSTALL_DIR}
|
||||
COMPONENT Devel
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS khttp
|
||||
EXPORT kdelibsLibraryTargets
|
||||
${INSTALL_TARGETS_DEFAULT_ARGS}
|
||||
)
|
|
@ -1,482 +0,0 @@
|
|||
/* This file is part of the KDE libraries
|
||||
Copyright (C) 2022 Ivailo Monev <xakepa10@gmail.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License version 2, as published by the Free Software Foundation.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public License
|
||||
along with this library; see the file COPYING.LIB. If not, write to
|
||||
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "khttp.h"
|
||||
#include "kdebug.h"
|
||||
#include "kde_file.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QUrl>
|
||||
#include <QTimer>
|
||||
|
||||
#if defined(HAVE_LIBMICROHTTPD)
|
||||
# include <netinet/in.h>
|
||||
# include <microhttpd.h>
|
||||
#endif
|
||||
|
||||
static const int s_MHDPollInterval = 100;
|
||||
static const uint s_MHDIPConnectionLimit = 10;
|
||||
static const uint s_MHDConnectionLimit = (s_MHDIPConnectionLimit * s_MHDIPConnectionLimit);
|
||||
|
||||
class KHTTPPrivate : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KHTTPPrivate(QObject *parent);
|
||||
~KHTTPPrivate();
|
||||
|
||||
bool setCertificate(const QByteArray &keydata, const QByteArray &certdata, const QByteArray &password);
|
||||
bool setAuthenticate(const QByteArray &username, const QByteArray &password, const QString &message);
|
||||
bool start(const QHostAddress &address, quint16 port);
|
||||
bool stop();
|
||||
QString errorString() const;
|
||||
|
||||
#if defined(HAVE_LIBMICROHTTPD)
|
||||
static enum MHD_Result keyValueCallback(void *cls,
|
||||
enum MHD_ValueKind kind,
|
||||
const char *key,
|
||||
const char *value);
|
||||
static enum MHD_Result accessCallback(void *cls,
|
||||
struct MHD_Connection *connection,
|
||||
const char *url,
|
||||
const char *method,
|
||||
const char *version,
|
||||
const char *upload_data,
|
||||
size_t *upload_data_size,
|
||||
void **con_cls);
|
||||
static void loggerCallback(void *cls, const char *fm, va_list ap);
|
||||
static void panicCallback(void *cls, const char *file, unsigned int line, const char *reason);
|
||||
|
||||
private Q_SLOTS:
|
||||
void slotMHDPoll();
|
||||
|
||||
private:
|
||||
QByteArray m_tlskey;
|
||||
QByteArray m_tlscert;
|
||||
QByteArray m_tlspassword;
|
||||
QByteArray m_authusername;
|
||||
QByteArray m_authpassword;
|
||||
QByteArray m_authmessage;
|
||||
struct MHD_Daemon* m_mhddaemon;
|
||||
QTimer m_polltimer;
|
||||
QUrl m_url;
|
||||
#endif // HAVE_LIBMICROHTTPD
|
||||
private:
|
||||
QString m_errorstring;
|
||||
};
|
||||
|
||||
KHTTPPrivate::KHTTPPrivate(QObject *parent)
|
||||
: QObject(parent)
|
||||
#if defined(HAVE_LIBMICROHTTPD)
|
||||
, m_mhddaemon(nullptr),
|
||||
m_polltimer(this)
|
||||
#else
|
||||
, m_errorstring(QString::fromLatin1("Built without Libmicrohttpd"))
|
||||
#endif
|
||||
{
|
||||
#if defined(HAVE_LIBMICROHTTPD)
|
||||
connect(
|
||||
&m_polltimer, SIGNAL(timeout()),
|
||||
this, SLOT(slotMHDPoll())
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
KHTTPPrivate::~KHTTPPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
bool KHTTPPrivate::setCertificate(const QByteArray &keydata, const QByteArray &certdata, const QByteArray &password)
|
||||
{
|
||||
#if defined(HAVE_LIBMICROHTTPD)
|
||||
const enum MHD_Result mhdresult = MHD_is_feature_supported(MHD_FEATURE_TLS);
|
||||
if (mhdresult == MHD_NO) {
|
||||
m_errorstring = QString::fromLatin1("TLS is not supported");
|
||||
return false;
|
||||
}
|
||||
if (keydata.isEmpty() || certdata.isEmpty()) {
|
||||
m_errorstring = QString::fromLatin1("TLS key or certificate data is empty");
|
||||
m_tlskey.clear();
|
||||
m_tlscert.clear();
|
||||
return false;
|
||||
}
|
||||
m_tlskey = keydata;
|
||||
m_tlscert = certdata;
|
||||
m_tlspassword = password;
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool KHTTPPrivate::setAuthenticate(const QByteArray &username, const QByteArray &password, const QString &message)
|
||||
{
|
||||
#if defined(HAVE_LIBMICROHTTPD)
|
||||
const enum MHD_Result mhdresult = MHD_is_feature_supported(MHD_FEATURE_BASIC_AUTH);
|
||||
if (mhdresult == MHD_NO) {
|
||||
m_errorstring = QString::fromLatin1("Authentication is not supported");
|
||||
return false;
|
||||
}
|
||||
if (username.isEmpty() || password.isEmpty()) {
|
||||
m_errorstring = QString::fromLatin1("User name or password is empty");
|
||||
m_authusername.clear();
|
||||
m_authpassword.clear();
|
||||
return false;
|
||||
}
|
||||
m_authusername = username;
|
||||
m_authpassword = password;
|
||||
m_authmessage = message.toAscii();
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool KHTTPPrivate::start(const QHostAddress &address, quint16 port)
|
||||
{
|
||||
#if defined(HAVE_LIBMICROHTTPD)
|
||||
if (m_mhddaemon) {
|
||||
m_errorstring = QString::fromLatin1("Already started");
|
||||
return false;
|
||||
}
|
||||
// qDebug() << Q_FUNC_INFO << address.protocol();
|
||||
int mhdflags = MHD_NO_FLAG;
|
||||
if (!m_tlskey.isEmpty() && !m_tlscert.isEmpty()) {
|
||||
kDebug() << "Enabling TLS";
|
||||
mhdflags |= MHD_USE_TLS;
|
||||
}
|
||||
enum MHD_Result mhdresult = MHD_is_feature_supported(MHD_FEATURE_MESSAGES);
|
||||
if (mhdresult == MHD_NO) {
|
||||
kWarning() << "Messages are not supported";
|
||||
} else {
|
||||
kDebug() << "Enabling logger";
|
||||
mhdflags |= MHD_USE_ERROR_LOG;
|
||||
}
|
||||
switch (address.protocol()) {
|
||||
case QAbstractSocket::IPv4Protocol: {
|
||||
struct sockaddr_in socketaddress;
|
||||
::memset(&socketaddress, 0, sizeof(struct sockaddr_in));
|
||||
socketaddress.sin_family = AF_INET;
|
||||
socketaddress.sin_port = htons(port);
|
||||
socketaddress.sin_addr.s_addr = htonl(address.toIPv4Address());
|
||||
m_mhddaemon = MHD_start_daemon(
|
||||
mhdflags,
|
||||
0,
|
||||
NULL, NULL,
|
||||
KHTTPPrivate::accessCallback, this,
|
||||
MHD_OPTION_EXTERNAL_LOGGER, KHTTPPrivate::loggerCallback, this,
|
||||
MHD_OPTION_HTTPS_MEM_KEY, m_tlskey.constData(),
|
||||
MHD_OPTION_HTTPS_MEM_CERT, m_tlscert.constData(),
|
||||
MHD_OPTION_HTTPS_KEY_PASSWORD, m_tlspassword.constData(),
|
||||
MHD_OPTION_SOCK_ADDR, &socketaddress,
|
||||
MHD_OPTION_CONNECTION_LIMIT, s_MHDConnectionLimit,
|
||||
MHD_OPTION_PER_IP_CONNECTION_LIMIT, s_MHDIPConnectionLimit,
|
||||
MHD_OPTION_END
|
||||
);
|
||||
break;
|
||||
}
|
||||
case QAbstractSocket::IPv6Protocol: {
|
||||
mhdresult = MHD_is_feature_supported(MHD_FEATURE_IPv6);
|
||||
if (mhdresult == MHD_NO) {
|
||||
m_errorstring = QString::fromLatin1("IPv6 is not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct sockaddr_in6 socketaddress;
|
||||
::memset(&socketaddress, 0, sizeof(struct sockaddr_in6));
|
||||
socketaddress.sin6_family = AF_INET6;
|
||||
socketaddress.sin6_port = htons(port);
|
||||
const Q_IPV6ADDR ipv6address = address.toIPv6Address();
|
||||
::memcpy(&socketaddress.sin6_addr.s6_addr, &ipv6address, sizeof(ipv6address));
|
||||
m_mhddaemon = MHD_start_daemon(
|
||||
mhdflags | MHD_USE_IPv6,
|
||||
0,
|
||||
NULL, NULL,
|
||||
KHTTPPrivate::accessCallback, this,
|
||||
MHD_OPTION_EXTERNAL_LOGGER, KHTTPPrivate::loggerCallback, this,
|
||||
MHD_OPTION_HTTPS_MEM_KEY, m_tlskey.constData(),
|
||||
MHD_OPTION_HTTPS_MEM_CERT, m_tlscert.constData(),
|
||||
MHD_OPTION_HTTPS_KEY_PASSWORD, m_tlspassword.constData(),
|
||||
MHD_OPTION_SOCK_ADDR, &socketaddress,
|
||||
MHD_OPTION_CONNECTION_LIMIT, s_MHDConnectionLimit,
|
||||
MHD_OPTION_PER_IP_CONNECTION_LIMIT, s_MHDIPConnectionLimit,
|
||||
MHD_OPTION_END
|
||||
);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
m_errorstring = QString::fromLatin1("Invalid address protocol: %1").arg(int(address.protocol()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!m_mhddaemon) {
|
||||
// logger should provide a clue why
|
||||
kWarning() << "Could not start MHD";
|
||||
return false;
|
||||
}
|
||||
MHD_set_panic_func(KHTTPPrivate::panicCallback, this);
|
||||
m_polltimer.start(s_MHDPollInterval);
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool KHTTPPrivate::stop()
|
||||
{
|
||||
#if defined(HAVE_LIBMICROHTTPD)
|
||||
if (!m_mhddaemon) {
|
||||
kDebug() << "MHD not started";
|
||||
return true;
|
||||
}
|
||||
MHD_stop_daemon(m_mhddaemon);
|
||||
m_mhddaemon = nullptr;
|
||||
return true;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
QString KHTTPPrivate::errorString() const
|
||||
{
|
||||
return m_errorstring;
|
||||
}
|
||||
|
||||
#if defined(HAVE_LIBMICROHTTPD)
|
||||
void KHTTPPrivate::slotMHDPoll()
|
||||
{
|
||||
if (Q_UNLIKELY(!m_mhddaemon)) {
|
||||
kDebug() << "MHD daemon pointer is null";
|
||||
m_polltimer.stop();
|
||||
return;
|
||||
}
|
||||
|
||||
short pollcounter = 0;
|
||||
const union MHD_DaemonInfo* mhddaemoninfo = MHD_get_daemon_info(
|
||||
m_mhddaemon,
|
||||
MHD_DAEMON_INFO_CURRENT_CONNECTIONS,
|
||||
NULL
|
||||
);
|
||||
if (mhddaemoninfo) {
|
||||
pollcounter = mhddaemoninfo->num_connections;
|
||||
}
|
||||
pollcounter++;
|
||||
// pollcounter = s_MHDConnectionLimit;
|
||||
while (pollcounter) {
|
||||
const enum MHD_Result mhdresult = MHD_run(m_mhddaemon);
|
||||
if (Q_UNLIKELY(mhdresult == MHD_NO)) {
|
||||
kWarning() << "Could not poll";
|
||||
}
|
||||
pollcounter--;
|
||||
}
|
||||
}
|
||||
|
||||
enum MHD_Result KHTTPPrivate::keyValueCallback(void *cls,
|
||||
enum MHD_ValueKind kind,
|
||||
const char *key,
|
||||
const char *value)
|
||||
{
|
||||
// qDebug() << Q_FUNC_INFO << key << value;
|
||||
Q_UNUSED(kind);
|
||||
KHTTPPrivate* khttpprivate = static_cast<KHTTPPrivate*>(cls);
|
||||
khttpprivate->m_url.addQueryItem(QString::fromUtf8(key), QString::fromUtf8(value));
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
enum MHD_Result KHTTPPrivate::accessCallback(void *cls,
|
||||
struct MHD_Connection *connection,
|
||||
const char *url,
|
||||
const char *method,
|
||||
const char *version,
|
||||
const char *upload_data,
|
||||
size_t *upload_data_size,
|
||||
void **con_cls)
|
||||
{
|
||||
// qDebug() << Q_FUNC_INFO << url << method << version;
|
||||
Q_UNUSED(method);
|
||||
Q_UNUSED(version);
|
||||
Q_UNUSED(upload_data);
|
||||
Q_UNUSED(upload_data_size);
|
||||
Q_UNUSED(con_cls);
|
||||
|
||||
KHTTPPrivate* khttpprivate = static_cast<KHTTPPrivate*>(cls);
|
||||
KHTTP* khttp = qobject_cast<KHTTP*>(khttpprivate->parent());
|
||||
|
||||
if (!khttpprivate->m_authusername.isEmpty() && !khttpprivate->m_authpassword.isEmpty()) {
|
||||
char* mhdpassword = NULL;
|
||||
char* mhdusername = MHD_basic_auth_get_username_password(connection, &mhdpassword);
|
||||
if (!mhdpassword || !mhdusername
|
||||
|| khttpprivate->m_authusername != mhdusername
|
||||
|| khttpprivate->m_authpassword != mhdpassword) {
|
||||
if (mhdusername) {
|
||||
MHD_free(mhdusername);
|
||||
}
|
||||
if (mhdpassword) {
|
||||
MHD_free(mhdpassword);
|
||||
}
|
||||
struct MHD_Response *mhdresponse = MHD_create_response_from_buffer(
|
||||
khttpprivate->m_authmessage.size(), khttpprivate->m_authmessage.data(),
|
||||
MHD_RESPMEM_MUST_COPY
|
||||
);
|
||||
if (Q_UNLIKELY(!mhdresponse)) {
|
||||
kWarning() << "Could not create MHD auth response";
|
||||
return MHD_NO;
|
||||
}
|
||||
const QByteArray mhdrealm = QCoreApplication::applicationName().toAscii();
|
||||
const enum MHD_Result mhdresult = MHD_queue_basic_auth_fail_response(
|
||||
connection,
|
||||
mhdrealm.constData(),
|
||||
mhdresponse
|
||||
);
|
||||
MHD_destroy_response(mhdresponse);
|
||||
return mhdresult;
|
||||
}
|
||||
if (mhdusername) {
|
||||
MHD_free(mhdusername);
|
||||
}
|
||||
if (mhdpassword) {
|
||||
MHD_free(mhdpassword);
|
||||
}
|
||||
}
|
||||
|
||||
khttpprivate->m_url = QUrl(QString::fromAscii(url));
|
||||
MHD_get_connection_values(
|
||||
connection,
|
||||
MHD_GET_ARGUMENT_KIND,
|
||||
KHTTPPrivate::keyValueCallback,
|
||||
cls
|
||||
);
|
||||
|
||||
QByteArray khttpurl = khttpprivate->m_url.toEncoded();
|
||||
QByteArray mhdoutdata;
|
||||
ushort mhdouthttpstatus = MHD_HTTP_OK;
|
||||
KHTTPHeaders mhdouthttpheaders;
|
||||
QString mhdoutfilepath;
|
||||
khttp->respond(khttpurl, &mhdoutdata, &mhdouthttpstatus, &mhdouthttpheaders, &mhdoutfilepath);
|
||||
|
||||
enum MHD_Result mhdresult = MHD_NO;
|
||||
struct MHD_Response *mhdresponse = NULL;
|
||||
if (!mhdoutdata.isEmpty()) {
|
||||
mhdresponse = MHD_create_response_from_buffer(
|
||||
mhdoutdata.size(), mhdoutdata.data(),
|
||||
MHD_RESPMEM_MUST_COPY
|
||||
);
|
||||
} else if (!mhdoutfilepath.isEmpty()) {
|
||||
KDE_struct_stat statbuf;
|
||||
if (KDE::stat(mhdoutfilepath, &statbuf) == -1) {
|
||||
kWarning() << "Could not stat" << mhdoutfilepath;
|
||||
return MHD_NO;
|
||||
}
|
||||
if (!S_ISREG(statbuf.st_mode)) {
|
||||
kWarning() << "Filepath does not point to regular file" << mhdoutfilepath;
|
||||
return MHD_NO;
|
||||
}
|
||||
int mhdfd = KDE::open(mhdoutfilepath, O_RDONLY);
|
||||
mhdresult = MHD_is_feature_supported(MHD_FEATURE_LARGE_FILE);
|
||||
if (mhdresult == MHD_NO) {
|
||||
mhdresponse = MHD_create_response_from_fd(statbuf.st_size, mhdfd);
|
||||
} else {
|
||||
mhdresponse = MHD_create_response_from_fd64(statbuf.st_size, mhdfd);
|
||||
}
|
||||
} else {
|
||||
kWarning() << "Either output data or filepath must be non-empty";
|
||||
return MHD_NO;
|
||||
}
|
||||
if (Q_UNLIKELY(!mhdresponse)) {
|
||||
kWarning() << "Could not create MHD response";
|
||||
return MHD_NO;
|
||||
}
|
||||
|
||||
foreach (const QByteArray &httpheaderkey, mhdouthttpheaders.keys()) {
|
||||
// MHD refuses to add these
|
||||
if (qstricmp(httpheaderkey.constData(), "Content-Length") == 0) {
|
||||
kDebug() << "Ignoring content-length";
|
||||
continue;
|
||||
} else if (qstricmp(httpheaderkey.constData(), "Date") == 0) {
|
||||
kDebug() << "Ignoring date";
|
||||
continue;
|
||||
}
|
||||
const QByteArray httpheadervalue = mhdouthttpheaders.value(httpheaderkey);
|
||||
mhdresult = MHD_add_response_header(mhdresponse, httpheaderkey.constData(), httpheadervalue.constData());
|
||||
if (mhdresult == MHD_NO) {
|
||||
kWarning() << "Could not add response header" << httpheaderkey << httpheadervalue;
|
||||
}
|
||||
}
|
||||
|
||||
mhdresult = MHD_queue_response(connection, mhdouthttpstatus, mhdresponse);
|
||||
MHD_destroy_response(mhdresponse);
|
||||
return mhdresult;
|
||||
}
|
||||
|
||||
void KHTTPPrivate::loggerCallback(void *cls, const char *fm, va_list ap)
|
||||
{
|
||||
char mhdloggerbuff[1024];
|
||||
::memset(mhdloggerbuff, 0, sizeof(mhdloggerbuff) * sizeof(char));
|
||||
::vsnprintf(mhdloggerbuff, sizeof(mhdloggerbuff), fm, ap);
|
||||
// qDebug() << Q_FUNC_INFO << mhdloggerbuff;
|
||||
KHTTPPrivate* khttpprivate = static_cast<KHTTPPrivate*>(cls);
|
||||
khttpprivate->m_errorstring = QString::fromAscii(mhdloggerbuff);
|
||||
}
|
||||
|
||||
void KHTTPPrivate::panicCallback(void *cls, const char *file, unsigned int line, const char *reason)
|
||||
{
|
||||
KHTTPPrivate* khttpprivate = static_cast<KHTTPPrivate*>(cls);
|
||||
khttpprivate->m_mhddaemon = nullptr;
|
||||
kFatal() << QString::fromAscii(file) << QString::number(line) << QString::fromAscii(reason);
|
||||
}
|
||||
#endif // HAVE_LIBMICROHTTPD
|
||||
|
||||
KHTTP::KHTTP(QObject *parent)
|
||||
: QObject(parent),
|
||||
d(new KHTTPPrivate(this))
|
||||
{
|
||||
}
|
||||
|
||||
KHTTP::~KHTTP()
|
||||
{
|
||||
stop();
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool KHTTP::setCertificate(const QByteArray &keydata, const QByteArray &certdata, const QByteArray &password)
|
||||
{
|
||||
return d->setCertificate(keydata, certdata, password);
|
||||
}
|
||||
|
||||
bool KHTTP::setAuthenticate(const QByteArray &username, const QByteArray &password, const QString &message)
|
||||
{
|
||||
return d->setAuthenticate(username, password, message);
|
||||
}
|
||||
|
||||
bool KHTTP::start(const QHostAddress &address, quint16 port)
|
||||
{
|
||||
return d->start(address, port);
|
||||
}
|
||||
|
||||
bool KHTTP::stop()
|
||||
{
|
||||
return d->stop();
|
||||
}
|
||||
|
||||
QString KHTTP::errorString() const
|
||||
{
|
||||
return d->errorString();
|
||||
}
|
||||
|
||||
#include "khttp.moc"
|
Loading…
Add table
Reference in a new issue