2026-07-11 16:02:57 +08:00

175 lines
4.9 KiB
C++

#include "ParkingStatusHttpServer.h"
#include <algorithm>
#include <exception>
#include <ios>
#include <ostream>
#include <utility>
#include "Poco/Exception.h"
#include "Poco/Net/HTTPServer.h"
#include "Poco/Net/HTTPRequest.h"
#include "Poco/Net/HTTPRequestHandler.h"
#include "Poco/Net/HTTPRequestHandlerFactory.h"
#include "Poco/Net/HTTPServerParams.h"
#include "Poco/Net/HTTPServerRequest.h"
#include "Poco/Net/HTTPServerResponse.h"
#include "Poco/Net/ServerSocket.h"
#include "Poco/Net/SocketAddress.h"
#include "Poco/URI.h"
namespace {
const char* kStatusPath = "/api/parking/status";
bool IsStatusPath(const std::string& path)
{
return path == "/" ||
path == "/status" ||
path == "/api/status" ||
path == kStatusPath;
}
void WriteJson(Poco::Net::HTTPServerResponse& response,
Poco::Net::HTTPResponse::HTTPStatus status,
const std::string& body)
{
response.setStatus(status);
response.setContentType("application/json; charset=utf-8");
response.set("Cache-Control", "no-store");
response.setContentLength(static_cast<std::streamsize>(body.size()));
std::ostream& output = response.send();
output.write(body.data(), static_cast<std::streamsize>(body.size()));
}
class StatusRequestHandler : public Poco::Net::HTTPRequestHandler
{
public:
explicit StatusRequestHandler(ParkingStatusHttpServer::StatusProvider statusProvider)
: m_statusProvider(std::move(statusProvider))
{
}
void handleRequest(Poco::Net::HTTPServerRequest& request,
Poco::Net::HTTPServerResponse& response) override
{
if (request.getMethod() != Poco::Net::HTTPRequest::HTTP_GET) {
WriteJson(response,
Poco::Net::HTTPResponse::HTTP_METHOD_NOT_ALLOWED,
"{\"success\":false,\"message\":\"请求方法不支持\"}");
return;
}
const std::string path = Poco::URI(request.getURI()).getPath();
if (!IsStatusPath(path)) {
WriteJson(response,
Poco::Net::HTTPResponse::HTTP_NOT_FOUND,
"{\"success\":false,\"message\":\"接口不存在\"}");
return;
}
const std::string body = m_statusProvider
? m_statusProvider()
: std::string("{\"success\":false,\"message\":\"状态服务不可用\"}");
WriteJson(response, Poco::Net::HTTPResponse::HTTP_OK, body);
}
private:
ParkingStatusHttpServer::StatusProvider m_statusProvider;
};
class StatusRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory
{
public:
explicit StatusRequestHandlerFactory(ParkingStatusHttpServer::StatusProvider statusProvider)
: m_statusProvider(std::move(statusProvider))
{
}
Poco::Net::HTTPRequestHandler* createRequestHandler(const Poco::Net::HTTPServerRequest&) override
{
return new StatusRequestHandler(m_statusProvider);
}
private:
ParkingStatusHttpServer::StatusProvider m_statusProvider;
};
} // namespace
ParkingStatusHttpServer::ParkingStatusHttpServer() = default;
ParkingStatusHttpServer::~ParkingStatusHttpServer()
{
Stop();
}
int ParkingStatusHttpServer::Start(const HttpServerConfig& config, StatusProvider statusProvider)
{
Stop();
m_lastError.clear();
if (!config.enabled) {
return 0;
}
if (!statusProvider) {
m_lastError = "HTTP状态服务缺少状态提供函数";
return -1;
}
try {
if (config.port <= 0 || config.port > 65535) {
m_lastError = "HTTP状态服务端口无效";
return -1;
}
const int maxQueued = std::max(1, config.maxQueued);
const std::string listenAddress = config.address.empty() ? "0.0.0.0" : config.address;
Poco::Net::ServerSocket socket;
socket.bind(Poco::Net::SocketAddress(listenAddress, config.port), true);
socket.listen(maxQueued);
Poco::Net::HTTPServerParams* params = new Poco::Net::HTTPServerParams();
params->setMaxQueued(maxQueued);
params->setMaxThreads(std::max(1, config.maxThreads));
m_server.reset(new Poco::Net::HTTPServer(
new StatusRequestHandlerFactory(std::move(statusProvider)),
socket,
params));
m_server->start();
m_running = true;
return 0;
} catch (const Poco::Exception& e) {
m_lastError = e.displayText();
} catch (const std::exception& e) {
m_lastError = e.what();
} catch (...) {
m_lastError = "HTTP状态服务启动异常";
}
m_server.reset();
m_running = false;
return -1;
}
void ParkingStatusHttpServer::Stop()
{
if (m_server) {
m_server->stopAll(true);
m_server.reset();
}
m_running = false;
}
bool ParkingStatusHttpServer::IsRunning() const
{
return m_running;
}
std::string ParkingStatusHttpServer::LastError() const
{
return m_lastError;
}