Fix dummy values in gRPC message sent to RC xApp
[ric-app/ts.git] / src / utils / restclient.cpp
1 // vi: ts=4 sw=4 noet:
2 /*
3 ==================================================================================
4     Copyright (c) 2022 Alexandre Huff
5
6    Licensed under the Apache License, Version 2.0 (the "License");
7    you may not use this file except in compliance with the License.
8    You may obtain a copy of the License at
9
10        http://www.apache.org/licenses/LICENSE-2.0
11
12    Unless required by applicable law or agreed to in writing, software
13    distributed under the License is distributed on an "AS IS" BASIS,
14    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15    See the License for the specific language governing permissions and
16    limitations under the License.
17 ==================================================================================
18 */
19
20 /*
21     Mnemonic:   restclient.cpp
22     Abstract:   Implements a tiny wrapper on top of libcurl to handle with rest requests.
23
24     Date:       8 Apr 2022
25     Author:     Alexandre Huff
26 */
27
28
29 #include "restclient.hpp"
30 #include <mutex>
31 #include <string.h>
32 #include <memory>
33
34 namespace restclient {
35
36 // callback to handle http responses
37 static size_t http_response_callback( const char *in, size_t size, size_t num, std::string *out ) {
38     if( out == NULL ) {
39         return 0;
40     }
41     const size_t totalBytes( size * num );
42     out->append( in, totalBytes );
43     return totalBytes;
44 }
45
46 /*
47     Create a RestClient instance to exchange messages with
48     a given rest api available on baseUrl, which consists of
49     scheme://domain[:port]
50 */
51 RestClient::RestClient( std::string baseUrl ) {
52     this->baseUrl = baseUrl;
53
54     if( ! init() ) {
55          fprintf( stderr, "unable to initialize RestClient\n" );
56     }
57 }
58
59 RestClient::~RestClient( ) {
60     curl_slist_free_all( headers );
61     curl_easy_cleanup( curl );
62 }
63
64 std::string RestClient::getBaseUrl( ) {
65     return baseUrl;
66 }
67
68 bool RestClient::init( ) {
69     static std::mutex curl_mutex;
70
71     {   // scoped mutex to make curl_global_init thread-safe
72         const std::lock_guard<std::mutex> lock( curl_mutex );
73         curl_global_init( CURL_GLOBAL_DEFAULT );
74     }
75
76     curl = curl_easy_init();
77     if( curl == NULL ) {
78         return false;
79     }
80
81     // curl_easy_setopt( curl, CURLOPT_VERBOSE, 1L );
82     curl_easy_setopt( curl, CURLOPT_TIMEOUT, 5 );
83
84     /* provide a buffer to store errors in */
85     if( curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf) != CURLE_OK ) {
86         fprintf( stderr, "unable to set CURLOPT_ERRORBUFFER\n" );
87         return false;
88     }
89     errbuf[0] = 0;
90
91     if( curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, http_response_callback ) != CURLE_OK ) {
92         fprintf( stderr, "unable to set CURLOPT_WRITEFUNCTION\n" );
93         return false;
94     }
95
96     if( curl_easy_setopt( curl, CURLOPT_WRITEDATA, &response.body ) != CURLE_OK ) {
97         fprintf( stderr, "unable to set CURLOPT_WRITEDATA\n" );
98         return false;
99     }
100
101     headers = curl_slist_append( headers, "Accept: application/json" );
102     headers = curl_slist_append( headers, "Content-Type: application/json" );
103     if( curl_easy_setopt( curl, CURLOPT_HTTPHEADER, headers ) != CURLE_OK ) {
104         fprintf( stderr, "unable to set CURLOPT_HTTPHEADER\n" );
105         return false;
106     }
107
108     return true;
109 }
110
111 /*
112     Executes a GET request at the path of this RestClient instance.
113     Returns the HTTP status code and the correspoding message body.
114 */
115 response_t RestClient::do_get( std::string path ) {
116     response = { 0, "" };
117
118     const std::string endpoint = baseUrl + path;
119
120     if( curl_easy_setopt( curl, CURLOPT_URL, endpoint.c_str() ) != CURLE_OK ) {
121         fprintf( stderr, "unable to set CURLOPT_URL\n" );
122         return response;
123     }
124     if( curl_easy_setopt( curl, CURLOPT_HTTPGET, 1L ) != CURLE_OK ) {
125         fprintf( stderr, "unable to set CURLOPT_HTTPGET\n" );
126         return response;
127     }
128
129     CURLcode res = curl_easy_perform( curl );
130     if( res == CURLE_OK ) {
131         if( curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &response.status_code ) != CURLE_OK ) {
132             fprintf( stderr, "unable to get CURLINFO_RESPONSE_CODE\n" );
133         }
134     } else {
135         size_t len = strlen( errbuf );
136         fprintf( stderr, "unable to complete the request url=%s ", endpoint.c_str() );
137         if(len)
138             fprintf( stderr, "error=%s%s", errbuf,
139                     ( ( errbuf[len - 1] != '\n' ) ? "\n" : "") );
140         else
141             fprintf( stderr, "error=%s\n", curl_easy_strerror( res ) );
142     }
143
144     return response;
145 }
146
147 /*
148     Executes a POST request of a json message at the path of this RestClient instance.
149     Returns the HTTP status code and the correspoding message body.
150 */
151 response_t RestClient::do_post( std::string path, std::string json ) {
152     response = { 0, "" };
153
154     const std::string endpoint = baseUrl + path;
155
156     if( curl_easy_setopt( curl, CURLOPT_URL, endpoint.c_str() ) != CURLE_OK ) {
157         fprintf( stderr, "unable to set CURLOPT_URL\n" );
158         return response;
159     }
160     if( curl_easy_setopt( curl, CURLOPT_POST, 1L ) != CURLE_OK ) {
161         fprintf( stderr, "unable to set CURLOPT_POST\n" );
162         return response;
163     }
164     if( curl_easy_setopt( curl, CURLOPT_POSTFIELDS, json.c_str() ) != CURLE_OK ) {
165         fprintf( stderr, "unable to set CURLOPT_POSTFIELDS\n" );
166         return response;
167     }
168
169     CURLcode res = curl_easy_perform( curl );
170     if( res == CURLE_OK ) {
171         if( curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &response.status_code ) != CURLE_OK ) {
172             fprintf( stderr, "unable to get CURLINFO_RESPONSE_CODE\n" );
173         }
174     } else {
175         size_t len = strlen( errbuf );
176         fprintf( stderr, "unable to complete the request url=%s ", endpoint.c_str() );
177         if(len)
178             fprintf( stderr, "error=%s%s", errbuf,
179                     ( (errbuf[len - 1] != '\n' ) ? "\n" : "") );
180         else
181             fprintf( stderr, "error=%s\n", curl_easy_strerror( res ) );
182     }
183
184     return response;
185 }
186
187 } // namespace