1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 
3 #include <fluent-bit/flb_sds.h>
4 #include <fluent-bit/flb_aws_credentials.h>
5 #include <fluent-bit/flb_mem.h>
6 #include <fluent-bit/flb_info.h>
7 #include <fluent-bit/flb_http_client.h>
8 
9 #include <monkey/mk_core.h>
10 #include <string.h>
11 #include <unistd.h>
12 
13 #include "flb_tests_internal.h"
14 
15 #define ACCESS_KEY_HTTP "http_akid"
16 #define SECRET_KEY_HTTP "http_skid"
17 #define TOKEN_HTTP      "http_token"
18 
19 #define HTTP_CREDENTIALS_RESPONSE "{\n\
20     \"AccessKeyId\": \"http_akid\",\n\
21     \"Expiration\": \"2025-10-24T23:00:23Z\",\n\
22     \"RoleArn\": \"TASK_ROLE_ARN\",\n\
23     \"SecretAccessKey\": \"http_skid\",\n\
24     \"Token\": \"http_token\"\n\
25 }"
26 
27 /*
28  * Unexpected/invalid HTTP response. The goal of this is not to test anything
29  * that might happen in production, but rather to test the error handling
30  * code for the providers. This helps ensure all code paths are tested and
31  * the error handling code does not introduce memory leaks.
32  */
33 #define HTTP_RESPONSE_MALFORMED  "{\n\
34     \"AccessKeyId\": \"http_akid\",\n\
35     \"partially-correct\": \"json\",\n\
36     \"RoleArn\": \"TASK_ROLE_ARN\",\n\
37     \"but incomplete\": \"and not terminated with a closing brace\",\n\
38     \"Token\": \"http_token\""
39 
40 
41 /*
42  * Global Variable that allows us to check the number of calls
43  * made in each test
44  */
45 int g_request_count;
46 
request_happy_case(struct flb_aws_client * aws_client,int method,const char * uri)47 struct flb_http_client *request_happy_case(struct flb_aws_client *aws_client,
48                                            int method, const char *uri)
49 {
50     struct flb_http_client *c = NULL;
51 
52     TEST_CHECK(method == FLB_HTTP_GET);
53 
54     TEST_CHECK(strstr(uri, "happy-case") != NULL);
55 
56     /* create an http client so that we can set the response */
57     c = flb_calloc(1, sizeof(struct flb_http_client));
58     if (!c) {
59         flb_errno();
60         return NULL;
61     }
62     mk_list_init(&c->headers);
63 
64     c->resp.status = 200;
65     c->resp.payload = HTTP_CREDENTIALS_RESPONSE;
66     c->resp.payload_size = strlen(HTTP_CREDENTIALS_RESPONSE);
67 
68     return c;
69 }
70 
71 /* unexpected output test- see description for HTTP_RESPONSE_MALFORMED */
request_malformed(struct flb_aws_client * aws_client,int method,const char * uri)72 struct flb_http_client *request_malformed(struct flb_aws_client *aws_client,
73                                           int method, const char *uri)
74 {
75     struct flb_http_client *c = NULL;
76 
77     TEST_CHECK(method == FLB_HTTP_GET);
78 
79     TEST_CHECK(strstr(uri, "malformed") != NULL);
80 
81     /* create an http client so that we can set the response */
82     c = flb_calloc(1, sizeof(struct flb_http_client));
83     if (!c) {
84         flb_errno();
85         return NULL;
86     }
87     mk_list_init(&c->headers);
88 
89     c->resp.status = 200;
90     c->resp.payload = HTTP_RESPONSE_MALFORMED;
91     c->resp.payload_size = strlen(HTTP_RESPONSE_MALFORMED);
92 
93     return c;
94 }
95 
request_error_case(struct flb_aws_client * aws_client,int method,const char * uri)96 struct flb_http_client *request_error_case(struct flb_aws_client *aws_client,
97                                            int method, const char *uri)
98 {
99     struct flb_http_client *c = NULL;
100 
101     TEST_CHECK(method == FLB_HTTP_GET);
102 
103     TEST_CHECK(strstr(uri, "error-case") != NULL);
104 
105     /* create an http client so that we can set the response */
106     c = flb_calloc(1, sizeof(struct flb_http_client));
107     if (!c) {
108         flb_errno();
109         return NULL;
110     }
111     mk_list_init(&c->headers);
112 
113     c->resp.status = 400;
114     c->resp.payload = NULL;
115     c->resp.payload_size = 0;
116 
117     return c;
118 }
119 
120 /* test/mock version of the flb_aws_client request function */
test_http_client_request(struct flb_aws_client * aws_client,int method,const char * uri,const char * body,size_t body_len,struct flb_aws_header * dynamic_headers,size_t dynamic_headers_len)121 struct flb_http_client *test_http_client_request(struct flb_aws_client *aws_client,
122                                                  int method, const char *uri,
123                                                  const char *body, size_t body_len,
124                                                  struct flb_aws_header *dynamic_headers,
125                                                  size_t dynamic_headers_len)
126 {
127     g_request_count++;
128     /*
129      * route to the correct test case fn using the uri
130      */
131     if (strstr(uri, "happy-case") != NULL) {
132         return request_happy_case(aws_client, method, uri);
133     } else if (strstr(uri, "error-case") != NULL) {
134         return request_error_case(aws_client, method, uri);
135     } else if (strstr(uri, "malformed") != NULL) {
136         return request_malformed(aws_client, method, uri);
137     }
138 
139     /* uri should match one of the above conditions */
140     flb_errno();
141     return NULL;
142 
143 }
144 
145 /* Test/mock flb_aws_client */
146 static struct flb_aws_client_vtable test_vtable = {
147     .request = test_http_client_request,
148 };
149 
test_http_client_create()150 struct flb_aws_client *test_http_client_create()
151 {
152     struct flb_aws_client *client = flb_calloc(1,
153                                                 sizeof(struct flb_aws_client));
154     if (!client) {
155         flb_errno();
156         return NULL;
157     }
158     client->client_vtable = &test_vtable;
159     return client;
160 }
161 
162 /* Generator that returns clients with the test vtable */
163 static struct flb_aws_client_generator test_generator = {
164     .create = test_http_client_create,
165 };
166 
generator_in_test()167 struct flb_aws_client_generator *generator_in_test()
168 {
169     return &test_generator;
170 }
171 
172 /* http and ecs providers */
test_http_provider()173 static void test_http_provider()
174 {
175     struct flb_aws_provider *provider;
176     struct flb_aws_credentials *creds;
177     int ret;
178     struct flb_config *config;
179     flb_sds_t host;
180     flb_sds_t path;
181 
182     g_request_count = 0;
183 
184     config = flb_calloc(1, sizeof(struct flb_config));
185     if (!config) {
186         flb_errno();
187         return;
188     }
189 
190     mk_list_init(&config->upstreams);
191 
192     host = flb_sds_create("127.0.0.1");
193     if (!host) {
194         flb_errno();
195         return;
196     }
197     path = flb_sds_create("/happy-case");
198     if (!path) {
199         flb_errno();
200         return;
201     }
202 
203     provider = flb_http_provider_create(config, host, path,
204                                  generator_in_test());
205 
206     if (!provider) {
207         flb_errno();
208         return;
209     }
210 
211     /* repeated calls to get credentials should return the same set */
212     creds = provider->provider_vtable->get_credentials(provider);
213     if (!creds) {
214         flb_errno();
215         return;
216     }
217     TEST_CHECK(strcmp(ACCESS_KEY_HTTP, creds->access_key_id) == 0);
218     TEST_CHECK(strcmp(SECRET_KEY_HTTP, creds->secret_access_key) == 0);
219     TEST_CHECK(strcmp(TOKEN_HTTP, creds->session_token) == 0);
220 
221     flb_aws_credentials_destroy(creds);
222 
223     creds = provider->provider_vtable->get_credentials(provider);
224     if (!creds) {
225         flb_errno();
226         return;
227     }
228     TEST_CHECK(strcmp(ACCESS_KEY_HTTP, creds->access_key_id) == 0);
229     TEST_CHECK(strcmp(SECRET_KEY_HTTP, creds->secret_access_key) == 0);
230     TEST_CHECK(strcmp(TOKEN_HTTP, creds->session_token) == 0);
231 
232     flb_aws_credentials_destroy(creds);
233 
234     /* refresh should return 0 (success) */
235     ret = provider->provider_vtable->refresh(provider);
236     TEST_CHECK(ret == 0);
237 
238     /*
239      * Request count should be 2:
240      * - One for the first call to get_credentials (2nd should hit cred cache)
241      * - One for the call to refresh
242      */
243     TEST_CHECK(g_request_count == 2);
244 
245     flb_aws_provider_destroy(provider);
246     flb_free(config);
247 }
248 
test_http_provider_error_case()249 static void test_http_provider_error_case()
250 {
251     struct flb_aws_provider *provider;
252     struct flb_aws_credentials *creds;
253     int ret;
254     struct flb_config *config;
255     flb_sds_t host;
256     flb_sds_t path;
257 
258     g_request_count = 0;
259 
260     config = flb_calloc(1, sizeof(struct flb_config));
261     if (!config) {
262         flb_errno();
263         return;
264     }
265 
266     mk_list_init(&config->upstreams);
267 
268     host = flb_sds_create("127.0.0.1");
269     if (!host) {
270         flb_errno();
271         return;
272     }
273     path = flb_sds_create("/error-case");
274     if (!path) {
275         flb_errno();
276         return;
277     }
278 
279     provider = flb_http_provider_create(config, host, path,
280                                         generator_in_test());
281 
282     if (!provider) {
283         flb_errno();
284         return;
285     }
286 
287     /* get_credentials will fail */
288     creds = provider->provider_vtable->get_credentials(provider);
289     TEST_CHECK(creds == NULL);
290 
291     creds = provider->provider_vtable->get_credentials(provider);
292     TEST_CHECK(creds == NULL);
293 
294     /* refresh should return -1 (failure) */
295     ret = provider->provider_vtable->refresh(provider);
296     TEST_CHECK(ret < 0);
297 
298     /*
299      * Request count should be 3:
300      * - Each call to get_credentials and refresh invokes the client's
301      * request method and returns a request failure.
302      */
303     TEST_CHECK(g_request_count == 3);
304 
305     flb_aws_provider_destroy(provider);
306     flb_free(config);
307 }
308 
test_http_provider_malformed_response()309 static void test_http_provider_malformed_response()
310 {
311     struct flb_aws_provider *provider;
312     struct flb_aws_credentials *creds;
313     int ret;
314     struct flb_config *config;
315     flb_sds_t host;
316     flb_sds_t path;
317 
318     g_request_count = 0;
319 
320     config = flb_calloc(1, sizeof(struct flb_config));
321     if (!config) {
322         flb_errno();
323         return;
324     }
325 
326     mk_list_init(&config->upstreams);
327 
328     host = flb_sds_create("127.0.0.1");
329     if (!host) {
330         flb_errno();
331         return;
332     }
333     path = flb_sds_create("/malformed");
334     if (!path) {
335         flb_errno();
336         return;
337     }
338 
339     provider = flb_http_provider_create(config, host, path,
340                                  generator_in_test());
341 
342     if (!provider) {
343         flb_errno();
344         return;
345     }
346 
347     /* get_credentials will fail */
348     creds = provider->provider_vtable->get_credentials(provider);
349     TEST_CHECK(creds == NULL);
350 
351     creds = provider->provider_vtable->get_credentials(provider);
352     TEST_CHECK(creds == NULL);
353 
354     /* refresh should return -1 (failure) */
355     ret = provider->provider_vtable->refresh(provider);
356     TEST_CHECK(ret < 0);
357 
358     /*
359      * Request count should be 3:
360      * - Each call to get_credentials and refresh invokes the client's
361      * request method and returns a request failure.
362      */
363     TEST_CHECK(g_request_count == 3);
364 
365     flb_aws_provider_destroy(provider);
366     flb_free(config);
367 }
368 
369 TEST_LIST = {
370     { "test_http_provider" , test_http_provider},
371     { "test_http_provider_error_case" , test_http_provider_error_case},
372     { "test_http_provider_malformed_response" ,
373     test_http_provider_malformed_response},
374     { 0 }
375 };
376