modbus rtu 1.2
This commit is contained in:
43
src/api/http/RestApiCommandHttp.cpp
Normal file
43
src/api/http/RestApiCommandHttp.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "api/http/RestApiCommandHttp.h"
|
||||
|
||||
#include <QHttpServerResponse>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonParseError>
|
||||
|
||||
#include "api/ipc_dbus/CommandDispatcher.h"
|
||||
|
||||
namespace softbus::api::http
|
||||
{
|
||||
|
||||
QHttpServerResponse makeCommandHttpResponse(const QString& id, const softbus::api::ipc::DispatchResult& r)
|
||||
{
|
||||
QJsonObject o;
|
||||
o.insert(QStringLiteral("id"), id);
|
||||
o.insert(QStringLiteral("code"), r.code);
|
||||
o.insert(QStringLiteral("message"), r.message);
|
||||
o.insert(QStringLiteral("data"), r.data);
|
||||
const auto httpStatus =
|
||||
r.code == 0 ? QHttpServerResponse::StatusCode::Ok : QHttpServerResponse::StatusCode::BadRequest;
|
||||
return QHttpServerResponse(o, httpStatus);
|
||||
}
|
||||
|
||||
QHttpServerResponse handleExecuteCommandHttpBody(const QByteArray& body, softbus::api::ipc::CommandDispatcher& dispatcher)
|
||||
{
|
||||
QJsonParseError err;
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(body, &err);
|
||||
if (err.error != QJsonParseError::NoError || !doc.isObject()) {
|
||||
return makeCommandHttpResponse(QString(), {4000, QStringLiteral("invalid_json"), {}});
|
||||
}
|
||||
const QJsonObject req = doc.object();
|
||||
const QString id = req.value(QStringLiteral("id")).toString();
|
||||
const QString action = req.value(QStringLiteral("action")).toString();
|
||||
const QJsonObject params = req.value(QStringLiteral("params")).toObject();
|
||||
if (action.isEmpty()) {
|
||||
return makeCommandHttpResponse(id, {4003, QStringLiteral("invalid_action"), {}});
|
||||
}
|
||||
const auto result = dispatcher.dispatch(action, params);
|
||||
return makeCommandHttpResponse(id, result);
|
||||
}
|
||||
|
||||
} // namespace softbus::api::http
|
||||
22
src/api/http/RestApiCommandHttp.h
Normal file
22
src/api/http/RestApiCommandHttp.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QHttpServerResponse>
|
||||
#include <QString>
|
||||
|
||||
namespace softbus::api::ipc
|
||||
{
|
||||
struct DispatchResult;
|
||||
class CommandDispatcher;
|
||||
} // namespace softbus::api::ipc
|
||||
|
||||
namespace softbus::api::http
|
||||
{
|
||||
|
||||
/// Same envelope as D-Bus `executeCommand` response body (id/code/message/data).
|
||||
QHttpServerResponse makeCommandHttpResponse(const QString& id, const softbus::api::ipc::DispatchResult& r);
|
||||
|
||||
/// POST body same shape as D-Bus request: { "id", "action", "params" }.
|
||||
QHttpServerResponse handleExecuteCommandHttpBody(const QByteArray& body, softbus::api::ipc::CommandDispatcher& dispatcher);
|
||||
|
||||
} // namespace softbus::api::http
|
||||
67
src/api/http/RestApiHttpConfig.cpp
Normal file
67
src/api/http/RestApiHttpConfig.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "api/http/RestApiHttpConfig.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QIODevice>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonParseError>
|
||||
|
||||
namespace softbus::api::http
|
||||
{
|
||||
|
||||
bool loadRestApiHttpListenOptions(const QString& configPath, RestApiHttpListenOptions* out, QString* err)
|
||||
{
|
||||
if (!out) {
|
||||
if (err) {
|
||||
*err = QStringLiteral("null_output");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
*out = {};
|
||||
|
||||
if (configPath.isEmpty()) {
|
||||
if (err) {
|
||||
*err = QStringLiteral("config_path_empty");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QFile f(configPath);
|
||||
if (!f.exists()) {
|
||||
return true;
|
||||
}
|
||||
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
if (err) {
|
||||
*err = QStringLiteral("config_open_failed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const QByteArray raw = f.readAll();
|
||||
f.close();
|
||||
|
||||
QJsonParseError pe;
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(raw, &pe);
|
||||
if (pe.error != QJsonParseError::NoError || !doc.isObject()) {
|
||||
if (err) {
|
||||
*err = QStringLiteral("config_json_invalid");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const QJsonObject root = doc.object();
|
||||
const QJsonObject api = root.value(QStringLiteral("api")).toObject();
|
||||
const QJsonObject httpCfg = api.value(QStringLiteral("http")).toObject();
|
||||
if (httpCfg.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
out->sectionPresent = true;
|
||||
out->enabled = httpCfg.value(QStringLiteral("enabled")).toBool(false);
|
||||
out->bindHost = httpCfg.value(QStringLiteral("bind")).toString(QStringLiteral("127.0.0.1"));
|
||||
if (out->bindHost.isEmpty()) {
|
||||
out->bindHost = QStringLiteral("127.0.0.1");
|
||||
}
|
||||
out->port = httpCfg.value(QStringLiteral("port")).toInt(18080);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace softbus::api::http
|
||||
21
src/api/http/RestApiHttpConfig.h
Normal file
21
src/api/http/RestApiHttpConfig.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace softbus::api::http
|
||||
{
|
||||
|
||||
/// Parsed `api.http` block from daemon_config.json.
|
||||
struct RestApiHttpListenOptions
|
||||
{
|
||||
bool sectionPresent{false};
|
||||
bool enabled{false};
|
||||
QString bindHost;
|
||||
int port{18080};
|
||||
};
|
||||
|
||||
/// Reads and validates `api.http`. On success returns true; on missing file returns true with sectionPresent=false.
|
||||
/// Hard errors (open/parse) set err and return false.
|
||||
bool loadRestApiHttpListenOptions(const QString& configPath, RestApiHttpListenOptions* out, QString* err = nullptr);
|
||||
|
||||
} // namespace softbus::api::http
|
||||
44
src/api/http/RestApiRoutes.cpp
Normal file
44
src/api/http/RestApiRoutes.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include "api/http/RestApiRoutes.h"
|
||||
|
||||
#include <QHttpServer>
|
||||
#include <QHttpServerRequest>
|
||||
#include <QHttpServerResponse>
|
||||
#include <QHttpServerResponder>
|
||||
#include <QJsonObject>
|
||||
#include <utility>
|
||||
|
||||
#include "api/http/RestApiCommandHttp.h"
|
||||
#include "api/ipc_dbus/CommandDispatcher.h"
|
||||
|
||||
namespace softbus::api::http
|
||||
{
|
||||
|
||||
void registerSoftbusRestApiRoutes(QHttpServer* server,
|
||||
QObject* routeContext,
|
||||
softbus::api::ipc::CommandDispatcher* dispatcher)
|
||||
{
|
||||
if (!server || !routeContext || !dispatcher) {
|
||||
return;
|
||||
}
|
||||
|
||||
server->route(QStringLiteral("/health"), QHttpServerRequest::Method::Get, routeContext, []() {
|
||||
return QHttpServerResponse(QJsonObject{{QStringLiteral("ok"), true}}, QHttpServerResponse::StatusCode::Ok);
|
||||
});
|
||||
|
||||
server->route(QStringLiteral("/v1/status"), QHttpServerRequest::Method::Get, routeContext, [dispatcher]() {
|
||||
return makeCommandHttpResponse(QString(), dispatcher->dispatch(QStringLiteral("get_status"), {}));
|
||||
});
|
||||
|
||||
server->route(QStringLiteral("/v1/command"), QHttpServerRequest::Method::Post, routeContext,
|
||||
[dispatcher](const QHttpServerRequest& request) {
|
||||
return handleExecuteCommandHttpBody(request.body(), *dispatcher);
|
||||
});
|
||||
|
||||
server->setMissingHandler(routeContext, [](const QHttpServerRequest&, QHttpServerResponder& responder) {
|
||||
QHttpServerResponse resp(QJsonObject{{QStringLiteral("error"), QStringLiteral("not_found")}},
|
||||
QHttpServerResponse::StatusCode::NotFound);
|
||||
responder.sendResponse(std::move(resp));
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace softbus::api::http
|
||||
19
src/api/http/RestApiRoutes.h
Normal file
19
src/api/http/RestApiRoutes.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
class QObject;
|
||||
class QHttpServer;
|
||||
|
||||
namespace softbus::api::ipc
|
||||
{
|
||||
class CommandDispatcher;
|
||||
} // namespace softbus::api::ipc
|
||||
|
||||
namespace softbus::api::http
|
||||
{
|
||||
|
||||
/// Registers REST routes on `server`. `routeContext` is the QObject passed to QHttpServer::route (lifetime).
|
||||
void registerSoftbusRestApiRoutes(QHttpServer* server,
|
||||
QObject* routeContext,
|
||||
softbus::api::ipc::CommandDispatcher* dispatcher);
|
||||
|
||||
} // namespace softbus::api::http
|
||||
115
src/api/http/RestApiServer.cpp
Normal file
115
src/api/http/RestApiServer.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
#include "api/http/RestApiServer.h"
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QHttpServer>
|
||||
#include <QTcpServer>
|
||||
|
||||
#include "api/http/RestApiHttpConfig.h"
|
||||
#include "api/http/RestApiRoutes.h"
|
||||
#include "api/ipc_dbus/CommandDispatcher.h"
|
||||
#include "utils/logs/logging.h"
|
||||
|
||||
namespace softbus::api::http
|
||||
{
|
||||
|
||||
struct RestApiServer::Impl
|
||||
{
|
||||
std::unique_ptr<softbus::api::ipc::CommandDispatcher> dispatcher;
|
||||
std::unique_ptr<QHttpServer> http;
|
||||
std::unique_ptr<QTcpServer> tcp;
|
||||
};
|
||||
|
||||
RestApiServer::RestApiServer(QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_impl(std::make_unique<Impl>())
|
||||
{
|
||||
m_impl->dispatcher = std::make_unique<softbus::api::ipc::CommandDispatcher>();
|
||||
}
|
||||
|
||||
RestApiServer::~RestApiServer()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void RestApiServer::stop()
|
||||
{
|
||||
if (!m_impl || !m_impl->http) {
|
||||
return;
|
||||
}
|
||||
m_impl->http->disconnect();
|
||||
m_impl->http.reset();
|
||||
m_impl->tcp.reset();
|
||||
}
|
||||
|
||||
bool RestApiServer::tryStartFromConfigFile(const QString& configPath, QString* err)
|
||||
{
|
||||
if (!m_impl || !m_impl->dispatcher) {
|
||||
if (err) {
|
||||
*err = QStringLiteral("internal_error");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
RestApiHttpListenOptions opts;
|
||||
if (!loadRestApiHttpListenOptions(configPath, &opts, err)) {
|
||||
return false;
|
||||
}
|
||||
if (!opts.sectionPresent) {
|
||||
LOG_INFO() << "RestApiServer: no api.http section, HTTP disabled";
|
||||
return true;
|
||||
}
|
||||
if (!opts.enabled) {
|
||||
LOG_INFO() << "RestApiServer: api.http.enabled is false or missing, HTTP disabled";
|
||||
return true;
|
||||
}
|
||||
|
||||
if (opts.port <= 0 || opts.port > 65535) {
|
||||
if (err) {
|
||||
*err = QStringLiteral("invalid_port");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
stop();
|
||||
|
||||
m_impl->http = std::make_unique<QHttpServer>(this);
|
||||
m_impl->tcp = std::make_unique<QTcpServer>();
|
||||
|
||||
QHttpServer* srv = m_impl->http.get();
|
||||
registerSoftbusRestApiRoutes(srv, this, m_impl->dispatcher.get());
|
||||
|
||||
const QHostAddress addr(opts.bindHost);
|
||||
if (addr.isNull()) {
|
||||
if (err) {
|
||||
*err = QStringLiteral("invalid_bind_address");
|
||||
}
|
||||
stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_impl->tcp->listen(addr, static_cast<quint16>(opts.port))) {
|
||||
if (err) {
|
||||
*err = QStringLiteral("tcp_listen_failed");
|
||||
}
|
||||
LOG_ERROR() << "RestApiServer: listen failed bind=" << opts.bindHost << "port=" << opts.port
|
||||
<< "err=" << m_impl->tcp->errorString();
|
||||
stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!srv->bind(m_impl->tcp.get())) {
|
||||
if (err) {
|
||||
*err = QStringLiteral("http_bind_failed");
|
||||
}
|
||||
LOG_ERROR() << "RestApiServer: QHttpServer::bind failed";
|
||||
stop();
|
||||
return false;
|
||||
}
|
||||
|
||||
(void)m_impl->tcp.release();
|
||||
|
||||
LOG_INFO() << "RestApiServer: listening http://" << opts.bindHost << ":" << opts.port;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace softbus::api::http
|
||||
29
src/api/http/RestApiServer.h
Normal file
29
src/api/http/RestApiServer.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <memory>
|
||||
|
||||
namespace softbus::api::http
|
||||
{
|
||||
|
||||
class RestApiServer final : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit RestApiServer(QObject* parent = nullptr);
|
||||
~RestApiServer() override;
|
||||
|
||||
/// Reads `api.http` from JSON config. If disabled or missing, returns true and does nothing.
|
||||
/// If enabled but listen/bind fails, returns false and sets err.
|
||||
bool tryStartFromConfigFile(const QString& configPath, QString* err = nullptr);
|
||||
|
||||
void stop();
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
};
|
||||
|
||||
} // namespace softbus::api::http
|
||||
Reference in New Issue
Block a user