Add Phase 0 spike: end-to-end transport verified
Bare PHP behind FrankenPHP plus a Qt/QML host that spawns it. GET /api/ping publishes a Mercure event; the QML window receives it back over the SSE stream. Findings (Caddy directive ordering, Mercure transport scalar, PR_SET_PDEATHSIG for child cleanup, PHP 8.5 curl_close deprecation, port collision with system FrankenPHP, pure-QML SSE viability) are recorded in spike/README.md so Phase 1 starts from a known-good baseline. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
72
spike/qt/main.cpp
Normal file
72
spike/qt/main.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <QCoreApplication>
|
||||
#include <QGuiApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QProcess>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QObject>
|
||||
#include <QDebug>
|
||||
|
||||
#include <csignal>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
// Resolve the spike root by walking up from the running binary
|
||||
// until we find a directory containing Caddyfile + bin/frankenphp.
|
||||
static QString resolveSpikeRoot()
|
||||
{
|
||||
QDir d(QCoreApplication::applicationDirPath());
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
if (QFileInfo(d.filePath("Caddyfile")).exists() &&
|
||||
QFileInfo(d.filePath("bin/frankenphp")).exists()) {
|
||||
return d.absolutePath();
|
||||
}
|
||||
if (!d.cdUp()) break;
|
||||
}
|
||||
qWarning() << "Could not locate spike root from"
|
||||
<< QCoreApplication::applicationDirPath();
|
||||
return QCoreApplication::applicationDirPath();
|
||||
}
|
||||
|
||||
// SIGTERM / SIGINT → graceful Qt quit so aboutToQuit cleanup runs.
|
||||
static void installSignalHandlers()
|
||||
{
|
||||
auto handler = +[](int) { QCoreApplication::quit(); };
|
||||
std::signal(SIGTERM, handler);
|
||||
std::signal(SIGINT, handler);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QGuiApplication app(argc, argv);
|
||||
installSignalHandlers();
|
||||
|
||||
const QString spikeRoot = resolveSpikeRoot();
|
||||
qInfo() << "spike root:" << spikeRoot;
|
||||
|
||||
QProcess franken;
|
||||
franken.setProgram(spikeRoot + "/bin/frankenphp");
|
||||
franken.setArguments({"run", "--config", spikeRoot + "/Caddyfile"});
|
||||
franken.setWorkingDirectory(spikeRoot);
|
||||
franken.setProcessChannelMode(QProcess::ForwardedChannels);
|
||||
|
||||
// Linux belt-and-suspenders: ask the kernel to SIGTERM the child
|
||||
// when this process dies, regardless of how (crash, kill -9, etc.).
|
||||
franken.setChildProcessModifier([] {
|
||||
prctl(PR_SET_PDEATHSIG, SIGTERM);
|
||||
});
|
||||
|
||||
franken.start();
|
||||
|
||||
QObject::connect(&app, &QCoreApplication::aboutToQuit, [&]() {
|
||||
if (franken.state() == QProcess::NotRunning) return;
|
||||
franken.terminate();
|
||||
if (!franken.waitForFinished(2000)) franken.kill();
|
||||
});
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
engine.loadFromModule("Spike", "Main");
|
||||
|
||||
if (engine.rootObjects().isEmpty()) return -1;
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
Reference in New Issue
Block a user