Files
infix/package/rousette/0027-restconf-refactor-uri-parser-for-stream-URIs.patch
T

197 lines
8.5 KiB
Diff

From 97ceef119c900c37bbaa27860c3b43cfa6d69f95 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Pecka?= <tomas.pecka@cesnet.cz>
Date: Mon, 13 Jan 2025 13:01:07 +0100
Subject: [PATCH 27/44] restconf: refactor uri parser for stream URIs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Organization: Wires
This is a preparation for the upcoming patch that will add support for
subscribed streams. I will use boost spirit x3 parser to parse such
URIs so I need to refactor the existing parser, which is not really a
parser, but we only match for the two known URIs.
Change-Id: Ib76973cd967fc3e9558ceb6be8f51239ce4c5ef3
Signed-off-by: Mattias Walström <lazzer@gmail.com>
---
src/restconf/Server.cpp | 15 +--------------
src/restconf/uri.cpp | 36 +++++++++++++++++++++++++++---------
src/restconf/uri.h | 11 ++++++-----
src/restconf/uri_impl.h | 2 ++
tests/uri-parser.cpp | 4 ++--
5 files changed, 38 insertions(+), 30 deletions(-)
diff --git a/src/restconf/Server.cpp b/src/restconf/Server.cpp
index daf23ed..d356f7e 100644
--- a/src/restconf/Server.cpp
+++ b/src/restconf/Server.cpp
@@ -874,7 +874,6 @@ Server::Server(sysrepo::Connection conn, const std::string& address, const std::
server->handle(netconfStreamRoot, [this, conn](const auto& req, const auto& res) mutable {
auto sess = conn.sessionStart();
- libyang::DataFormat dataFormat;
std::optional<std::string> xpathFilter;
std::optional<sysrepo::NotificationTimeStamp> startTime;
std::optional<sysrepo::NotificationTimeStamp> stopTime;
@@ -890,18 +889,6 @@ Server::Server(sysrepo::Connection conn, const std::string& address, const std::
auto streamRequest = asRestconfStreamRequest(req.method(), req.uri().path, req.uri().raw_query);
- switch(streamRequest.type) {
- case RestconfStreamRequest::Type::NetconfNotificationJSON:
- dataFormat = libyang::DataFormat::JSON;
- break;
- case RestconfStreamRequest::Type::NetconfNotificationXML:
- dataFormat = libyang::DataFormat::XML;
- break;
- default:
- // GCC 14 complains about uninitialized variable, but asRestconfStreamRequest() would have thrown
- __builtin_unreachable();
- }
-
if (auto it = streamRequest.queryParams.find("filter"); it != streamRequest.queryParams.end()) {
xpathFilter = std::get<std::string>(it->second);
}
@@ -916,7 +903,7 @@ Server::Server(sysrepo::Connection conn, const std::string& address, const std::
// The signal is constructed outside NotificationStream class because it is required to be passed to
// NotificationStream's parent (EventStream) constructor where it already must be constructed
// Yes, this is a hack.
- auto client = std::make_shared<NotificationStream>(req, res, std::make_shared<rousette::http::EventStream::Signal>(), sess, dataFormat, xpathFilter, startTime, stopTime);
+ auto client = std::make_shared<NotificationStream>(req, res, std::make_shared<rousette::http::EventStream::Signal>(), sess, streamRequest.type.encoding, xpathFilter, startTime, stopTime);
client->activate();
} catch (const auth::Error& e) {
processAuthError(req, res, e, [&res]() {
diff --git a/src/restconf/uri.cpp b/src/restconf/uri.cpp
index da1e3a5..e95d70e 100644
--- a/src/restconf/uri.cpp
+++ b/src/restconf/uri.cpp
@@ -57,6 +57,13 @@ const auto resources = x3::rule<class resources, URIPath>{"resources"} =
((x3::lit("/") | x3::eps) /* /restconf/ and /restconf */ >> x3::attr(URIPrefix{URIPrefix::Type::RestconfRoot}) >> x3::attr(std::vector<PathSegment>{}));
const auto uriGrammar = x3::rule<class grammar, URIPath>{"grammar"} = x3::lit("/restconf") >> resources;
+
+const auto netconfStream = x3::rule<class netconfStream, RestconfStreamRequest::NetconfStream>{"netconfStream"} =
+ x3::lit("/NETCONF") >>
+ ((x3::lit("/XML") >> x3::attr(libyang::DataFormat::XML)) |
+ (x3::lit("/JSON") >> x3::attr(libyang::DataFormat::JSON)));
+const auto streamUriGrammar = x3::rule<class grammar, RestconfStreamRequest::NetconfStream>{"streamsGrammar"} = x3::lit("/streams") >> netconfStream;
+
// clang-format on
}
@@ -197,6 +204,17 @@ std::optional<queryParams::QueryParams> parseQueryParams(const std::string& quer
return ret;
}
+std::optional<RestconfStreamRequest::NetconfStream> parseStreamUri(const std::string& uriPath)
+{
+ std::optional<RestconfStreamRequest::NetconfStream> ret;
+
+ if (!x3::parse(std::begin(uriPath), std::end(uriPath), streamUriGrammar >> x3::eoi, ret)) {
+ return std::nullopt;
+ }
+
+ return ret;
+}
+
URIPrefix::URIPrefix()
: resourceType(URIPrefix::Type::BasicRestconfData)
{
@@ -637,20 +655,20 @@ std::optional<std::variant<libyang::Module, libyang::SubmoduleParsed>> asYangMod
return std::nullopt;
}
-RestconfStreamRequest asRestconfStreamRequest(const std::string& httpMethod, const std::string& uriPath, const std::string& uriQueryString)
+RestconfStreamRequest::NetconfStream::NetconfStream() = default;
+RestconfStreamRequest::NetconfStream::NetconfStream(const libyang::DataFormat& encoding)
+ : encoding(encoding)
{
- static const auto netconfStreamRoot = "/streams/NETCONF/";
- RestconfStreamRequest::Type type;
+}
+RestconfStreamRequest asRestconfStreamRequest(const std::string& httpMethod, const std::string& uriPath, const std::string& uriQueryString)
+{
if (httpMethod != "GET" && httpMethod != "HEAD") {
throw ErrorResponse(405, "application", "operation-not-supported", "Method not allowed.");
}
- if (uriPath == netconfStreamRoot + "XML"s) {
- type = RestconfStreamRequest::Type::NetconfNotificationXML;
- } else if (uriPath == netconfStreamRoot + "JSON"s) {
- type = RestconfStreamRequest::Type::NetconfNotificationJSON;
- } else {
+ auto type = impl::parseStreamUri(uriPath);
+ if (!type) {
throw ErrorResponse(404, "application", "invalid-value", "Invalid stream");
}
@@ -661,7 +679,7 @@ RestconfStreamRequest asRestconfStreamRequest(const std::string& httpMethod, con
validateQueryParametersForStream(*queryParameters);
- return {type, *queryParameters};
+ return {*type, *queryParameters};
}
/** @brief Returns a set of allowed HTTP methods for given URI. Usable for the 'allow' header */
diff --git a/src/restconf/uri.h b/src/restconf/uri.h
index f6df724..6f1f69f 100644
--- a/src/restconf/uri.h
+++ b/src/restconf/uri.h
@@ -197,11 +197,12 @@ struct RestconfRequest {
};
struct RestconfStreamRequest {
- enum class Type {
- NetconfNotificationJSON,
- NetconfNotificationXML,
- };
- Type type;
+ struct NetconfStream {
+ libyang::DataFormat encoding;
+
+ NetconfStream();
+ NetconfStream(const libyang::DataFormat& encoding);
+ } type;
queryParams::QueryParams queryParams;
};
diff --git a/src/restconf/uri_impl.h b/src/restconf/uri_impl.h
index 2bcdb3f..00b22f6 100644
--- a/src/restconf/uri_impl.h
+++ b/src/restconf/uri_impl.h
@@ -66,6 +66,8 @@ BOOST_FUSION_ADAPT_STRUCT(rousette::restconf::impl::YangModule, name, revision);
BOOST_FUSION_ADAPT_STRUCT(rousette::restconf::PathSegment, apiIdent, keys);
BOOST_FUSION_ADAPT_STRUCT(rousette::restconf::ApiIdentifier, prefix, identifier);
+BOOST_FUSION_ADAPT_STRUCT(rousette::restconf::RestconfStreamRequest::NetconfStream, encoding);
+
BOOST_FUSION_ADAPT_STRUCT(rousette::restconf::queryParams::fields::ParenExpr, lhs, rhs);
BOOST_FUSION_ADAPT_STRUCT(rousette::restconf::queryParams::fields::SlashExpr, lhs, rhs);
BOOST_FUSION_ADAPT_STRUCT(rousette::restconf::queryParams::fields::SemiExpr, lhs, rhs);
diff --git a/tests/uri-parser.cpp b/tests/uri-parser.cpp
index fd5dd8b..53d1fbb 100644
--- a/tests/uri-parser.cpp
+++ b/tests/uri-parser.cpp
@@ -1072,13 +1072,13 @@ TEST_CASE("URI path parser")
{
auto [type, queryParams] = asRestconfStreamRequest("GET", "/streams/NETCONF/XML", "");
- REQUIRE(type == RestconfStreamRequest::Type::NetconfNotificationXML);
+ REQUIRE(type.encoding == libyang::DataFormat::XML);
REQUIRE(queryParams.empty());
}
{
auto [type, queryParams] = asRestconfStreamRequest("GET", "/streams/NETCONF/JSON", "");
- REQUIRE(type == RestconfStreamRequest::Type::NetconfNotificationJSON);
+ REQUIRE(type.encoding == libyang::DataFormat::JSON);
REQUIRE(queryParams.empty());
}
--
2.43.0