Skip to content
Snippets Groups Projects
Commit 093a49e9 authored by Stefan Spettel's avatar Stefan Spettel
Browse files

feat(pcf): Implement HTTP/2 support for SM Policy API

parent a4359844
No related branches found
No related tags found
1 merge request!29Add HTTP/2 support
Pipeline #36690 passed
......@@ -33,7 +33,8 @@
namespace oai::pcf::api {
class sm_policies {
public:
static inline const std::string API_BASE = "/npcf-smpolicycontrol/";
static inline const std::string API_NAME = "npcf-smpolicycontrol";
static inline const std::string API_BASE = "/" + API_NAME + "/";
static inline const std::string CREATE_ROUTE = "/sm-policies";
static std::string get_route();
......
......@@ -61,8 +61,8 @@ api_response sm_policies_collection_api_handler::create_sm_policy(
switch (res) {
case status_code::CREATED:
http_code = http_status_code_e::HTTP_STATUS_CODE_201_CREATED;
location = m_address + sm_policies::get_route() + association_id;
http_code = http_status_code_e::HTTP_STATUS_CODE_201_CREATED;
location = m_address + sm_policies::get_route() + "/" + association_id;
content_type = "application/json";
break;
......@@ -100,7 +100,8 @@ api_response sm_policies_collection_api_handler::create_sm_policy(
}
response.headers.add<Pistache::Http::Header::ContentType>(
Pistache::Http::Mime::MediaType(content_type));
response.body = json_data.dump();
response.body = json_data.dump();
response.status_code = http_code;
return response;
}
......
......@@ -69,7 +69,7 @@ void setUpUnixSignals(std::vector<int> quitSignals) {
}
#endif
// using namespace oai::pcf::api;
using namespace oai::pcf::api;
void PCFApiServer::init(size_t thr) {
auto opts = Pistache::Http::Endpoint::options().threads(thr);
......@@ -87,6 +87,7 @@ void PCFApiServer::start() {
m_httpEndpoint->setHandler(m_router->handler());
m_httpEndpoint->serve();
}
void PCFApiServer::shutdown() {
m_httpEndpoint->shutdown();
m_smPoliciesCollectionApi = nullptr;
......
......@@ -31,8 +31,7 @@
* contact@openairinterface.org
*/
#ifndef FILE_PCF_API_SERVER_SEEN
#define FILE_PCF_API_SERVER_SEEN
#pragma once
//#include "IndividualSMPolicyDocumentApi.h"
......@@ -50,6 +49,8 @@
#include "SMPoliciesCollectionApiImpl.h"
#include "IndividualSMPolicyDocumentApiImpl.h"
namespace oai::pcf::api {
class PCFApiServer {
public:
PCFApiServer(
......@@ -84,5 +85,4 @@ class PCFApiServer {
std::shared_ptr<oai::pcf::api::IndividualSMPolicyDocumentApiImpl>
m_individualSmPolicyDocumentApi;
};
#endif
} // namespace oai::pcf::api
\ No newline at end of file
......@@ -21,9 +21,9 @@
/*! \file pcf_http2-server.h
\brief
\author Rohan Kharade
\author Rohan Kharade, Stefan Spettel
\company Openairinterface Software Allianse
\date 2022
\date 2023
\email: rohan.kharade@openairinterface.org
*/
......@@ -37,13 +37,15 @@
#include "3gpp_29.500.h"
#include "logger.hpp"
#include "pcf.h"
#include "pcf_config.hpp"
#include "api_defs.h"
#include "SmPolicyContextData.h"
using namespace nghttp2::asio_http2;
using namespace nghttp2::asio_http2::server;
using namespace oai::pcf::model;
using namespace oai::pcf::config;
using namespace oai::pcf::api;
extern std::unique_ptr<pcf_config> pcf_cfg;
......@@ -55,41 +57,152 @@ void pcf_http2_server::start() {
std::string nfId = {};
std::string subscriptionID = {};
// Get list of supported APIs
// SM Policies Collection API
server.handle(
"/", [&](const request& request, const response& /* response */) {
request.on_data([&](const uint8_t* /* data */, std::size_t /* len */) {
if (request.method().compare("GET") == 0) {
// this->get_api_list(response);
sm_policies::get_route(),
[&](const request& request, const response& response) {
if (request.method() != "POST") {
handle_method_not_exists(response, request);
return;
}
auto request_body = std::make_shared<std::stringstream>();
request.on_data(
[&, request_body](const uint8_t* data, std::size_t len) {
if (len > 0) {
std::copy(
data, data + len,
std::ostream_iterator<uint8_t>(*request_body));
return;
}
SmPolicyContextData context;
try {
nlohmann::json::parse(request_body->str()).get_to(context);
context.validate();
api_response resp =
m_collection_api_handler->create_sm_policy(context);
auto h_map = convert_headers(resp);
response.write_head(
static_cast<unsigned int>(resp.status_code), h_map);
response.end(resp.body);
return;
} catch (std::exception& e) {
handle_parsing_error(response, e);
return;
}
});
});
// Individual SM Policy
// We match for sm-policies/*
server.handle(
sm_policies::get_route() + "/",
[&](const request& request, const response& response) {
std::vector<std::string> split_result;
boost::split(split_result, request.uri().path, boost::is_any_of("/"));
bool is_update = false;
bool is_delete = false;
bool is_get = false;
std::string policy_id;
if (split_result[split_result.size() - 1] == "delete") {
is_delete = true;
policy_id = split_result[split_result.size() - 2];
} else if (split_result[split_result.size() - 1] == "update") {
is_update = true;
policy_id = split_result[split_result.size() - 2];
} else {
is_get = true;
policy_id = split_result[split_result.size() - 1];
}
if ((is_delete || is_update) && request.method() != "POST") {
handle_method_not_exists(response, request);
return;
}
if (is_get && request.method() != "GET") {
handle_method_not_exists(response, request);
return;
}
auto request_body = std::make_shared<std::stringstream>();
request.on_data([&, request_body, is_get, is_update, is_delete,
policy_id](const uint8_t* data, std::size_t len) {
if (len > 0) {
std::copy(
data, data + len,
std::ostream_iterator<uint8_t>(*request_body));
return;
}
SmPolicyDeleteData delete_data;
SmPolicyUpdateContextData update_context_data;
api_response resp;
try {
if (is_update) {
nlohmann::json::parse(request_body->str())
.get_to(update_context_data);
update_context_data.validate();
resp = m_individual_api_handler->update_sm_policy(
policy_id, update_context_data);
} else if (is_delete) {
nlohmann::json::parse(request_body->str()).get_to(delete_data);
delete_data.validate();
resp = m_individual_api_handler->delete_sm_policy(
policy_id, delete_data);
} else if (is_get) {
resp = m_individual_api_handler->get_sm_policy(policy_id);
}
auto h_map = convert_headers(resp);
response.write_head(
static_cast<unsigned int>(resp.status_code), h_map);
response.end(resp.body);
return;
} catch (std::exception& e) {
handle_parsing_error(response, e);
return;
}
});
});
// Default Route
server.handle("/", [&](const request& request, const response& response) {
handle_method_not_exists(response, request);
return;
});
if (server.listen_and_serve(ec, m_address, std::to_string(m_port))) {
std::cerr << "HTTP Server error: " << ec.message() << std::endl;
Logger::pcf_sbi().error("HTTP Server error: %s", ec.message());
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// void pcf_http2_server::get_api_list(const response &response) {
// int http_code = 0;
// nlohmann::json json_data = {};
// std::string content_type = "application/json";
// header_map h;
// h.emplace("content-type", header_value{content_type});
// if (pcf_cfg.get_api_list(json_data)) {
// http_code = HTTP_STATUS_CODE_200_OK;
// response.write_head(http_code, h);
// response.end(json_data.dump(4).c_str());
// } else {
// http_code = HTTP_STATUS_CODE_503_SERVICE_UNAVAILABLE;
// response.write_head(http_code, h);
// response.end();
// }
// }
//------------------------------------------------------------------------------
void pcf_http2_server::stop() {
server.stop();
}
void pcf_http2_server::handle_method_not_exists(
const response& response, const request& request) {
Logger::pcf_sbi().warn(
"Invalid route/method called: %s : %s", request.method(),
request.uri().path);
response.write_head(static_cast<unsigned int>(
http_status_code_e::HTTP_STATUS_CODE_404_NOT_FOUND));
response.end("The requested method does not exist");
}
void pcf_http2_server::handle_parsing_error(
const response& response, const std::exception& ex) {
Logger::pcf_sbi().warn("Parsing error: %s", ex.what());
response.write_head(static_cast<unsigned int>(
http_status_code_e::HTTP_STATUS_CODE_400_BAD_REQUEST));
// for security reasons it is better to not give the internal exception to the
// user, we can also decide to change that
response.end("Could not parse JSON data");
}
header_map pcf_http2_server::convert_headers(const api_response& response) {
header_map h_map;
for (const auto& hdr : response.headers.list()) {
std::stringstream ss;
hdr->write(ss);
h_map.emplace(hdr->name(), header_value{ss.str(), false});
}
return h_map;
}
......@@ -21,14 +21,13 @@
/*! \file pcf_http2-server.h
\brief
\author Rohan Kharade
\author Rohan Kharade, Stefan Spettel
\company Openairinterface Software Allianse
\date 2022
\email: rohan.kharade@openairinterface.org
*/
#ifndef FILE_PCF_HTTP2_SERVER_SEEN
#define FILE_PCF_HTTP2_SERVER_SEEN
#pragma once
#include "conversions.hpp"
#include "pcf.h"
......@@ -38,16 +37,28 @@
#include <nghttp2/asio_http2_server.h>
#include "IndividualSMPolicyDocumentApiImpl.h"
#include "SMPoliciesCollectionApiImpl.h"
#include "sm_policies_collection_api_handler.h"
#include "individual_sm_policy_document_api_handler.h"
namespace oai::pcf::api {
class pcf_http2_server {
public:
pcf_http2_server(
const std::string& addr, uint32_t port,
const std::unique_ptr<oai::pcf::app::pcf_app>& pcf_app_inst)
: m_address(addr),
m_port(port),
server(),
smpc_service(pcf_app_inst->get_pcf_smpc_service()){};
: m_address(addr), m_port(port), server() {
// TODO hardcode http string, how to handle https
std::string address = "http://" + addr + ":" + std::to_string(port);
m_collection_api_handler =
std::make_shared<sm_policies_collection_api_handler>(
pcf_app_inst->get_pcf_smpc_service(), address);
m_individual_api_handler =
std::make_shared<individual_sm_policy_document_api_handler>(
pcf_app_inst->get_pcf_smpc_service());
};
void start();
void init(size_t /* thr */) {}
......@@ -63,7 +74,20 @@ class pcf_http2_server {
nghttp2::asio_http2::server::http2 server;
std::shared_ptr<oai::pcf::app::pcf_smpc> smpc_service;
std::shared_ptr<sm_policies_collection_api_handler> m_collection_api_handler;
std::shared_ptr<individual_sm_policy_document_api_handler>
m_individual_api_handler;
static void handle_method_not_exists(
const nghttp2::asio_http2::server::response& response,
const nghttp2::asio_http2::server::request& request);
static void handle_parsing_error(
const nghttp2::asio_http2::server::response& response,
const std::exception& ex);
static nghttp2::asio_http2::header_map convert_headers(
const api_response& response);
};
#endif
} // namespace oai::pcf::api
......@@ -151,6 +151,4 @@ typedef struct nf_service_s {
#define AMF_NUMBER_RETRIES 3
#define UDM_NUMBER_RETRIES 3
#define SM_POLICY_API_NAME "npcf-smpolicycontrol";
#endif
......@@ -32,6 +32,7 @@ using namespace std;
using namespace oai::pcf::app;
using namespace oai::pcf::config;
using namespace oai::utils;
using namespace oai::pcf::api;
using namespace oai::config;
......
......@@ -34,6 +34,7 @@
#include "pcf_config.hpp"
#include "pcf_client.hpp"
#include "Snssai.h"
#include "api_defs.h"
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/uuid_io.hpp>
......@@ -80,8 +81,8 @@ void pcf_nrf::generate_pcf_profile() {
// NF services
nf_service_t nf_service = {};
nf_service.service_instance_id = SM_POLICY_API_NAME;
nf_service.service_name = SM_POLICY_API_NAME;
nf_service.service_instance_id = oai::pcf::api::sm_policies::API_NAME;
nf_service.service_name = oai::pcf::api::sm_policies::API_NAME;
nf_service_version_t version = {};
version.api_version_in_uri = pcf_cfg->sbi.get_api_version();
version.api_full_version = "1.0.0"; // TODO: to be updated
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment