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>
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
3439struct 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
5663static 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
106113static 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+
414429AWS_STATIC_STRING_FROM_LITERAL (http_cmp1 , "http" );
415430AWS_STATIC_STRING_FROM_LITERAL (http_cmp2 , "Http" );
416431AWS_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