From: Alexandre Huff Date: Thu, 7 Jul 2022 23:07:32 +0000 (-0300) Subject: Improve error handling on rest api X-Git-Tag: 1.2.5~3 X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?p=ric-app%2Fts.git;a=commitdiff_plain;h=a1ae3a52ede2beeb84efc833f768c200727400db Improve error handling on rest api This change includes error handling improvements in the REST API. Issue-ID: RICAPP-198 Signed-off-by: Alexandre Huff Change-Id: Id20541ba0a925407bbca32ebbdd5c37adf5692cd --- diff --git a/Dockerfile b/Dockerfile index 7a2d2e9..44c1e04 100644 --- a/Dockerfile +++ b/Dockerfile @@ -150,10 +150,10 @@ WORKDIR /data COPY --from=buildenv /playpen/assets/* /data # if needed, set RMR vars -ENV RMR_SEED_RT=/data/bootstrap.rt +# ENV RMR_SEED_RT=/data/bootstrap.rt #ENV RMR_RTG_SVC=rm-host:port ENV RMR_SRC_ID=service-ricxapp-trafficxapp-rmr.ricxapp:4560 -ENV RMR_VCTL_FILE=/tmp/rmr.v -RUN echo "2" >/tmp/rmr.v +# ENV RMR_VCTL_FILE=/tmp/rmr.v +# RUN echo "2" >/tmp/rmr.v CMD [ "/usr/local/bin/ts_xapp" ] diff --git a/src/ts_xapp/ts_xapp.cpp b/src/ts_xapp/ts_xapp.cpp index 47bcd8f..02a88fe 100644 --- a/src/ts_xapp/ts_xapp.cpp +++ b/src/ts_xapp/ts_xapp.cpp @@ -498,24 +498,30 @@ void send_rest_control_request( string ue_id, string serving_cell_id, string tar cout << "[INFO] Sending a HandOff CONTROL message to \"" << ts_control_ep << "\"\n"; cout << "[INFO] HandOff request is " << msg << endl; - // sending request - restclient::RestClient client( ts_control_ep ); - restclient::response_t resp = client.do_post( "", msg ); // we already have the full path in ts_control_ep - - if( resp.status_code == 200 ) { - // ============== DO SOMETHING USEFUL HERE =============== - // Currently, we only print out the HandOff reply - rapidjson::Document document; - document.Parse( resp.body.c_str() ); - rapidjson::StringBuffer s; - rapidjson::PrettyWriter writer(s); - document.Accept( writer ); - cout << "[INFO] HandOff reply is " << s.GetString() << endl; + try { + // sending request + restclient::RestClient client( ts_control_ep ); + restclient::response_t resp = client.do_post( "", msg ); // we already have the full path in ts_control_ep + + if( resp.status_code == 200 ) { + // ============== DO SOMETHING USEFUL HERE =============== + // Currently, we only print out the HandOff reply + rapidjson::Document document; + document.Parse( resp.body.c_str() ); + rapidjson::StringBuffer s; + rapidjson::PrettyWriter writer(s); + document.Accept( writer ); + cout << "[INFO] HandOff reply is " << s.GetString() << endl; + + } else { + cout << "[ERROR] Unexpected HTTP code " << resp.status_code << " from " << \ + client.getBaseUrl() << \ + "\n[ERROR] HTTP payload is " << resp.body.c_str() << endl; + } + + } catch( const restclient::RestClientException &e ) { + cout << "[ERROR] " << e.what() << endl; - } else { - cout << "[ERROR] Unexpected HTTP code " << resp.status_code << " from " << \ - client.getBaseUrl() << \ - "\n[ERROR] HTTP payload is " << resp.body.c_str() << endl; } } @@ -552,6 +558,8 @@ void send_grpc_control_request( string ue_id, string target_cell_id ) { } request->set_riccontrolackreqval( rc::RIC_CONTROL_ACK_UNKWON ); // not yet used in api.proto + cout << "[INFO] Sending gRPC control request to " << ts_control_ep << "\n" << request->DebugString(); + grpc::Status status = rc_stub->SendRICControlReqServiceGrpc( &context, *request, &response ); if( status.ok() ) { @@ -731,33 +739,39 @@ bool build_cell_mapping() { base_url = string( data ); } - restclient::RestClient client( base_url ); - - vector nb_list = get_nodeb_list( client ); + try { + restclient::RestClient client( base_url ); + + vector nb_list = get_nodeb_list( client ); + + for( string nb : nb_list ) { + string full_path = string("/v1/nodeb/") + nb; + restclient::response_t response = client.do_get( full_path ); + if( response.status_code != 200 ) { + if( response.body.empty() ) { + cout << "[ERROR] Unexpected HTTP code " << response.status_code << " from " << \ + client.getBaseUrl() + full_path << endl; + } else { + cout << "[ERROR] Unexpected HTTP code " << response.status_code << " from " << \ + client.getBaseUrl() + full_path << ". HTTP payload is " << response.body.c_str() << endl; + } + return false; + } - for( string nb : nb_list ) { - string full_path = string("/v1/nodeb/") + nb; - restclient::response_t response = client.do_get( full_path ); - if( response.status_code != 200 ) { - if( response.body.empty() ) { - cout << "[ERROR] Unexpected HTTP code " << response.status_code << " from " << \ - client.getBaseUrl() + full_path << endl; - } else { - cout << "[ERROR] Unexpected HTTP code " << response.status_code << " from " << \ - client.getBaseUrl() + full_path << ". HTTP payload is " << response.body.c_str() << endl; + try { + NodebHandler handler; + Reader reader; + StringStream ss( response.body.c_str() ); + reader.Parse( ss, handler ); + } catch (...) { + cout << "[ERROR] Got an exception on parsing nodeb (stringstream read parse)\n"; + return false; } - return false; } - try { - NodebHandler handler; - Reader reader; - StringStream ss( response.body.c_str() ); - reader.Parse( ss, handler ); - } catch (...) { - cout << "[ERROR] Got an exception on parsing nodeb (stringstream read parse)\n"; - return false; - } + } catch( const restclient::RestClientException &e ) { + cout << "[ERROR] " << e.what() << endl; + return false; } return true; @@ -788,7 +802,7 @@ extern int main( int argc, char** argv ) { rc_stub = rc::MsgComm::NewStub(channel, grpc::StubOptions()); } - fprintf( stderr, "[TS xApp] listening on port %s\n", port ); + fprintf( stderr, "[INFO] listening on port %s\n", port ); xfw = std::unique_ptr( new Xapp( port, true ) ); xfw->Add_msg_cb( A1_POLICY_REQ, policy_callback, NULL ); // msg type 20010 diff --git a/src/utils/restclient.cpp b/src/utils/restclient.cpp index afdb356..ac9013b 100644 --- a/src/utils/restclient.cpp +++ b/src/utils/restclient.cpp @@ -30,6 +30,7 @@ #include #include #include +#include namespace restclient { @@ -51,9 +52,7 @@ static size_t http_response_callback( const char *in, size_t size, size_t num, s RestClient::RestClient( std::string baseUrl ) { this->baseUrl = baseUrl; - if( ! init() ) { - fprintf( stderr, "unable to initialize RestClient\n" ); - } + init(); } RestClient::~RestClient( ) { @@ -65,47 +64,60 @@ std::string RestClient::getBaseUrl( ) { return baseUrl; } -bool RestClient::init( ) { +void RestClient::init( ) { static std::mutex curl_mutex; { // scoped mutex to make curl_global_init thread-safe const std::lock_guard lock( curl_mutex ); - curl_global_init( CURL_GLOBAL_DEFAULT ); + CURLcode code = curl_global_init( CURL_GLOBAL_DEFAULT ); + if( code != 0 ) { + std::stringstream ss; + ss << "curl_global_init returned error code " << code << ", unable to proceed"; + std::string s = std::to_string(code); + throw RestClientException( ss.str() ); + } } - curl = curl_easy_init(); - if( curl == NULL ) { - return false; - } + try { + curl = curl_easy_init(); + if( curl == NULL ) { + throw RestClientException( "CURL did not return a handler, unable to proceed" ); + } - // curl_easy_setopt( curl, CURLOPT_VERBOSE, 1L ); - curl_easy_setopt( curl, CURLOPT_TIMEOUT, 5 ); + // curl_easy_setopt( curl, CURLOPT_VERBOSE, 1L ); + if( curl_easy_setopt( curl, CURLOPT_TIMEOUT, 5 ) != CURLE_OK ) { + throw RestClientException( "unable to set CURLOPT_TIMEOUT" ); + } - /* provide a buffer to store errors in */ - if( curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf) != CURLE_OK ) { - fprintf( stderr, "unable to set CURLOPT_ERRORBUFFER\n" ); - return false; - } - errbuf[0] = 0; + /* provide a buffer to store errors in */ + if( curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf) != CURLE_OK ) { + throw RestClientException( "unable to set CURLOPT_ERRORBUFFER" ); + } + errbuf[0] = 0; - if( curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, http_response_callback ) != CURLE_OK ) { - fprintf( stderr, "unable to set CURLOPT_WRITEFUNCTION\n" ); - return false; - } + if( curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, http_response_callback ) != CURLE_OK ) { + throw RestClientException( "unable to set CURLOPT_WRITEFUNCTION" ); + } - if( curl_easy_setopt( curl, CURLOPT_WRITEDATA, &response.body ) != CURLE_OK ) { - fprintf( stderr, "unable to set CURLOPT_WRITEDATA\n" ); - return false; - } + headers = curl_slist_append( headers, "Accept: application/json" ); + headers = curl_slist_append( headers, "Content-Type: application/json" ); + if( curl_easy_setopt( curl, CURLOPT_HTTPHEADER, headers ) != CURLE_OK ) { + throw RestClientException( "unable to set CURLOPT_HTTPHEADER" ); + } - headers = curl_slist_append( headers, "Accept: application/json" ); - headers = curl_slist_append( headers, "Content-Type: application/json" ); - if( curl_easy_setopt( curl, CURLOPT_HTTPHEADER, headers ) != CURLE_OK ) { - fprintf( stderr, "unable to set CURLOPT_HTTPHEADER\n" ); - return false; + } catch( const RestClientException &e ) { // avoid memory leakage + if( headers != NULL ) { + curl_slist_free_all( headers ); + } + if( curl != NULL ) { + curl_easy_cleanup( curl ); + } + + std::stringstream ss; + ss << "Failed to initialize RestClient: " << e.what(); + throw RestClientException( ss.str() ); } - return true; } /* @@ -113,32 +125,36 @@ bool RestClient::init( ) { Returns the HTTP status code and the correspoding message body. */ response_t RestClient::do_get( std::string path ) { - response = { 0, "" }; + response_t response = { 0, "" }; const std::string endpoint = baseUrl + path; + if( curl_easy_setopt( curl, CURLOPT_WRITEDATA, &response.body ) != CURLE_OK ) { + throw RestClientException( "unable to set CURLOPT_WRITEDATA" ); + } + if( curl_easy_setopt( curl, CURLOPT_URL, endpoint.c_str() ) != CURLE_OK ) { - fprintf( stderr, "unable to set CURLOPT_URL\n" ); - return response; + throw RestClientException( "unable to set CURLOPT_URL" ); } if( curl_easy_setopt( curl, CURLOPT_HTTPGET, 1L ) != CURLE_OK ) { - fprintf( stderr, "unable to set CURLOPT_HTTPGET\n" ); - return response; + throw RestClientException( "unable to set CURLOPT_HTTPGET" ); } CURLcode res = curl_easy_perform( curl ); if( res == CURLE_OK ) { if( curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &response.status_code ) != CURLE_OK ) { - fprintf( stderr, "unable to get CURLINFO_RESPONSE_CODE\n" ); + throw RestClientException( std::string("unable to get CURLINFO_RESPONSE_CODE. ") + errbuf ); } } else { size_t len = strlen( errbuf ); - fprintf( stderr, "unable to complete the request url=%s ", endpoint.c_str() ); + std::stringstream ss; + ss << "unable to complete the request at " << endpoint.c_str(); if(len) - fprintf( stderr, "error=%s%s", errbuf, - ( ( errbuf[len - 1] != '\n' ) ? "\n" : "") ); + ss << ". " << errbuf; else - fprintf( stderr, "error=%s\n", curl_easy_strerror( res ) ); + ss << ". " << curl_easy_strerror( res ); + + throw RestClientException( ss.str() ); } return response; @@ -149,36 +165,39 @@ response_t RestClient::do_get( std::string path ) { Returns the HTTP status code and the correspoding message body. */ response_t RestClient::do_post( std::string path, std::string json ) { - response = { 0, "" }; + response_t response = { 0, "" }; const std::string endpoint = baseUrl + path; + if( curl_easy_setopt( curl, CURLOPT_WRITEDATA, &response.body ) != CURLE_OK ) { + throw RestClientException( "unable to set CURLOPT_WRITEDATA" ); + } + if( curl_easy_setopt( curl, CURLOPT_URL, endpoint.c_str() ) != CURLE_OK ) { - fprintf( stderr, "unable to set CURLOPT_URL\n" ); - return response; + throw RestClientException( "unable to set CURLOPT_URL" ); } if( curl_easy_setopt( curl, CURLOPT_POST, 1L ) != CURLE_OK ) { - fprintf( stderr, "unable to set CURLOPT_POST\n" ); - return response; + throw RestClientException( "unable to set CURLOPT_POST" ); } if( curl_easy_setopt( curl, CURLOPT_POSTFIELDS, json.c_str() ) != CURLE_OK ) { - fprintf( stderr, "unable to set CURLOPT_POSTFIELDS\n" ); - return response; + throw RestClientException( "unable to set CURLOPT_POSTFIELDS" ); } CURLcode res = curl_easy_perform( curl ); if( res == CURLE_OK ) { if( curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &response.status_code ) != CURLE_OK ) { - fprintf( stderr, "unable to get CURLINFO_RESPONSE_CODE\n" ); + throw RestClientException( std::string("unable to get CURLINFO_RESPONSE_CODE. ") + errbuf ); } } else { size_t len = strlen( errbuf ); - fprintf( stderr, "unable to complete the request url=%s ", endpoint.c_str() ); + std::stringstream ss; + ss << "unable to complete the request at " << endpoint.c_str(); if(len) - fprintf( stderr, "error=%s%s", errbuf, - ( (errbuf[len - 1] != '\n' ) ? "\n" : "") ); + ss << ". " << errbuf; else - fprintf( stderr, "error=%s\n", curl_easy_strerror( res ) ); + ss << ". " << curl_easy_strerror( res ); + + throw RestClientException( ss.str() ); } return response; diff --git a/src/utils/restclient.hpp b/src/utils/restclient.hpp index b2d64ed..d17b681 100644 --- a/src/utils/restclient.hpp +++ b/src/utils/restclient.hpp @@ -32,6 +32,7 @@ #include #include +#include namespace restclient { @@ -43,12 +44,11 @@ typedef struct resp { class RestClient { private: std::string baseUrl; - response_t response; CURL *curl = NULL; struct curl_slist *headers = NULL; char errbuf[CURL_ERROR_SIZE]; - bool init( ); + void init( ); public: RestClient( std::string baseUrl ); @@ -58,6 +58,12 @@ class RestClient { response_t do_post( std::string path, std::string json ); }; +class RestClientException : public std::runtime_error { + public: + RestClientException( const std::string &error ) + : std::runtime_error{ error.c_str() } { } +}; + } // namespace