Skip to content

Commit deb474a

Browse files
Added windows cli support to elasticurl. (#20)
* Added windows cli support to elasticurl.
1 parent 4c98208 commit deb474a

File tree

2 files changed

+71
-64
lines changed

2 files changed

+71
-64
lines changed

CMakeLists.txt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,4 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}-config.cmake"
7979
include(CTest)
8080
add_subdirectory(tests)
8181

82-
if (NOT WIN32)
83-
# uncomment this to run on windows when we implement getoptargs() on that platform
84-
add_subdirectory(bin/elasticurl)
85-
endif()
82+
add_subdirectory(bin/elasticurl)

bin/elasticurl/main.c

Lines changed: 70 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <aws/http/connection.h>
1616
#include <aws/http/request_response.h>
1717

18+
#include <aws/common/command_line_parser.h>
1819
#include <aws/common/condition_variable.h>
1920
#include <aws/common/mutex.h>
2021
#include <aws/common/string.h>
@@ -29,12 +30,17 @@
2930
#include <aws/io/tls_channel_handler.h>
3031
#include <aws/io/uri.h>
3132

32-
#include <getopt.h>
33+
#ifdef _MSC_VER
34+
# pragma warning(disable : 4996) /* Disable warnings about fopen() being insecure */
35+
# pragma warning(disable : 4204) /* Declared initializers */
36+
# pragma warning(disable : 4221) /* Local var in declared initializer */
37+
#endif
3338

3439
struct elasticurl_ctx {
3540
struct aws_allocator *allocator;
3641
const char *verb;
3742
struct aws_uri uri;
43+
struct aws_mutex mutex;
3844
struct aws_condition_variable c_var;
3945
bool response_code_written;
4046
const char *cacert;
@@ -51,6 +57,7 @@ struct elasticurl_ctx {
5157
FILE *output;
5258
const char *trace_file;
5359
enum aws_log_level log_level;
60+
bool exchange_completed;
5461
};
5562

5663
static void s_usage(void) {
@@ -80,33 +87,33 @@ static void s_usage(void) {
8087
exit(1);
8188
}
8289

83-
static struct option s_long_options[] = {
84-
{"cacert", required_argument, NULL, 'a'},
85-
{"capath", required_argument, NULL, 'b'},
86-
{"cert", required_argument, NULL, 'c'},
87-
{"key", required_argument, NULL, 'e'},
88-
{"connect-timeout", required_argument, NULL, 'f'},
89-
{"header", required_argument, NULL, 'H'},
90-
{"data", required_argument, NULL, 'd'},
91-
{"data-file", required_argument, NULL, 'g'},
92-
{"method", required_argument, NULL, 'M'},
93-
{"get", no_argument, NULL, 'G'},
94-
{"post", no_argument, NULL, 'P'},
95-
{"head", no_argument, NULL, 'I'},
96-
{"include", no_argument, NULL, 'i'},
97-
{"insecure", no_argument, NULL, 'k'},
98-
{"output", required_argument, NULL, 'o'},
99-
{"trace", required_argument, NULL, 't'},
100-
{"verbose", required_argument, NULL, 'v'},
101-
{"help", no_argument, NULL, 'h'},
90+
static struct aws_cli_option s_long_options[] = {
91+
{"cacert", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'a'},
92+
{"capath", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'b'},
93+
{"cert", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'c'},
94+
{"key", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'e'},
95+
{"connect-timeout", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'f'},
96+
{"header", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'H'},
97+
{"data", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'd'},
98+
{"data-file", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'g'},
99+
{"method", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'M'},
100+
{"get", AWS_CLI_OPTIONS_NO_ARGUMENT, NULL, 'G'},
101+
{"post", AWS_CLI_OPTIONS_NO_ARGUMENT, NULL, 'P'},
102+
{"head", AWS_CLI_OPTIONS_NO_ARGUMENT, NULL, 'I'},
103+
{"include", AWS_CLI_OPTIONS_NO_ARGUMENT, NULL, 'i'},
104+
{"insecure", AWS_CLI_OPTIONS_NO_ARGUMENT, NULL, 'k'},
105+
{"output", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'o'},
106+
{"trace", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 't'},
107+
{"verbose", AWS_CLI_OPTIONS_REQUIRED_ARGUMENT, NULL, 'v'},
108+
{"help", AWS_CLI_OPTIONS_NO_ARGUMENT, NULL, 'h'},
102109
/* Per getopt(3) the last element of the array has to be filled with all zeros */
103-
{NULL, no_argument, NULL, 0},
110+
{NULL, AWS_CLI_OPTIONS_NO_ARGUMENT, NULL, 0},
104111
};
105112

106113
static void s_parse_options(int argc, char **argv, struct elasticurl_ctx *ctx) {
107114
while (true) {
108115
int option_index = 0;
109-
int c = getopt_long(argc, argv, "a:b:c:e:f:H:d:g:M:GPHiko:t:v:h", s_long_options, &option_index);
116+
int c = aws_cli_getopt_long(argc, argv, "a:b:c:e:f:H:d:g:M:GPHiko:t:v:h", s_long_options, &option_index);
110117
if (c == -1) {
111118
break;
112119
}
@@ -116,42 +123,40 @@ static void s_parse_options(int argc, char **argv, struct elasticurl_ctx *ctx) {
116123
/* getopt_long() returns 0 if an option.flag is non-null */
117124
break;
118125
case 'a':
119-
ctx->cacert = optarg;
126+
ctx->cacert = aws_cli_optarg;
120127
break;
121128
case 'b':
122-
ctx->capath = optarg;
129+
ctx->capath = aws_cli_optarg;
123130
break;
124131
case 'c':
125-
ctx->cert = optarg;
132+
ctx->cert = aws_cli_optarg;
126133
break;
127134
case 'e':
128-
ctx->key = optarg;
135+
ctx->key = aws_cli_optarg;
129136
break;
130137
case 'f':
131-
ctx->connect_timeout = atoi(optarg);
138+
ctx->connect_timeout = atoi(aws_cli_optarg);
132139
break;
133140
case 'H':
134141
if (ctx->header_line_count >= sizeof(ctx->header_lines) / sizeof(const char *)) {
135142
fprintf(stderr, "currently only 10 header lines are supported.\n");
136143
s_usage();
137-
exit(1);
138144
}
139-
ctx->header_lines[ctx->header_line_count++] = optarg;
145+
ctx->header_lines[ctx->header_line_count++] = aws_cli_optarg;
140146
break;
141147
case 'd':
142-
ctx->data = aws_byte_cursor_from_c_str(optarg);
148+
ctx->data = aws_byte_cursor_from_c_str(aws_cli_optarg);
143149
break;
144150
case 'g':
145151

146-
ctx->data_file = fopen(optarg, "r");
152+
ctx->data_file = fopen(aws_cli_optarg, "rb");
147153
if (!ctx->data_file) {
148-
fprintf(stderr, "unable to open file %s.\n", optarg);
154+
fprintf(stderr, "unable to open file %s.\n", aws_cli_optarg);
149155
s_usage();
150-
exit(1);
151156
}
152157
break;
153158
case 'M':
154-
ctx->verb = optarg;
159+
ctx->verb = aws_cli_optarg;
155160
break;
156161
case 'G':
157162
ctx->verb = "GET";
@@ -169,44 +174,41 @@ static void s_parse_options(int argc, char **argv, struct elasticurl_ctx *ctx) {
169174
ctx->insecure = true;
170175
break;
171176
case 'o':
172-
ctx->output = fopen(optarg, "w");
177+
ctx->output = fopen(aws_cli_optarg, "wb");
173178

174179
if (!ctx->output) {
175-
fprintf(stderr, "unable to open file %s.\n", optarg);
180+
fprintf(stderr, "unable to open file %s.\n", aws_cli_optarg);
176181
s_usage();
177-
exit(1);
178182
}
179183
break;
180184
case 't':
181-
ctx->trace_file = optarg;
185+
ctx->trace_file = aws_cli_optarg;
182186
break;
183187
case 'v':
184-
if (!strcmp(optarg, "TRACE")) {
188+
if (!strcmp(aws_cli_optarg, "TRACE")) {
185189
ctx->log_level = AWS_LL_TRACE;
186-
} else if (!strcmp(optarg, "INFO")) {
190+
} else if (!strcmp(aws_cli_optarg, "INFO")) {
187191
ctx->log_level = AWS_LL_INFO;
188-
} else if (!strcmp(optarg, "DEBUG")) {
192+
} else if (!strcmp(aws_cli_optarg, "DEBUG")) {
189193
ctx->log_level = AWS_LL_DEBUG;
190-
} else if (!strcmp(optarg, "ERROR")) {
194+
} else if (!strcmp(aws_cli_optarg, "ERROR")) {
191195
ctx->log_level = AWS_LL_ERROR;
192196
} else {
193-
fprintf(stderr, "unsupported log level %s.\n", optarg);
197+
fprintf(stderr, "unsupported log level %s.\n", aws_cli_optarg);
194198
s_usage();
195-
exit(1);
196199
}
197200
break;
198201
case 'h':
199202
s_usage();
200-
exit(1);
203+
break;
201204
default:
202205
fprintf(stderr, "Unknown option\n");
203206
s_usage();
204-
exit(1);
205207
}
206208
}
207209

208-
if (optind < argc) {
209-
struct aws_byte_cursor uri_cursor = aws_byte_cursor_from_c_str(argv[optind++]);
210+
if (aws_cli_optind < argc) {
211+
struct aws_byte_cursor uri_cursor = aws_byte_cursor_from_c_str(argv[aws_cli_optind++]);
210212

211213
if (aws_uri_init_parse(&ctx->uri, ctx->allocator, &uri_cursor)) {
212214
fprintf(
@@ -215,12 +217,10 @@ static void s_parse_options(int argc, char **argv, struct elasticurl_ctx *ctx) {
215217
(char *)uri_cursor.ptr,
216218
aws_error_debug_str(aws_last_error()));
217219
s_usage();
218-
exit(1);
219220
};
220221
} else {
221222
fprintf(stderr, "A URI for the request must be supplied.\n");
222223
s_usage();
223-
exit(1);
224224
}
225225
}
226226

@@ -260,7 +260,12 @@ enum aws_http_outgoing_body_state s_stream_outgoing_body_fn(
260260
}
261261

262262
if (app_ctx->data_file) {
263+
#ifdef _WIN32
264+
size_t read_val = fread(buf->buffer, 1, buf->len, app_ctx->data_file);
265+
long long read = read_val == 0 ? ferror(app_ctx->data_file) : (long long)read_val;
266+
#else
263267
ssize_t read = fread(buf->buffer, 1, buf->len, app_ctx->data_file);
268+
#endif
264269

265270
/* if any data is left in the buffer, tell the client that we're still in progress,
266271
* otherwise say we're done. */
@@ -317,6 +322,9 @@ static void s_on_client_connection_setup(struct aws_http_connection *connection,
317322

318323
if (error_code) {
319324
fprintf(stderr, "Connection failed with error %s\n", aws_error_debug_str(error_code));
325+
aws_mutex_lock(&app_ctx->mutex);
326+
app_ctx->exchange_completed = true;
327+
aws_mutex_unlock(&app_ctx->mutex);
320328
aws_condition_variable_notify_all(&app_ctx->c_var);
321329
return;
322330
}
@@ -399,7 +407,6 @@ static void s_on_client_connection_setup(struct aws_http_connection *connection,
399407
exit(1);
400408
}
401409

402-
/* Release hold on connection, it will clean itself up once stream completes */
403410
aws_http_connection_release(connection);
404411
}
405412

@@ -408,9 +415,17 @@ static void s_on_client_connection_shutdown(struct aws_http_connection *connecti
408415
(void)connection;
409416
struct elasticurl_ctx *app_ctx = user_data;
410417

418+
aws_mutex_lock(&app_ctx->mutex);
419+
app_ctx->exchange_completed = true;
420+
aws_mutex_unlock(&app_ctx->mutex);
411421
aws_condition_variable_notify_all(&app_ctx->c_var);
412422
}
413423

424+
static bool s_completion_predicate(void *arg) {
425+
struct elasticurl_ctx *app_ctx = arg;
426+
return app_ctx->exchange_completed;
427+
}
428+
414429
AWS_STATIC_STRING_FROM_LITERAL(http_cmp1, "http");
415430
AWS_STATIC_STRING_FROM_LITERAL(http_cmp2, "Http");
416431
AWS_STATIC_STRING_FROM_LITERAL(http_cmp3, "HTTP");
@@ -428,17 +443,13 @@ int main(int argc, char **argv) {
428443
app_ctx.connect_timeout = 3000;
429444
app_ctx.output = stdout;
430445
app_ctx.verb = "GET";
446+
aws_mutex_init(&app_ctx.mutex);
431447

432448
s_parse_options(argc, argv, &app_ctx);
433449

434450
struct aws_logger logger;
435451
AWS_ZERO_STRUCT(logger);
436-
struct aws_log_writer log_writer;
437-
AWS_ZERO_STRUCT(log_writer);
438-
struct aws_log_formatter log_formatter;
439-
AWS_ZERO_STRUCT(log_formatter);
440-
struct aws_log_channel log_channel;
441-
AWS_ZERO_STRUCT(log_channel);
452+
442453
if (app_ctx.log_level) {
443454
aws_io_load_log_subject_strings();
444455
aws_http_load_log_subject_strings();
@@ -577,10 +588,9 @@ int main(int argc, char **argv) {
577588
.on_shutdown = s_on_client_connection_shutdown,
578589
};
579590

580-
struct aws_mutex semaphore_mutex = AWS_MUTEX_INIT;
581591
aws_http_client_connect(&http_client_options);
582-
aws_mutex_lock(&semaphore_mutex);
583-
aws_condition_variable_wait(&app_ctx.c_var, &semaphore_mutex);
592+
aws_mutex_lock(&app_ctx.mutex);
593+
aws_condition_variable_wait_pred(&app_ctx.c_var, &app_ctx.mutex, s_completion_predicate, &app_ctx);
584594

585595
aws_client_bootstrap_destroy(bootstrap);
586596
aws_event_loop_group_clean_up(&el_group);

0 commit comments

Comments
 (0)