Improve error handling on rest api 44/8744/1
authorAlexandre Huff <alexandrehuff@utfpr.edu.br>
Thu, 7 Jul 2022 23:07:32 +0000 (20:07 -0300)
committerAlexandre Huff <alexandrehuff@utfpr.edu.br>
Thu, 7 Jul 2022 23:07:32 +0000 (20:07 -0300)
This change includes error handling improvements in the REST API.

Issue-ID: RICAPP-198

Signed-off-by: Alexandre Huff <alexandrehuff@utfpr.edu.br>
Change-Id: Id20541ba0a925407bbca32ebbdd5c37adf5692cd

Dockerfile
src/ts_xapp/ts_xapp.cpp
src/utils/restclient.cpp
src/utils/restclient.hpp

index 7a2d2e9..44c1e04 100644 (file)
@@ -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" ]
index 47bcd8f..02a88fe 100644 (file)
@@ -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<rapidjson::StringBuffer> 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<rapidjson::StringBuffer> 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<string> nb_list = get_nodeb_list( client );
+  try {
+    restclient::RestClient client( base_url );
+
+    vector<string> 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<Xapp>( new Xapp( port, true ) );
 
   xfw->Add_msg_cb( A1_POLICY_REQ, policy_callback, NULL );          // msg type 20010
index afdb356..ac9013b 100644 (file)
@@ -30,6 +30,7 @@
 #include <mutex>
 #include <string.h>
 #include <memory>
+#include <sstream>
 
 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<std::mutex> 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;
index b2d64ed..d17b681 100644 (file)
@@ -32,6 +32,7 @@
 
 #include <curl/curl.h>
 #include <string>
+#include <stdexcept>
 
 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