blob: 120eef884a8c4a41596bce6a7f707a5af4455661 [file] [log] [blame]
Julian Hall83632872023-03-06 14:33:32 +00001/*
2 * Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
Julian Hall83632872023-03-06 14:33:32 +00007#include "http_caller.h"
8
Gyorgy Szing3c446242023-03-31 01:53:15 +02009#include <assert.h>
10#include <curl/curl.h>
11#include <stddef.h>
12#include <stdint.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16
17#include "protocols/rpc/common/packed-c/header.h"
18#include "protocols/rpc/common/packed-c/status.h"
19#include "trace.h"
20
21#define USER_AGENT "libcurl-agent/1.0"
Julian Hall83632872023-03-06 14:33:32 +000022
23struct payload_buffer {
24 uint8_t *data;
25 size_t size;
26 size_t pos;
27};
28
Julian Hall83632872023-03-06 14:33:32 +000029static rpc_call_handle call_begin(void *context, uint8_t **req_buf, size_t req_len);
30static rpc_status_t call_invoke(void *context, rpc_call_handle handle, uint32_t opcode,
Gyorgy Szing3c446242023-03-31 01:53:15 +020031 rpc_opstatus_t *opstatus, uint8_t **resp_buf, size_t *resp_len);
Julian Hall83632872023-03-06 14:33:32 +000032static void call_end(void *context, rpc_call_handle handle);
33
34static size_t request_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
35{
36 size_t bytes_requested = size * nmemb;
37 struct payload_buffer *buf = (struct payload_buffer *)userdata;
38
39 size_t bytes_remaining = buf->size - buf->pos;
Gyorgy Szing3c446242023-03-31 01:53:15 +020040 size_t bytes_to_send = (bytes_remaining < bytes_requested) ? bytes_remaining :
41 bytes_requested;
Julian Hall83632872023-03-06 14:33:32 +000042
43 memcpy(ptr, &buf->data[buf->pos], bytes_to_send);
44
45 buf->pos += bytes_to_send;
46
47 return bytes_to_send;
48}
49
50static size_t response_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
51{
52 size_t bytes_received = size * nmemb;
53 struct payload_buffer *buf = (struct payload_buffer *)userdata;
54
55 buf->data = realloc(buf->data, buf->size + bytes_received);
56
57 if (buf->data) {
58 memcpy(&buf->data[buf->size], ptr, bytes_received);
59 buf->size += bytes_received;
60 } else {
61 EMSG("Out of memory");
62 bytes_received = 0;
63 }
64
65 return bytes_received;
66}
67
Gyorgy Szing3c446242023-03-31 01:53:15 +020068static bool send_head_request(const char *url, struct payload_buffer *response_buf, long *http_code)
Julian Hall83632872023-03-06 14:33:32 +000069{
70 assert(url);
71 assert(response_buf);
72
73 bool is_success = false;
74 CURL *curl_session = curl_easy_init();
75
76 *http_code = 0;
77
78 if (curl_session) {
Julian Hall83632872023-03-06 14:33:32 +000079 curl_easy_setopt(curl_session, CURLOPT_URL, url);
80 curl_easy_setopt(curl_session, CURLOPT_NOBODY, 1L);
81 curl_easy_setopt(curl_session, CURLOPT_WRITEFUNCTION, response_callback);
82 curl_easy_setopt(curl_session, CURLOPT_WRITEDATA, (void *)response_buf);
83 curl_easy_setopt(curl_session, CURLOPT_USERAGENT, USER_AGENT);
84
85 CURLcode status = curl_easy_perform(curl_session);
86
87 if (status == CURLE_OK) {
Julian Hall83632872023-03-06 14:33:32 +000088 status = curl_easy_getinfo(curl_session, CURLINFO_RESPONSE_CODE, http_code);
Gyorgy Szing3c446242023-03-31 01:53:15 +020089 is_success = (status == CURLE_OK) && (*http_code >= 200) &&
90 (*http_code < 300);
Julian Hall83632872023-03-06 14:33:32 +000091 }
92
93 curl_easy_cleanup(curl_session);
94 } else {
95 EMSG("Failed to init Curl session");
96 }
97
98 return is_success;
99}
100
Gyorgy Szing3c446242023-03-31 01:53:15 +0200101static bool send_put_request(const char *url, struct payload_buffer *request_buf,
102 struct payload_buffer *response_buf)
Julian Hall83632872023-03-06 14:33:32 +0000103{
104 bool is_success = false;
105
106 assert(url);
107 assert(request_buf);
108 assert(response_buf);
109
110 CURL *curl_session = curl_easy_init();
111
112 if (curl_session) {
Julian Hall83632872023-03-06 14:33:32 +0000113 curl_easy_setopt(curl_session, CURLOPT_UPLOAD, 1L);
114 curl_easy_setopt(curl_session, CURLOPT_URL, url);
115 curl_easy_setopt(curl_session, CURLOPT_READFUNCTION, request_callback);
116 curl_easy_setopt(curl_session, CURLOPT_READDATA, (void *)request_buf);
Gyorgy Szing3c446242023-03-31 01:53:15 +0200117 curl_easy_setopt(curl_session, CURLOPT_INFILESIZE_LARGE,
118 (curl_off_t)request_buf->size);
Julian Hall83632872023-03-06 14:33:32 +0000119 curl_easy_setopt(curl_session, CURLOPT_WRITEFUNCTION, response_callback);
120 curl_easy_setopt(curl_session, CURLOPT_WRITEDATA, (void *)response_buf);
121 curl_easy_setopt(curl_session, CURLOPT_USERAGENT, USER_AGENT);
122
123 CURLcode status = curl_easy_perform(curl_session);
124
125 if (status == CURLE_OK) {
Julian Hall83632872023-03-06 14:33:32 +0000126 long http_code = 0;
127
Gyorgy Szing3c446242023-03-31 01:53:15 +0200128 status =
129 curl_easy_getinfo(curl_session, CURLINFO_RESPONSE_CODE, &http_code);
130 is_success = (status == CURLE_OK) && (http_code >= 200) &&
131 (http_code < 300);
Julian Hall83632872023-03-06 14:33:32 +0000132 }
133
134 curl_easy_cleanup(curl_session);
135 } else {
136 EMSG("Failed to init Curl session");
137 }
138
139 return is_success;
140}
141
Gyorgy Szing3c446242023-03-31 01:53:15 +0200142static void prepare_call_url(const struct http_caller *s, unsigned int opcode, char *url_buf,
143 size_t url_buf_size)
Julian Hall83632872023-03-06 14:33:32 +0000144{
145 size_t base_url_len = strnlen(s->rpc_call_url, HTTP_CALLER_MAX_URL_LEN);
146
147 assert(base_url_len > 0);
148 assert(base_url_len < url_buf_size);
149
150 memset(url_buf, 0, url_buf_size);
151 memcpy(url_buf, s->rpc_call_url, base_url_len);
152
153 /* Ensure '/' is present before adding opcode */
154 if (url_buf[base_url_len - 1] != '/') {
Julian Hall83632872023-03-06 14:33:32 +0000155 url_buf[base_url_len] = '/';
156 base_url_len += 1;
157 }
158
159 size_t remaining_space = url_buf_size - base_url_len;
Gyorgy Szing3c446242023-03-31 01:53:15 +0200160 size_t opcode_len = snprintf(&url_buf[base_url_len], remaining_space, "%u", opcode);
Julian Hall83632872023-03-06 14:33:32 +0000161
162 assert(opcode_len < remaining_space);
163}
164
165struct rpc_caller *http_caller_init(struct http_caller *s)
166{
167 assert(s);
168
169 struct rpc_caller *base = &s->rpc_caller;
170
171 rpc_caller_init(base, s);
172 base->call_begin = call_begin;
173 base->call_invoke = call_invoke;
174 base->call_end = call_end;
175
176 memset(s->rpc_call_url, 0, sizeof(s->rpc_call_url));
177
178 s->req_body_size = 0;
179 s->req_body_buf = NULL;
180 s->resp_body_buf = NULL;
181
182 CURLcode status = curl_global_init(CURL_GLOBAL_ALL);
183
184 return (status == CURLE_OK) ? base : NULL;
185}
186
187void http_caller_deinit(struct http_caller *s)
188{
189 assert(s);
190
191 s->rpc_caller.context = NULL;
192 s->rpc_caller.call_begin = NULL;
193 s->rpc_caller.call_invoke = NULL;
194 s->rpc_caller.call_end = NULL;
195
196 call_end(s, s);
197}
198
199bool http_caller_probe(const char *url, long *http_code)
200{
201 assert(url);
202
203 struct payload_buffer response_buf;
204
205 response_buf.data = NULL;
206 response_buf.size = 0;
207
208 bool is_reached = send_head_request(url, &response_buf, http_code);
209
210 free(response_buf.data);
211
212 return is_reached;
213}
214
215int http_caller_open(struct http_caller *s, const char *rpc_call_url)
216{
217 assert(s);
218 assert(rpc_call_url);
219
220 strncpy(s->rpc_call_url, rpc_call_url, sizeof(s->rpc_call_url));
221
222 return 0;
223}
224
225int http_caller_close(struct http_caller *s)
226{
227 (void)s;
228 return 0;
229}
230
Gyorgy Szing3c446242023-03-31 01:53:15 +0200231static rpc_call_handle call_begin(void *context, uint8_t **req_buf, size_t req_len)
Julian Hall83632872023-03-06 14:33:32 +0000232{
233 assert(context);
234 assert(req_buf || !req_len);
235
236 rpc_call_handle handle = NULL;
237 struct http_caller *s = (struct http_caller *)context;
238
239 if (!s->req_body_buf) {
Julian Hall83632872023-03-06 14:33:32 +0000240 size_t req_body_size = sizeof(struct ts_rpc_req_hdr) + req_len;
241
242 s->req_body_buf = malloc(req_body_size);
243
244 if (s->req_body_buf) {
Julian Hall83632872023-03-06 14:33:32 +0000245 memset(s->req_body_buf, 0, req_body_size);
246
247 handle = s;
248 *req_buf = &s->req_body_buf[sizeof(struct ts_rpc_req_hdr)];
249 s->req_body_size = req_body_size;
250
251 struct ts_rpc_req_hdr *rpc_hdr = (struct ts_rpc_req_hdr *)s->req_body_buf;
252
253 rpc_hdr->encoding = s->rpc_caller.encoding;
254 rpc_hdr->param_len = req_len;
255
256 } else {
257 EMSG("Out of memory");
258 }
259 }
260
261 return handle;
262}
263
Gyorgy Szing3c446242023-03-31 01:53:15 +0200264static rpc_status_t call_invoke(void *context, const rpc_call_handle handle, uint32_t opcode,
265 rpc_opstatus_t *opstatus, uint8_t **resp_buf, size_t *resp_len)
Julian Hall83632872023-03-06 14:33:32 +0000266{
267 rpc_status_t rpc_status = TS_RPC_ERROR_INTERNAL;
268 struct http_caller *s = (struct http_caller *)context;
269
270 *resp_len = 0;
271
272 if ((handle == s) && s->req_body_buf) {
Julian Hall83632872023-03-06 14:33:32 +0000273 struct ts_rpc_req_hdr *rpc_hdr = (struct ts_rpc_req_hdr *)s->req_body_buf;
Gyorgy Szing3c446242023-03-31 01:53:15 +0200274 struct payload_buffer request_buf = { 0 };
275 struct payload_buffer response_buf = { 0 };
Julian Hall83632872023-03-06 14:33:32 +0000276
277 rpc_status = TS_RPC_ERROR_EP_DOES_NOT_EXIT;
278
279 rpc_hdr->opcode = opcode;
280
281 request_buf.data = s->req_body_buf;
282 request_buf.size = s->req_body_size;
283
284 char call_url[HTTP_CALLER_MAX_URL_LEN];
285
286 prepare_call_url(s, opcode, call_url, sizeof(call_url));
287
Gyorgy Szing3c446242023-03-31 01:53:15 +0200288 if (send_put_request(call_url, &request_buf, &response_buf) && response_buf.data &&
289 response_buf.size >= sizeof(struct ts_rpc_resp_hdr)) {
290 struct ts_rpc_resp_hdr *resp_hdr =
291 (struct ts_rpc_resp_hdr *)response_buf.data;
292 size_t response_param_len =
293 response_buf.size - sizeof(struct ts_rpc_resp_hdr);
Julian Hall83632872023-03-06 14:33:32 +0000294
295 if (resp_hdr->param_len == response_param_len) {
Julian Hall83632872023-03-06 14:33:32 +0000296 rpc_status = resp_hdr->rpc_status;
297
298 if (rpc_status == TS_RPC_CALL_ACCEPTED) {
Gyorgy Szing3c446242023-03-31 01:53:15 +0200299 *resp_buf =
300 &response_buf.data[sizeof(struct ts_rpc_resp_hdr)];
Julian Hall83632872023-03-06 14:33:32 +0000301 *resp_len = response_param_len;
302
303 *opstatus = resp_hdr->op_status;
304 }
305
306 } else {
Julian Hall83632872023-03-06 14:33:32 +0000307 rpc_status = TS_RPC_ERROR_INVALID_RESP_BODY;
308 }
309 }
310 }
311
312 return rpc_status;
313}
314
Gyorgy Szing3c446242023-03-31 01:53:15 +0200315static void call_end(void *context, const rpc_call_handle handle)
Julian Hall83632872023-03-06 14:33:32 +0000316{
317 struct http_caller *s = (struct http_caller *)context;
318
319 if ((handle == s) && s->req_body_buf) {
Julian Hall83632872023-03-06 14:33:32 +0000320 free(s->req_body_buf);
321 s->req_body_buf = NULL;
322
323 free(s->resp_body_buf);
324 s->resp_body_buf = NULL;
325 }
326}