25 |
|
|
26 |
|
#include "serf.h" |
27 |
|
|
28 |
+ |
/* Add Connection: close header to each request. */ |
29 |
+ |
/* #define CONNECTION_CLOSE_HDR */ |
30 |
+ |
|
31 |
|
typedef struct { |
32 |
|
const char *hostinfo; |
33 |
|
int using_ssl; |
34 |
+ |
int head_request; |
35 |
|
serf_ssl_context_t *ssl_ctx; |
36 |
|
serf_bucket_alloc_t *bkt_alloc; |
37 |
|
} app_baton_t; |
41 |
|
apr_status_t why, |
42 |
|
apr_pool_t *pool) |
43 |
|
{ |
44 |
+ |
app_baton_t *ctx = closed_baton; |
45 |
+ |
|
46 |
+ |
ctx->ssl_ctx = NULL; |
47 |
+ |
|
48 |
|
if (why) { |
49 |
|
abort(); |
50 |
|
} |
70 |
|
static apr_status_t ignore_all_cert_errors(void *data, int failures, |
71 |
|
const serf_ssl_certificate_t *cert) |
72 |
|
{ |
73 |
< |
print_ssl_cert_errors(failures); |
73 |
> |
print_ssl_cert_errors(failures); |
74 |
|
|
75 |
|
/* In a real application, you would normally would not want to do this */ |
76 |
|
return APR_SUCCESS; |
113 |
|
subject = serf_ssl_cert_subject(current, pool); |
114 |
|
issuer = serf_ssl_cert_issuer(current, pool); |
115 |
|
serf_cert = serf_ssl_cert_certificate(current, pool); |
116 |
< |
|
116 |
> |
|
117 |
|
fprintf(stderr, "\n-----BEGIN CERTIFICATE-----\n"); |
118 |
|
fprintf(stderr, "Hostname: %s\n", |
119 |
|
(const char *)apr_hash_get(subject, "CN", APR_HASH_KEY_STRING)); |
137 |
|
fprintf(stderr, "%s\n", serf_ssl_cert_export(current, pool)); |
138 |
|
fprintf(stderr, "-----END CERTIFICATE-----\n"); |
139 |
|
++certs; |
140 |
< |
} |
140 |
> |
} |
141 |
|
|
142 |
|
apr_pool_destroy(pool); |
143 |
|
return APR_SUCCESS; |
178 |
|
apr_pool_t *pool) |
179 |
|
{ |
180 |
|
serf_bucket_t *c; |
181 |
+ |
serf_bucket_t *response; |
182 |
|
serf_bucket_alloc_t *bkt_alloc; |
183 |
+ |
app_baton_t *app_ctx = acceptor_baton; |
184 |
|
|
185 |
|
/* get the per-request bucket allocator */ |
186 |
|
bkt_alloc = serf_request_get_alloc(request); |
188 |
|
/* Create a barrier so the response doesn't eat us! */ |
189 |
|
c = serf_bucket_barrier_create(stream, bkt_alloc); |
190 |
|
|
191 |
< |
return serf_bucket_response_create(c, bkt_alloc); |
191 |
> |
response = serf_bucket_response_create(c, bkt_alloc); |
192 |
> |
|
193 |
> |
if (app_ctx->head_request) |
194 |
> |
serf_bucket_response_set_head(response); |
195 |
> |
|
196 |
> |
return response; |
197 |
|
} |
198 |
|
|
199 |
|
typedef struct { |
203 |
|
apr_atomic_t completed_requests; |
204 |
|
#endif |
205 |
|
int print_headers; |
206 |
+ |
apr_file_t *output_file; |
207 |
|
|
208 |
|
serf_response_acceptor_t acceptor; |
209 |
|
app_baton_t *acceptor_baton; |
214 |
|
const char *method; |
215 |
|
const char *path; |
216 |
|
const char *req_body_path; |
217 |
< |
const char *authn; |
217 |
> |
const char *username; |
218 |
> |
const char *password; |
219 |
> |
int auth_attempts; |
220 |
> |
serf_bucket_t *req_hdrs; |
221 |
|
} handler_baton_t; |
222 |
|
|
223 |
|
/* Kludges for APR 0.9 support. */ |
227 |
|
#define apr_atomic_read32 apr_atomic_read |
228 |
|
#endif |
229 |
|
|
230 |
+ |
|
231 |
+ |
static int append_request_headers(void *baton, |
232 |
+ |
const char *key, |
233 |
+ |
const char *value) |
234 |
+ |
{ |
235 |
+ |
serf_bucket_t *hdrs_bkt = baton; |
236 |
+ |
serf_bucket_headers_setc(hdrs_bkt, key, value); |
237 |
+ |
return 0; |
238 |
+ |
} |
239 |
+ |
|
240 |
+ |
static apr_status_t setup_request(serf_request_t *request, |
241 |
+ |
void *setup_baton, |
242 |
+ |
serf_bucket_t **req_bkt, |
243 |
+ |
serf_response_acceptor_t *acceptor, |
244 |
+ |
void **acceptor_baton, |
245 |
+ |
serf_response_handler_t *handler, |
246 |
+ |
void **handler_baton, |
247 |
+ |
apr_pool_t *pool) |
248 |
+ |
{ |
249 |
+ |
handler_baton_t *ctx = setup_baton; |
250 |
+ |
serf_bucket_t *hdrs_bkt; |
251 |
+ |
serf_bucket_t *body_bkt; |
252 |
+ |
|
253 |
+ |
if (ctx->req_body_path) { |
254 |
+ |
apr_file_t *file; |
255 |
+ |
apr_status_t status; |
256 |
+ |
|
257 |
+ |
status = apr_file_open(&file, ctx->req_body_path, APR_READ, |
258 |
+ |
APR_OS_DEFAULT, pool); |
259 |
+ |
|
260 |
+ |
if (status) { |
261 |
+ |
printf("Error opening file (%s)\n", ctx->req_body_path); |
262 |
+ |
return status; |
263 |
+ |
} |
264 |
+ |
|
265 |
+ |
body_bkt = serf_bucket_file_create(file, |
266 |
+ |
serf_request_get_alloc(request)); |
267 |
+ |
} |
268 |
+ |
else { |
269 |
+ |
body_bkt = NULL; |
270 |
+ |
} |
271 |
+ |
|
272 |
+ |
*req_bkt = serf_request_bucket_request_create(request, ctx->method, |
273 |
+ |
ctx->path, body_bkt, |
274 |
+ |
serf_request_get_alloc(request)); |
275 |
+ |
|
276 |
+ |
hdrs_bkt = serf_bucket_request_get_headers(*req_bkt); |
277 |
+ |
|
278 |
+ |
serf_bucket_headers_setn(hdrs_bkt, "User-Agent", |
279 |
+ |
"Serf/" SERF_VERSION_STRING); |
280 |
+ |
/* Shouldn't serf do this for us? */ |
281 |
+ |
serf_bucket_headers_setn(hdrs_bkt, "Accept-Encoding", "gzip"); |
282 |
+ |
#ifdef CONNECTION_CLOSE_HDR |
283 |
+ |
serf_bucket_headers_setn(hdrs_bkt, "Connection", "close"); |
284 |
+ |
#endif |
285 |
+ |
|
286 |
+ |
/* Add the extra headers from the command line */ |
287 |
+ |
if (ctx->req_hdrs != NULL) { |
288 |
+ |
serf_bucket_headers_do(ctx->req_hdrs, append_request_headers, hdrs_bkt); |
289 |
+ |
} |
290 |
+ |
|
291 |
+ |
*acceptor = ctx->acceptor; |
292 |
+ |
*acceptor_baton = ctx->acceptor_baton; |
293 |
+ |
*handler = ctx->handler; |
294 |
+ |
*handler_baton = ctx; |
295 |
+ |
|
296 |
+ |
return APR_SUCCESS; |
297 |
+ |
} |
298 |
+ |
|
299 |
|
static apr_status_t handle_response(serf_request_t *request, |
300 |
|
serf_bucket_t *response, |
301 |
|
void *handler_baton, |
302 |
|
apr_pool_t *pool) |
303 |
|
{ |
216 |
– |
const char *data; |
217 |
– |
apr_size_t len; |
304 |
|
serf_status_line sl; |
305 |
|
apr_status_t status; |
306 |
|
handler_baton_t *ctx = handler_baton; |
307 |
|
|
308 |
|
if (!response) { |
309 |
< |
/* A NULL response can come back if the request failed completely */ |
310 |
< |
return APR_EGENERAL; |
309 |
> |
/* A NULL response probably means that the connection was closed while |
310 |
> |
this request was already written. Just requeue it. */ |
311 |
> |
serf_connection_t *conn = serf_request_get_conn(request); |
312 |
> |
|
313 |
> |
serf_connection_request_create(conn, setup_request, handler_baton); |
314 |
> |
return APR_SUCCESS; |
315 |
|
} |
316 |
+ |
|
317 |
|
status = serf_bucket_response_status(response, &sl); |
318 |
|
if (status) { |
319 |
|
return status; |
320 |
|
} |
321 |
|
|
322 |
|
while (1) { |
323 |
< |
status = serf_bucket_read(response, 2048, &data, &len); |
323 |
> |
struct iovec vecs[64]; |
324 |
> |
int vecs_read; |
325 |
> |
apr_size_t bytes_written; |
326 |
> |
|
327 |
> |
status = serf_bucket_read_iovec(response, 8000, 64, vecs, &vecs_read); |
328 |
|
if (SERF_BUCKET_READ_ERROR(status)) |
329 |
|
return status; |
330 |
|
|
331 |
|
/* got some data. print it out. */ |
332 |
< |
fwrite(data, 1, len, stdout); |
332 |
> |
if (vecs_read) { |
333 |
> |
apr_file_writev(ctx->output_file, vecs, vecs_read, &bytes_written); |
334 |
> |
} |
335 |
|
|
336 |
|
/* are we done yet? */ |
337 |
|
if (APR_STATUS_IS_EOF(status)) { |
339 |
|
serf_bucket_t *hdrs; |
340 |
|
hdrs = serf_bucket_response_get_headers(response); |
341 |
|
while (1) { |
342 |
< |
status = serf_bucket_read(hdrs, 2048, &data, &len); |
342 |
> |
status = serf_bucket_read_iovec(hdrs, 8000, 64, vecs, |
343 |
> |
&vecs_read); |
344 |
> |
|
345 |
|
if (SERF_BUCKET_READ_ERROR(status)) |
346 |
|
return status; |
347 |
|
|
348 |
< |
fwrite(data, 1, len, stdout); |
348 |
> |
if (vecs_read) { |
349 |
> |
apr_file_writev(ctx->output_file, vecs, vecs_read, |
350 |
> |
&bytes_written); |
351 |
> |
} |
352 |
|
if (APR_STATUS_IS_EOF(status)) { |
353 |
|
break; |
354 |
|
} |
368 |
|
/* NOTREACHED */ |
369 |
|
} |
370 |
|
|
371 |
< |
static apr_status_t setup_request(serf_request_t *request, |
372 |
< |
void *setup_baton, |
373 |
< |
serf_bucket_t **req_bkt, |
374 |
< |
serf_response_acceptor_t *acceptor, |
375 |
< |
void **acceptor_baton, |
376 |
< |
serf_response_handler_t *handler, |
377 |
< |
void **handler_baton, |
276 |
< |
apr_pool_t *pool) |
371 |
> |
static apr_status_t |
372 |
> |
credentials_callback(char **username, |
373 |
> |
char **password, |
374 |
> |
serf_request_t *request, void *baton, |
375 |
> |
int code, const char *authn_type, |
376 |
> |
const char *realm, |
377 |
> |
apr_pool_t *pool) |
378 |
|
{ |
379 |
< |
handler_baton_t *ctx = setup_baton; |
279 |
< |
serf_bucket_t *hdrs_bkt; |
280 |
< |
serf_bucket_t *body_bkt; |
379 |
> |
handler_baton_t *ctx = baton; |
380 |
|
|
381 |
< |
if (ctx->req_body_path) { |
382 |
< |
apr_file_t *file; |
383 |
< |
apr_status_t status; |
285 |
< |
|
286 |
< |
status = apr_file_open(&file, ctx->req_body_path, APR_READ, |
287 |
< |
APR_OS_DEFAULT, pool); |
288 |
< |
|
289 |
< |
if (status) { |
290 |
< |
printf("Error opening file (%s)\n", ctx->req_body_path); |
291 |
< |
return status; |
292 |
< |
} |
293 |
< |
|
294 |
< |
body_bkt = serf_bucket_file_create(file, |
295 |
< |
serf_request_get_alloc(request)); |
381 |
> |
if (ctx->auth_attempts > 0) |
382 |
> |
{ |
383 |
> |
return SERF_ERROR_AUTHN_FAILED; |
384 |
|
} |
385 |
< |
else { |
386 |
< |
body_bkt = NULL; |
387 |
< |
} |
385 |
> |
else |
386 |
> |
{ |
387 |
> |
*username = (char*)ctx->username; |
388 |
> |
*password = (char*)ctx->password; |
389 |
> |
ctx->auth_attempts++; |
390 |
|
|
391 |
< |
*req_bkt = serf_request_bucket_request_create(request, ctx->method, |
302 |
< |
ctx->path, body_bkt, |
303 |
< |
serf_request_get_alloc(request)); |
304 |
< |
|
305 |
< |
hdrs_bkt = serf_bucket_request_get_headers(*req_bkt); |
306 |
< |
|
307 |
< |
serf_bucket_headers_setn(hdrs_bkt, "User-Agent", |
308 |
< |
"Serf/" SERF_VERSION_STRING); |
309 |
< |
/* Shouldn't serf do this for us? */ |
310 |
< |
serf_bucket_headers_setn(hdrs_bkt, "Accept-Encoding", "gzip"); |
311 |
< |
|
312 |
< |
if (ctx->authn != NULL) { |
313 |
< |
serf_bucket_headers_setn(hdrs_bkt, "Authorization", ctx->authn); |
391 |
> |
return APR_SUCCESS; |
392 |
|
} |
315 |
– |
|
316 |
– |
*acceptor = ctx->acceptor; |
317 |
– |
*acceptor_baton = ctx->acceptor_baton; |
318 |
– |
*handler = ctx->handler; |
319 |
– |
*handler_baton = ctx; |
320 |
– |
|
321 |
– |
return APR_SUCCESS; |
393 |
|
} |
394 |
|
|
395 |
|
static void print_usage(apr_pool_t *pool) |
399 |
|
puts("-v\tDisplay version"); |
400 |
|
puts("-H\tPrint response headers"); |
401 |
|
puts("-n <count> Fetch URL <count> times"); |
402 |
< |
puts("-a <user:password> Present Basic authentication credentials"); |
402 |
> |
puts("-x <count> Number of maximum outstanding requests inflight"); |
403 |
> |
puts("-U <user> Username for Basic/Digest authentication"); |
404 |
> |
puts("-P <password> Password for Basic/Digest authentication"); |
405 |
|
puts("-m <method> Use the <method> HTTP Method"); |
406 |
|
puts("-f <file> Use the <file> as the request body"); |
407 |
|
puts("-p <hostname:port> Use the <host:port> as proxy server"); |
408 |
+ |
puts("-r <header:value> Use <header:value> as request header"); |
409 |
|
} |
410 |
|
|
411 |
|
int main(int argc, const char **argv) |
412 |
|
{ |
413 |
|
apr_status_t status; |
414 |
|
apr_pool_t *pool; |
415 |
+ |
serf_bucket_alloc_t *bkt_alloc; |
416 |
|
serf_context_t *context; |
417 |
|
serf_connection_t *connection; |
418 |
|
serf_request_t *request; |
419 |
|
app_baton_t app_ctx; |
420 |
|
handler_baton_t handler_ctx; |
421 |
+ |
serf_bucket_t *req_hdrs = NULL; |
422 |
|
apr_uri_t url; |
423 |
|
const char *proxy = NULL; |
424 |
|
const char *raw_url, *method, *req_body_path = NULL; |
425 |
< |
int count; |
425 |
> |
int count, inflight; |
426 |
|
int i; |
427 |
|
int print_headers; |
428 |
< |
char *authn = NULL; |
428 |
> |
const char *username = NULL; |
429 |
> |
const char *password = ""; |
430 |
|
apr_getopt_t *opt; |
431 |
|
char opt_c; |
432 |
|
const char *opt_arg; |
436 |
|
|
437 |
|
apr_pool_create(&pool, NULL); |
438 |
|
/* serf_initialize(); */ |
439 |
+ |
bkt_alloc = serf_bucket_allocator_create(pool, NULL, NULL); |
440 |
|
|
441 |
< |
/* Default to one round of fetching. */ |
441 |
> |
/* Default to one round of fetching with no limit to max inflight reqs. */ |
442 |
|
count = 1; |
443 |
+ |
inflight = 0; |
444 |
|
/* Default to GET. */ |
445 |
|
method = "GET"; |
446 |
|
/* Do not print headers by default. */ |
448 |
|
|
449 |
|
apr_getopt_init(&opt, pool, argc, argv); |
450 |
|
|
451 |
< |
while ((status = apr_getopt(opt, "a:f:hHm:n:vp:", &opt_c, &opt_arg)) == |
451 |
> |
while ((status = apr_getopt(opt, "U:P:f:hHm:n:vp:x:r:", &opt_c, &opt_arg)) == |
452 |
|
APR_SUCCESS) { |
374 |
– |
int srclen, enclen; |
453 |
|
|
454 |
|
switch (opt_c) { |
455 |
< |
case 'a': |
456 |
< |
srclen = strlen(opt_arg); |
379 |
< |
enclen = apr_base64_encode_len(srclen); |
380 |
< |
authn = apr_palloc(pool, enclen + 6); |
381 |
< |
strcpy(authn, "Basic "); |
382 |
< |
(void) apr_base64_encode(&authn[6], opt_arg, srclen); |
455 |
> |
case 'U': |
456 |
> |
username = opt_arg; |
457 |
|
break; |
458 |
+ |
case 'P': |
459 |
+ |
password = opt_arg; |
460 |
+ |
break; |
461 |
|
case 'f': |
462 |
|
req_body_path = opt_arg; |
463 |
|
break; |
480 |
|
return errno; |
481 |
|
} |
482 |
|
break; |
483 |
+ |
case 'x': |
484 |
+ |
errno = 0; |
485 |
+ |
inflight = apr_strtoi64(opt_arg, NULL, 10); |
486 |
+ |
if (errno) { |
487 |
+ |
printf("Problem converting number of requests to have outstanding (%d)\n", |
488 |
+ |
errno); |
489 |
+ |
return errno; |
490 |
+ |
} |
491 |
+ |
break; |
492 |
|
case 'p': |
493 |
|
proxy = opt_arg; |
494 |
|
break; |
495 |
+ |
case 'r': |
496 |
+ |
{ |
497 |
+ |
char *sep; |
498 |
+ |
char *hdr_val; |
499 |
+ |
|
500 |
+ |
if (req_hdrs == NULL) { |
501 |
+ |
/* first request header, allocate bucket */ |
502 |
+ |
req_hdrs = serf_bucket_headers_create(bkt_alloc); |
503 |
+ |
} |
504 |
+ |
sep = strchr(opt_arg, ':'); |
505 |
+ |
if ((sep == NULL) || (sep == opt_arg) || (strlen(sep) <= 1)) { |
506 |
+ |
printf("Invalid request header string (%s)\n", opt_arg); |
507 |
+ |
return EINVAL; |
508 |
+ |
} |
509 |
+ |
hdr_val = sep + 1; |
510 |
+ |
while (*hdr_val == ' ') { |
511 |
+ |
hdr_val++; |
512 |
+ |
} |
513 |
+ |
serf_bucket_headers_setx(req_hdrs, opt_arg, (sep - opt_arg), 1, |
514 |
+ |
hdr_val, strlen(hdr_val), 1); |
515 |
+ |
} |
516 |
+ |
break; |
517 |
|
case 'v': |
518 |
|
puts("Serf version: " SERF_VERSION_STRING); |
519 |
|
exit(0); |
544 |
|
app_ctx.using_ssl = 0; |
545 |
|
} |
546 |
|
|
547 |
+ |
if (strcasecmp(method, "HEAD") == 0) { |
548 |
+ |
app_ctx.head_request = 1; |
549 |
+ |
} |
550 |
+ |
else { |
551 |
+ |
app_ctx.head_request = 0; |
552 |
+ |
} |
553 |
+ |
|
554 |
|
app_ctx.hostinfo = url.hostinfo; |
555 |
|
|
556 |
|
context = serf_context_create(pool); |
597 |
|
serf_config_proxy(context, proxy_address); |
598 |
|
} |
599 |
|
|
600 |
+ |
if (username) |
601 |
+ |
{ |
602 |
+ |
serf_config_authn_types(context, SERF_AUTHN_ALL); |
603 |
+ |
} |
604 |
+ |
else |
605 |
+ |
{ |
606 |
+ |
serf_config_authn_types(context, SERF_AUTHN_NTLM | SERF_AUTHN_NEGOTIATE); |
607 |
+ |
} |
608 |
+ |
|
609 |
+ |
serf_config_credentials_callback(context, credentials_callback); |
610 |
+ |
|
611 |
|
/* ### Connection or Context should have an allocator? */ |
612 |
< |
app_ctx.bkt_alloc = serf_bucket_allocator_create(pool, NULL, NULL); |
612 |
> |
app_ctx.bkt_alloc = bkt_alloc; |
613 |
|
app_ctx.ssl_ctx = NULL; |
614 |
|
|
615 |
|
status = serf_connection_create2(&connection, context, url, |
624 |
|
|
625 |
|
handler_ctx.completed_requests = 0; |
626 |
|
handler_ctx.print_headers = print_headers; |
627 |
+ |
apr_file_open_stdout(&handler_ctx.output_file, pool); |
628 |
|
|
629 |
|
handler_ctx.host = url.hostinfo; |
630 |
|
handler_ctx.method = method; |
631 |
< |
handler_ctx.path = url.path; |
632 |
< |
handler_ctx.authn = authn; |
631 |
> |
handler_ctx.path = apr_pstrcat(pool, |
632 |
> |
url.path, |
633 |
> |
url.query ? "?" : "", |
634 |
> |
url.query ? url.query : "", |
635 |
> |
NULL); |
636 |
> |
handler_ctx.username = username; |
637 |
> |
handler_ctx.password = password; |
638 |
> |
handler_ctx.auth_attempts = 0; |
639 |
|
|
640 |
|
handler_ctx.req_body_path = req_body_path; |
641 |
|
|
642 |
|
handler_ctx.acceptor = accept_response; |
643 |
|
handler_ctx.acceptor_baton = &app_ctx; |
644 |
|
handler_ctx.handler = handle_response; |
645 |
+ |
handler_ctx.req_hdrs = req_hdrs; |
646 |
|
|
647 |
+ |
serf_connection_set_max_outstanding_requests(connection, inflight); |
648 |
+ |
|
649 |
|
for (i = 0; i < count; i++) { |
650 |
|
request = serf_connection_request_create(connection, setup_request, |
651 |
|
&handler_ctx); |
657 |
|
continue; |
658 |
|
if (status) { |
659 |
|
char buf[200]; |
660 |
+ |
const char *err_string; |
661 |
+ |
err_string = serf_error_string(status); |
662 |
+ |
if (!err_string) { |
663 |
+ |
err_string = apr_strerror(status, buf, sizeof(buf)); |
664 |
+ |
} |
665 |
|
|
666 |
< |
printf("Error running context: (%d) %s\n", status, |
526 |
< |
apr_strerror(status, buf, sizeof(buf))); |
666 |
> |
printf("Error running context: (%d) %s\n", status, err_string); |
667 |
|
apr_pool_destroy(pool); |
668 |
|
exit(1); |
669 |
|
} |