nrf_client.cpp 9.52 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.1  (the "License"); you may not use this
 * file except in compliance with the License. You may obtain a copy of the
 * License at
 *
 *      http://www.openairinterface.org/?page_id=698
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

/*! \file nrf_client.cpp
 \brief
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
24
 \author  Tien-Thinh NGUYEN
25
 \company Eurecom
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
26
27
 \date 2020
 \email: Tien-Thinh.Nguyen@eurecom.fr
28
29
 */

30
#include "nrf_client.hpp"
31
32
33
34
35

#include <curl/curl.h>
#include <pistache/http.h>
#include <pistache/mime.h>
#include <nlohmann/json.hpp>
36
#include <stdexcept>
37

38
#include "3gpp_29.500.h"
39
#include "logger.hpp"
40
#include "nrf.h"
41
#include "nrf_config.hpp"
42
43
44
45
46
47

using namespace Pistache::Http;
using namespace Pistache::Http::Mime;
using namespace oai::nrf::app;
using json = nlohmann::json;

Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
48
extern nrf_client* nrf_client_inst;
49
extern nrf_config nrf_cfg;
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
50

51
//------------------------------------------------------------------------------
52
// To read content of the response from NF
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
53
54
static std::size_t callback(
    const char* in, std::size_t size, std::size_t num, std::string* out) {
55
56
57
58
59
  const std::size_t totalBytes(size * num);
  out->append(in, totalBytes);
  return totalBytes;
}

60
//------------------------------------------------------------------------------
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
61
nrf_client::nrf_client(nrf_event& ev) : m_event_sub(ev) {
62
63
  curl_global_init(CURL_GLOBAL_DEFAULT);
  curl_multi = curl_multi_init();
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
64
  handles    = {};
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
65
66
67
68
69
  headers    = NULL;
  headers    = curl_slist_append(headers, "Accept: application/json");
  headers    = curl_slist_append(headers, "Content-Type: application/json");
  headers    = curl_slist_append(headers, "charsets: utf-8");
  // subscribe_task_curl();
70
71
72
73
74
75
76
77
78
79
}

//------------------------------------------------------------------------------
nrf_client::~nrf_client() {
  Logger::nrf_app().debug("Delete NRF Client instance...");
  // Remove handle, free memory
  for (auto h : handles) {
    curl_multi_remove_handle(curl_multi, h);
    curl_easy_cleanup(h);
  }
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
80
81

  handles.clear();
82
83
  curl_multi_cleanup(curl_multi);
  curl_global_cleanup();
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
84
  curl_slist_free_all(headers);
85
86
87
88

  if (task_connection.connected()) task_connection.disconnect();
}

89
//------------------------------------------------------------------------------
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
90
91
92
CURL* nrf_client::curl_create_handle(
    const std::string& uri, const std::string& data,
    std::string& response_data) {
93
  // create handle for a curl request
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
94
  CURL* curl = curl_easy_init();
95
96
97
98
99
100
101

  if (curl) {
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_URL, uri.c_str());
    // curl_easy_setopt(curl, CURLOPT_PRIVATE, str);
    // curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);
    curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
102
    curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, NF_CURL_TIMEOUT_MS);
103
104
    // Hook up data handling function.
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &callback);
105
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_data);
106
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
107
108
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, data.length());
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
109
110
111
112
  }
  return curl;
}

113
//------------------------------------------------------------------------------
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
114
115
116
void nrf_client::send_curl_multi(
    const std::string& uri, const std::string& data,
    std::string& response_data) {
117
118
  // create a new handle and add to the multi handle
  // the curl will actually be sent in perform_curl_multi
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
119
  CURL* tmp = curl_create_handle(uri, data, response_data);
120
121
122
123
124
125
126
  curl_multi_add_handle(curl_multi, tmp);
  handles.push_back(tmp);
}

//------------------------------------------------------------------------------
void nrf_client::perform_curl_multi(uint64_t ms) {
  _unused(ms);
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
127
128
129
130
131
132
133
134
135
136
137
138
  int still_running = 0, numfds = 0;

  CURLMcode code = curl_multi_perform(curl_multi, &still_running);

  do {
    code = curl_multi_wait(curl_multi, NULL, 0, 200000, &numfds);
    if (code != CURLM_OK) {
      Logger::nrf_app().debug("curl_multi_wait() returned %d!", code);
    }
    curl_multi_perform(curl_multi, &still_running);
  } while (still_running);

139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
  curl_release_handles();
}

//------------------------------------------------------------------------------
void nrf_client::wait_curl_end() {
  // block until activity is detected on at least one of the handles or
  // MAX_WAIT_MSECS has passed.
  int still_running = 0, numfds = 0;
  do {
    CURLMcode code = curl_multi_perform(curl_multi, &still_running);
    if (code == CURLM_OK) {
      code = curl_multi_wait(curl_multi, NULL, 0, MAX_WAIT_MSECS, &numfds);
      if (code != CURLM_OK) break;
    } else {
      break;
    }
  } while (still_running);

  curl_release_handles();
}

//------------------------------------------------------------------------------
void nrf_client::curl_release_handles() {
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
162
163
164
165
166
  CURLMsg* curl_msg = nullptr;
  CURL* curl        = nullptr;
  CURLcode code     = {};
  int http_code     = 0;
  int msgs_left     = 0;
167
168

  while ((curl_msg = curl_multi_info_read(curl_multi, &msgs_left))) {
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
169
    if (curl_msg && curl_msg->msg == CURLMSG_DONE) {
170
171
172
173
174
175
176
      curl = curl_msg->easy_handle;
      code = curl_msg->data.result;
      // int res = curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &curl_url);
      if (code != CURLE_OK) {
        Logger::nrf_app().debug("CURL error code  %d!", curl_msg->data.result);
        continue;
      }
177
      // Get HTTP code
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
178
179
      curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
      Logger::nrf_app().debug("Got response with HTTP code  %d!", http_code);
180
181
182
183
184
185

      // TODO: remove handle from the multi session and end this handle now, or
      // later
      curl_multi_remove_handle(curl_multi, curl);
      curl_easy_cleanup(curl);

Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
186
      std::vector<CURL*>::iterator it;
187
188
189
190
191
      it = find(handles.begin(), handles.end(), curl);
      if (it != handles.end()) {
        handles.erase(it);
      }

Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
192
193
194
195
196
197
    } else if (curl_msg) {
      curl = curl_msg->easy_handle;
      Logger::nrf_app().debug("Error after curl_multi_info_read()");
      curl_multi_remove_handle(curl_multi, curl);
      curl_easy_cleanup(curl);

Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
198
      std::vector<CURL*>::iterator it;
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
199
200
201
202
      it = find(handles.begin(), handles.end(), curl);
      if (it != handles.end()) {
        handles.erase(it);
      }
203
    } else {
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
204
      Logger::nrf_app().debug("curl_msg null");
205
206
207
208
209
210
    }
  }
}

//------------------------------------------------------------------------------
void nrf_client::notify_subscribed_event(
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
211
212
    const std::shared_ptr<nrf_profile>& profile, const uint8_t& event_type,
    const std::vector<std::string>& uris) {
213
214
215
216
217
218
  Logger::nrf_app().debug(
      "Send notification for the subscribed event to the subscriptions");

  std::map<std::string, std::string> responses = {};
  // Fill the json part
  nlohmann::json json_data = {};
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
219
  json_data["event"]       = notification_event_type_e2str[event_type];
220
221
222
223
224

  std::vector<struct in_addr> instance_addrs = {};
  profile.get()->get_nf_ipv4_addresses(instance_addrs);
  // TODO: use the first IPv4 addr for now
  std::string instance_uri =
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
225
      std::string(inet_ntoa(*((struct in_addr*) &(instance_addrs[0]))));
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
  Logger::nrf_app().debug("NF instance URI: %s", instance_uri.c_str());
  json_data["nfInstanceUri"] = instance_uri;

  // NF profile
  if ((event_type == NOTIFICATION_TYPE_NF_REGISTERED) or
      (event_type == NOTIFICATION_TYPE_NF_PROFILE_CHANGED)) {
    nlohmann::json json_profile = {};
    switch (profile.get()->get_nf_type()) {
      case NF_TYPE_AMF: {
        std::static_pointer_cast<amf_profile>(profile).get()->to_json(
            json_profile);
      } break;
      case NF_TYPE_SMF: {
        std::static_pointer_cast<smf_profile>(profile).get()->to_json(
            json_profile);
      } break;
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
242
      case NF_TYPE_UPF: {
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
243
        std::static_pointer_cast<upf_profile>(profile).get()->to_json(
Rohan's avatar
Rohan committed
244
245
246
247
            json_profile);
      } break;
      case NF_TYPE_AUSF: {
        std::static_pointer_cast<ausf_profile>(profile).get()->to_json(
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
248
249
250
251
252
            json_profile);
      } break;
      default: {
        profile.get()->to_json(json_profile);
      }
253
254
255
256
257
258
259
260
    }
    json_data["nfProfile"] = json_profile;
  }

  std::string body = json_data.dump();

  for (auto uri : uris) {
    responses[uri] = "";
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
261
    std::unique_ptr<std::string> httpData(new std::string());
262
263
    send_curl_multi(uri, body, responses[uri]);
  }
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
264

265
266
  perform_curl_multi(
      0);  // TODO: current time as parameter if curl is performed per event
267
268
}

Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
269
//------------------------------------------------------------------------------
270
void nrf_client::subscribe_task_curl() {
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
271
  struct itimerspec its;
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
272
  its.it_value.tv_sec  = 100;               // TODO: to be updated 100 seconds
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
273
274
275
276
277
  its.it_value.tv_nsec = 10 * 1000 * 1000;  // 10ms
  const uint64_t interval =
      its.it_value.tv_sec * 1000 +
      its.it_value.tv_nsec / 1000000;  // convert sec, nsec to msec

278
  task_connection = m_event_sub.subscribe_task_tick(
Tien-Thinh Nguyen's avatar
Tien-Thinh Nguyen committed
279
      boost::bind(&nrf_client::perform_curl_multi, this, _1), interval, 0);
280
}