Skip to content

aws: add support for EKS Pod Identities #9206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 31 additions & 8 deletions include/fluent-bit/flb_aws_credentials.h
Original file line number Diff line number Diff line change
@@ -256,19 +256,21 @@ struct flb_aws_provider *flb_aws_env_provider_create();
* Calling flb_aws_provider_destroy on this provider frees the memory
* used by host and path.
*/
struct flb_aws_provider *flb_http_provider_create(struct flb_config *config,
flb_sds_t host,
flb_sds_t path,
struct
flb_aws_client_generator
*generator);
struct flb_aws_provider *flb_endpoint_provider_create(struct flb_config *config,
flb_sds_t host,
flb_sds_t path,
int port,
int insecure,
struct
flb_aws_client_generator
*generator);

/*
* ECS Provider
* HTTP Provider for EKS and ECS
* The ECS Provider is just a wrapper around the HTTP Provider
* with the ECS credentials endpoint.
*/
struct flb_aws_provider *flb_ecs_provider_create(struct flb_config *config,
struct flb_aws_provider *flb_http_provider_create(struct flb_config *config,
struct
flb_aws_client_generator
*generator);
@@ -350,5 +352,26 @@ int try_lock_provider(struct flb_aws_provider *provider);
void unlock_provider(struct flb_aws_provider *provider);


/*
* HTTP Credentials Provider - retrieve credentials from a local http server
* Used to implement the ECS Credentials provider.
* Equivalent to:
* https://github.com/aws/aws-sdk-go/tree/master/aws/credentials/endpointcreds
*/

struct flb_aws_provider_http {
struct flb_aws_credentials *creds;
time_t next_refresh;

struct flb_aws_client *client;

/* Host and Path to request credentials */
flb_sds_t host;
flb_sds_t path;

flb_sds_t auth_token; /* optional */
};


#endif
#endif /* FLB_HAVE_AWS */
22 changes: 20 additions & 2 deletions include/fluent-bit/flb_aws_util.h
Original file line number Diff line number Diff line change
@@ -104,6 +104,17 @@ struct flb_aws_client {
int debug_only;
};

/* frees dynamic_headers */
struct flb_http_client *flb_aws_client_request_basic_auth(
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,
char *header_name,
char* auth_token);

/*
* Frees the aws_client, the internal flb_http_client, error_code,
* and flb_upstream.
@@ -144,13 +155,20 @@ flb_sds_t flb_aws_xml_error(char *response, size_t response_len);
flb_sds_t flb_aws_error(char *response, size_t response_len);

/*
* Similar to 'flb_aws_error', except it prints the JSON error type and message
* to the user in a error log.
* Similar to 'flb_aws_error', except it prints the JSON error __type and message
* field values to the user in a error log.
* 'api' is the name of the API that was called; this is used in the error log.
*/
void flb_aws_print_error(char *response, size_t response_len,
char *api, struct flb_output_instance *ins);

/*
* Error parsing for json APIs that respond with a
* Code and Message fields for error responses.
*/
void flb_aws_print_error_code(char *response, size_t response_len,
char *api);

/* Similar to 'flb_aws_print_error', but for APIs that return XML */
void flb_aws_print_xml_error(char *response, size_t response_len,
char *api, struct flb_output_instance *ins);
2 changes: 2 additions & 0 deletions include/fluent-bit/flb_utils.h
Original file line number Diff line number Diff line change
@@ -73,5 +73,7 @@ int flb_utils_get_machine_id(char **out_id, size_t *out_size);
void flb_utils_set_plugin_string_property(const char *name,
flb_sds_t *field_storage,
flb_sds_t new_value);
int flb_utils_url_split_sds(const flb_sds_t in_url, flb_sds_t *out_protocol,
flb_sds_t *out_host, flb_sds_t *out_port, flb_sds_t *out_uri);

#endif
2 changes: 1 addition & 1 deletion src/aws/flb_aws_credentials.c
Original file line number Diff line number Diff line change
@@ -581,7 +581,7 @@ static struct flb_aws_provider *standard_chain_create(struct flb_config
}
}

sub_provider = flb_ecs_provider_create(config, generator);
sub_provider = flb_http_provider_create(config, generator);
if (sub_provider) {
/* ECS Provider will fail creation if we are not running in ECS */
mk_list_add(&sub_provider->_head, &implementation->sub_providers);
195 changes: 145 additions & 50 deletions src/aws/flb_aws_credentials_http.c
Original file line number Diff line number Diff line change
@@ -22,10 +22,12 @@
#include <fluent-bit/flb_http_client.h>
#include <fluent-bit/flb_aws_credentials.h>
#include <fluent-bit/flb_aws_util.h>
#include <fluent-bit/flb_utils.h>

#include <fluent-bit/flb_jsmn.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

@@ -36,32 +38,44 @@

#define ECS_CREDENTIALS_HOST "169.254.170.2"
#define ECS_CREDENTIALS_HOST_LEN 13
#define ECS_CREDENTIALS_PATH_ENV_VAR "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
#define EKS_CREDENTIALS_HOST "169.254.170.23"
#define EKS_CREDENTIALS_HOST_LEN 14
#define AWS_CREDENTIALS_PATH "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
#define AWS_CREDENTIALS_FULL_URI "AWS_CONTAINER_CREDENTIALS_FULL_URI"

#define AUTH_TOKEN_ENV_VAR "AWS_CONTAINER_AUTHORIZATION_TOKEN"
#define AUTH_TOKEN_FILE_ENV_VAR "AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE"



/* Declarations */
struct flb_aws_provider_http;
static int http_credentials_request(struct flb_aws_provider_http
*implementation);


/*
* HTTP Credentials Provider - retrieve credentials from a local http server
* Used to implement the ECS Credentials provider.
* Equivalent to:
* https://github.com/aws/aws-sdk-go/tree/master/aws/credentials/endpointcreds
*/

struct flb_aws_provider_http {
struct flb_aws_credentials *creds;
time_t next_refresh;
/*
If the resolved URI’s scheme is HTTPS, its hostname may be used in the request.
Otherwise, implementations MUST fail to resolve when the URI hostname
does not satisfy any of the following conditions:
struct flb_aws_client *client;
is within the loopback CIDR (IPv4 127.0.0.0/8, IPv6 ::1/128)
is the ECS container host 169.254.170.2
is the EKS container host (IPv4 169.254.170.23, IPv6 fd00:ec2::23)*/
static int validate_http_credential_uri(flb_sds_t protocol, flb_sds_t host)
{
if (strncmp(protocol, "https", 5) == 0) {
return 0;
} else if (strncmp(host, "127.", 4) == 0 ||
strncmp(host, ECS_CREDENTIALS_HOST, ECS_CREDENTIALS_HOST_LEN) == 0 ||
strncmp(host, EKS_CREDENTIALS_HOST, EKS_CREDENTIALS_HOST_LEN) == 0 ||
strstr(host, "::1") != NULL ||
strstr(host, "fd00:ec2::23") != NULL ||
strstr(host, "fe80:") != NULL) {
return 0;
}

/* Host and Path to request credentials */
flb_sds_t host;
flb_sds_t path;
};
return -1;
}


struct flb_aws_credentials *get_credentials_fn_http(struct flb_aws_provider
@@ -83,6 +97,8 @@ struct flb_aws_credentials *get_credentials_fn_http(struct flb_aws_provider
if (try_lock_provider(provider)) {
http_credentials_request(implementation);
unlock_provider(provider);
} else {
flb_error("try_lock_provider failed");
}
}

@@ -229,16 +245,19 @@ static struct flb_aws_provider_vtable http_provider_vtable = {
.upstream_set = upstream_set_fn_http,
};

struct flb_aws_provider *flb_http_provider_create(struct flb_config *config,
flb_sds_t host,
flb_sds_t path,
struct
flb_aws_client_generator
*generator)
struct flb_aws_provider *flb_endpoint_provider_create(struct flb_config *config,
flb_sds_t host,
flb_sds_t path,
int port,
int insecure,
struct
flb_aws_client_generator
*generator)
{
struct flb_aws_provider_http *implementation = NULL;
struct flb_aws_provider *provider = NULL;
struct flb_upstream *upstream = NULL;
int io_flags = insecure == FLB_TRUE ? FLB_IO_TCP : FLB_IO_TLS;

flb_debug("[aws_credentials] Configuring HTTP provider with %s:80%s",
host, path);
@@ -266,7 +285,7 @@ struct flb_aws_provider *flb_http_provider_create(struct flb_config *config,
implementation->host = host;
implementation->path = path;

upstream = flb_upstream_create(config, host, 80, FLB_IO_TCP, NULL);
upstream = flb_upstream_create(config, host, port, io_flags, NULL);

if (!upstream) {
flb_aws_provider_destroy(provider);
@@ -289,7 +308,7 @@ struct flb_aws_provider *flb_http_provider_create(struct flb_config *config,
implementation->client->provider = NULL;
implementation->client->region = NULL;
implementation->client->service = NULL;
implementation->client->port = 80;
implementation->client->port = port;
implementation->client->flags = 0;
implementation->client->proxy = NULL;
implementation->client->upstream = upstream;
@@ -302,39 +321,85 @@ struct flb_aws_provider *flb_http_provider_create(struct flb_config *config,
* The ECS Provider is just a wrapper around the HTTP Provider
* with the ECS credentials endpoint.
*/

struct flb_aws_provider *flb_ecs_provider_create(struct flb_config *config,
struct
flb_aws_client_generator
*generator)
struct flb_aws_provider *flb_http_provider_create(struct flb_config *config,
struct
flb_aws_client_generator
*generator)
{
flb_sds_t host = NULL;
flb_sds_t path = NULL;
char *path_var = NULL;
flb_sds_t protocol = NULL;
flb_sds_t host = NULL;
flb_sds_t port_sds = NULL;
int port = 80;
int insecure = FLB_TRUE;
char *relative_uri = NULL;
char *full_uri = NULL;
int ret;

host = flb_sds_create_len(ECS_CREDENTIALS_HOST, ECS_CREDENTIALS_HOST_LEN);
if (!host) {
flb_errno();
return NULL;
}
relative_uri = getenv(AWS_CREDENTIALS_PATH);
full_uri = getenv(AWS_CREDENTIALS_FULL_URI);

path_var = getenv(ECS_CREDENTIALS_PATH_ENV_VAR);
if (path_var && strlen(path_var) > 0) {
path = flb_sds_create(path_var);
if (relative_uri && strlen(relative_uri) > 0) {
host = flb_sds_create_len(ECS_CREDENTIALS_HOST, ECS_CREDENTIALS_HOST_LEN);
if (!host) {
flb_errno();
return NULL;
}
path = flb_sds_create(relative_uri);
if (!path) {
flb_errno();
flb_free(host);
return NULL;
}

return flb_http_provider_create(config, host, path, generator);
} else if (full_uri && strlen(full_uri) > 0) {
ret = flb_utils_url_split_sds(full_uri, &protocol, &host, &port_sds, &path);
if (ret < 0) {
return NULL;
}
insecure = strncmp(protocol, "http", 4) == 0 ? FLB_TRUE : FLB_FALSE;
ret = validate_http_credential_uri(protocol, host);
if (ret < 0) {
flb_error("[aws credentials] %s must be set to an https:// address or a link local IP address."
" Found protocol=%s, host=%s, port=%s, path=%s",
AWS_CREDENTIALS_FULL_URI, protocol, host, port_sds, path);
flb_sds_destroy(protocol);
flb_sds_destroy(host);
flb_sds_destroy(port_sds);
flb_sds_destroy(path);
return NULL;
}
} else {
flb_debug("[aws_credentials] Not initializing ECS Provider because"
" %s is not set", ECS_CREDENTIALS_PATH_ENV_VAR);
flb_sds_destroy(host);
flb_debug("[aws_credentials] Not initializing ECS/EKS HTTP Provider because"
" %s and %s is not set", AWS_CREDENTIALS_PATH, AWS_CREDENTIALS_FULL_URI);
return NULL;
}

if (port_sds != NULL) {
port = atoi(port_sds);
if (port == 0) {
flb_error("[aws credentials] invalid port: %s must be set to an https:// address or a link local IP address."
" Found protocol=%s, host=%s, port=%s, path=%s",
AWS_CREDENTIALS_FULL_URI, protocol, host, port_sds, path);
flb_sds_destroy(protocol);
flb_sds_destroy(host);
flb_sds_destroy(port_sds);
flb_sds_destroy(path);
return NULL;
}
}

return flb_endpoint_provider_create(config, host, path, port, insecure, generator);

}

static void trim_newline(char *token)
{
int i;
for (i = strlen(token) - 1; i > 0; i--) {
if (token[i] == '\r' || token[i] == '\n') {
token[i] = '\0';
}
}
}

static int http_credentials_request(struct flb_aws_provider_http
@@ -346,14 +411,43 @@ static int http_credentials_request(struct flb_aws_provider_http
struct flb_aws_credentials *creds = NULL;
struct flb_aws_client *client = implementation->client;
struct flb_http_client *c = NULL;
int ret;
char *auth_token = NULL;
size_t auth_token_size = 0;
char *auth_token_path = NULL;

auth_token_path = getenv(AUTH_TOKEN_FILE_ENV_VAR);
auth_token = getenv(AUTH_TOKEN_ENV_VAR);
if (auth_token_path != NULL && strlen(auth_token_path) > 0) {
flb_debug("[aws] reading authorization token from %s", auth_token_path);
ret = flb_read_file(auth_token_path, &auth_token,
&auth_token_size);
if (ret < 0) {
flb_error("[aws credentials] failed to read authorization token from %s",
auth_token_path);
return -1;
}
}

c = client->client_vtable->request(client, FLB_HTTP_GET,
implementation->path, NULL, 0,
NULL, 0);
if (auth_token != NULL && strlen(auth_token) > 0) {
trim_newline(auth_token);
c = flb_aws_client_request_basic_auth(client, FLB_HTTP_GET, implementation->path,
NULL, 0, NULL, 0,
"Authorization",
auth_token);
} else {
c = client->client_vtable->request(client, FLB_HTTP_GET,
implementation->path, NULL, 0,
NULL, 0);
}

if (!c || c->resp.status != 200) {
flb_debug("[aws_credentials] http credentials request failed");
if (c) {
if (c->resp.payload_size > 0) {
flb_aws_print_error_code(c->resp.payload, c->resp.payload_size,
"ContainerCredentialsLocalServer");
}
flb_http_client_destroy(c);
}
return -1;
@@ -397,6 +491,7 @@ struct flb_aws_credentials *flb_parse_http_credentials(char *response,
expiration);
}

//TODO: error code handling
struct flb_aws_credentials *flb_parse_json_credentials(char *response,
size_t response_len,
char* session_token_field,
@@ -532,12 +627,12 @@ struct flb_aws_credentials *flb_parse_json_credentials(char *response,
goto error;
}
*expiration = flb_aws_cred_expiration(tmp);
flb_sds_destroy(tmp);
if (*expiration < 0) {
flb_warn("[aws_credentials] '%s' was invalid or "
"could not be parsed. Disabling auto-refresh of "
"credentials.", AWS_CREDENTIAL_RESPONSE_EXPIRATION);
"credentials.", tmp);
}
flb_sds_destroy(tmp);
}
}

86 changes: 82 additions & 4 deletions src/aws/flb_aws_util.c
Original file line number Diff line number Diff line change
@@ -169,6 +169,7 @@ char *removeProtocol (char *endpoint, char *protocol) {
return endpoint;
}


struct flb_http_client *flb_aws_client_request(struct flb_aws_client *aws_client,
int method, const char *uri,
const char *body, size_t body_len,
@@ -200,17 +201,59 @@ struct flb_http_client *flb_aws_client_request(struct flb_aws_client *aws_client
if (flb_aws_is_auth_error(c->resp.payload, c->resp.payload_size)
== FLB_TRUE) {
flb_info("[aws_client] auth error, refreshing creds");
aws_client->refresh_limit = time(NULL)
+ FLB_AWS_CREDENTIAL_REFRESH_LIMIT;
aws_client->provider->provider_vtable->
refresh(aws_client->provider);
aws_client->refresh_limit = time(NULL) + FLB_AWS_CREDENTIAL_REFRESH_LIMIT;
aws_client->provider->provider_vtable->refresh(aws_client->provider);
}
}
}

return c;
}

/* always frees dynamic_headers */
struct flb_http_client *flb_aws_client_request_basic_auth(
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,
char *header_name,
char* auth_token)
{
struct flb_http_client *c = NULL;
struct flb_aws_header *auth_header = NULL;
struct flb_aws_header *headers = NULL;
auth_header = flb_calloc(1, sizeof(struct flb_aws_header));
if (!auth_header) {
flb_errno();
return NULL;
}

auth_header->key = header_name;
auth_header->key_len = strlen(header_name);
auth_header->val = auth_token;
auth_header->val_len = strlen(auth_token);

if (dynamic_headers_len == 0) {
c = aws_client->client_vtable->request(aws_client, method, uri, body, body_len,
auth_header, 1);
} else {
headers = flb_realloc(dynamic_headers, (dynamic_headers_len + 1) * sizeof(struct flb_aws_header));
if (!headers) {
flb_free(auth_header);
flb_errno();
return NULL;
}
*(headers + dynamic_headers_len) = *auth_header;
c = aws_client->client_vtable->request(aws_client, method, uri, body, body_len,
headers, dynamic_headers_len + 1);
flb_free(headers);
}
flb_free(auth_header);
return c;
}

static struct flb_aws_client_vtable client_vtable = {
.request = flb_aws_client_request,
};
@@ -573,6 +616,10 @@ flb_sds_t flb_aws_xml_get_val(char *response, size_t response_len, char *tag, ch
return val;
}

/*
* Error parsing for json APIs that respond with an
* __type and message fields for error responses.
*/
void flb_aws_print_error(char *response, size_t response_len,
char *api, struct flb_output_instance *ins)
{
@@ -600,6 +647,37 @@ void flb_aws_print_error(char *response, size_t response_len,
flb_sds_destroy(error);
}

/*
* Error parsing for json APIs that respond with a
* Code and Message fields for error responses.
*/
void flb_aws_print_error_code(char *response, size_t response_len,
char *api)
{
flb_sds_t error;
flb_sds_t message;

error = flb_json_get_val(response, response_len, "Code");
if (!error) {
/* error can not be parsed, print raw response */
flb_warn("%s: Raw response: %s", api, response);
return;
}

message = flb_json_get_val(response, response_len, "Message");
if (!message) {
/* just print the error */
flb_error("%s API responded with code='%s'", api, error);
}
else {
flb_error("%s API responded with code='%s', message='%s'",
api, error, message);
flb_sds_destroy(message);
}

flb_sds_destroy(error);
}

/* parses AWS JSON API error responses and returns the value of the __type field */
flb_sds_t flb_aws_error(char *response, size_t response_len)
{
128 changes: 125 additions & 3 deletions src/flb_utils.c
Original file line number Diff line number Diff line change
@@ -1017,13 +1017,27 @@ int flb_utils_write_str_buf(const char *str, size_t str_len, char **out, size_t
static char *flb_copy_host(const char *string, int pos_init, int pos_end)
{
if (string[pos_init] == '[') { /* IPv6 */
if (string[pos_end-1] != ']')
if (string[pos_end-1] != ']') {
return NULL;

}
return mk_string_copy_substr(string, pos_init + 1, pos_end - 1);
}
else
else {
return mk_string_copy_substr(string, pos_init, pos_end);
}
}

static char *flb_utils_copy_host_sds(const char *string, int pos_init, int pos_end)
{
if (string[pos_init] == '[') { /* IPv6 */
if (string[pos_end-1] != ']') {
return NULL;
}
return flb_sds_create_len(string + pos_init + 1, pos_end - 1);
}
else {
return flb_sds_create_len(string + pos_init, pos_end);
}
}

int flb_utils_url_split(const char *in_url, char **out_protocol,
@@ -1121,6 +1135,114 @@ int flb_utils_url_split(const char *in_url, char **out_protocol,
return -1;
}

int flb_utils_url_split_sds(const flb_sds_t in_url, flb_sds_t *out_protocol,
flb_sds_t *out_host, flb_sds_t *out_port, flb_sds_t *out_uri)
{
flb_sds_t protocol = NULL;
flb_sds_t host = NULL;
flb_sds_t port = NULL;
flb_sds_t uri = NULL;
char *p = NULL;
char *tmp = NULL;
char *sep = NULL;

/* Protocol */
p = strstr(in_url, "://");
if (!p) {
return -1;
}
if (p == in_url) {
return -1;
}

protocol = flb_sds_create_len(in_url, p - in_url);
if (!protocol) {
flb_errno();
return -1;
}

/* Advance position after protocol */
p += 3;

/* Check for first '/' */
sep = strchr(p, '/');
tmp = strchr(p, ':');

/* Validate port separator is found before the first slash */
if (sep && tmp) {
if (tmp > sep) {
tmp = NULL;
}
}

if (tmp) {
host = flb_utils_copy_host_sds(p, 0, tmp - p);
if (!host) {
flb_errno();
goto error;
}
p = tmp + 1;

/* Look for an optional URI */
tmp = strchr(p, '/');
if (tmp) {
port = flb_sds_create_len(p, tmp - p);
uri = flb_sds_create(tmp);
}
else {
port = flb_sds_create_len(p, strlen(p));
uri = flb_sds_create("/");
}
}
else {
tmp = strchr(p, '/');
if (tmp) {
host = flb_utils_copy_host_sds(p, 0, tmp - p);
uri = flb_sds_create(tmp);
}
else {
host = flb_utils_copy_host_sds(p, 0, strlen(p));
uri = flb_sds_create("/");
}
}

if (!port) {
if (strcmp(protocol, "http") == 0) {
port = flb_sds_create("80");
}
else if (strcmp(protocol, "https") == 0) {
port = flb_sds_create("443");
}
}

if (!host) {
flb_errno();
goto error;
}
if (!port) {
flb_errno();
goto error;
}
if (!uri) {
flb_errno();
goto error;
}

*out_protocol = protocol;
*out_host = host;
*out_port = port;
*out_uri = uri;

return 0;

error:
if (protocol) {
flb_free(protocol);
}

return -1;
}


/*
* flb_utils_proxy_url_split parses a proxy's information from a http_proxy URL.
454 changes: 444 additions & 10 deletions tests/internal/aws_credentials_http.c

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
local-http-credential-server-authorization-token
83 changes: 83 additions & 0 deletions tests/internal/utils.c
Original file line number Diff line number Diff line change
@@ -38,6 +38,88 @@ struct url_check url_checks[] = {
{-1, "://", NULL, NULL, NULL, NULL},
};

void test_url_split_sds()
{
int i;
int ret;
int size;
flb_sds_t protocol;
flb_sds_t host;
flb_sds_t port;
flb_sds_t uri;
struct url_check *u;

size = sizeof(url_checks) / sizeof(struct url_check);
for (i = 0; i < size; i ++) {
u = &url_checks[i];

protocol = NULL;
host = NULL;
port = NULL;
uri = NULL;

ret = flb_utils_url_split_sds(u->url, &protocol, &host, &port, &uri);
TEST_CHECK(ret == u->ret);
if (ret == -1) {
continue;
}

/* protocol */
if (u->prot) {
TEST_CHECK(protocol != NULL);

ret = strcmp(u->prot, protocol);
TEST_CHECK(ret == 0);
}
else {
TEST_CHECK(protocol == NULL);
}

/* host */
if (u->host) {
TEST_CHECK(host != NULL);
ret = strcmp(u->host, host);
TEST_CHECK(ret == 0);
}
else {
TEST_CHECK(host == NULL);
}

/* port */
if (u->port) {
TEST_CHECK(port != NULL);
ret = strcmp(u->port, port);
TEST_CHECK(ret == 0);
}
else {
TEST_CHECK(port == NULL);
}

/* uri */
if (u->uri) {
TEST_CHECK(uri != NULL);
ret = strcmp(u->uri, uri);
TEST_CHECK(ret == 0);
}
else {
TEST_CHECK(uri == NULL);
}

if (protocol) {
flb_sds_destroy(protocol);
}
if (host) {
flb_sds_destroy(host);
}
if (port) {
flb_sds_destroy(port);
}
if (uri) {
flb_sds_destroy(uri);
}
}
}

void test_url_split()
{
int i;
@@ -657,6 +739,7 @@ void test_size_to_bytes()
TEST_LIST = {
/* JSON maps iteration */
{ "url_split", test_url_split },
{ "url_split_sds", test_url_split_sds },
{ "write_str", test_write_str },
{ "test_write_str_invalid_trailing_bytes", test_write_str_invalid_trailing_bytes },
{ "test_write_str_invalid_leading_byte", test_write_str_invalid_leading_byte },