diff --git a/src/api-server/api_defs.h b/src/api-server/api_defs.h index f52775c0eb03482a18e498d529414f39ec7e864a..72858e5615e2bb3c14cc5f83a9df6896f635d431 100644 --- a/src/api-server/api_defs.h +++ b/src/api-server/api_defs.h @@ -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(); diff --git a/src/api-server/handler/sm_policies_collection_api_handler.cpp b/src/api-server/handler/sm_policies_collection_api_handler.cpp index 9dba21aa26189b52bdedf1d4610cd937aa5c2d43..b212d1ecc5f561984dcfef0b90ff0523174aab6d 100644 --- a/src/api-server/handler/sm_policies_collection_api_handler.cpp +++ b/src/api-server/handler/sm_policies_collection_api_handler.cpp @@ -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; } diff --git a/src/api-server/pcf-api-server.cpp b/src/api-server/pcf-api-server.cpp index 809d2af5412a04cd339d997e71cc6b2b7f61c7a6..682b13e54cd1f38ed6b04db884ec2943d72feb9e 100644 --- a/src/api-server/pcf-api-server.cpp +++ b/src/api-server/pcf-api-server.cpp @@ -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; diff --git a/src/api-server/pcf-api-server.hpp b/src/api-server/pcf-api-server.hpp index 88b0a6714f43e128ecd1adffc4cef3b8d447d3d6..5bdd9fbc471ec57960283fa55085feb7cb93b889 100644 --- a/src/api-server/pcf-api-server.hpp +++ b/src/api-server/pcf-api-server.hpp @@ -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 diff --git a/src/api-server/pcf-http2-server.cpp b/src/api-server/pcf-http2-server.cpp index 1c7039a8603a60d29a6f3861b7536938ff70b671..efa3d35644b0dd0ac7b8bb557f59f38f540e6b15 100644 --- a/src/api-server/pcf-http2-server.cpp +++ b/src/api-server/pcf-http2-server.cpp @@ -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; +} diff --git a/src/api-server/pcf-http2-server.hpp b/src/api-server/pcf-http2-server.hpp index 7350b0bd885af00358bb5c0ed13a4e47d91d8359..274d5a8eec3d4049158cdbc7655666f16770b5a6 100644 --- a/src/api-server/pcf-http2-server.hpp +++ b/src/api-server/pcf-http2-server.hpp @@ -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 diff --git a/src/common/pcf.h b/src/common/pcf.h index 2bf2861e7ba561f13714c2f297f1f69df07695af..caa8722a5fc219c816046978309be1b2eba283eb 100644 --- a/src/common/pcf.h +++ b/src/common/pcf.h @@ -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 diff --git a/src/oai_pcf/main.cpp b/src/oai_pcf/main.cpp index 8dbb5212ec83a7b3a9777b59453b17d4474aee3c..34190242778c0fdb7f0eb596d70f9f578d0075cd 100644 --- a/src/oai_pcf/main.cpp +++ b/src/oai_pcf/main.cpp @@ -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; diff --git a/src/pcf_app/pcf_nrf.cpp b/src/pcf_app/pcf_nrf.cpp index 75f444446b50552003f975470daa2f7e767a7192..24d861c5d34e4ce9bfe53306db8c7f38169fcf11 100644 --- a/src/pcf_app/pcf_nrf.cpp +++ b/src/pcf_app/pcf_nrf.cpp @@ -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