1 /* http_ping - measure HTTP latency
2 **
3 ** Copyright � 1998,1999,2001,2002 by Jef Poskanzer <jef@mail.acme.com>.
4 ** All rights reserved.
5 **
6 ** Redistribution and use in source and binary forms, with or without
7 ** modification, are permitted provided that the following conditions
8 ** are met:
9 ** 1. Redistributions of source code must retain the above copyright
10 ** notice, this list of conditions and the following disclaimer.
11 ** 2. Redistributions in binary form must reproduce the above copyright
12 ** notice, this list of conditions and the following disclaimer in the
13 ** documentation and/or other materials provided with the distribution.
14 **
15 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 ** SUCH DAMAGE.
26 */
27
28 #include <unistd.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <sys/types.h>
34 #include <sys/time.h>
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <netdb.h>
38 #include <errno.h>
39 #include <signal.h>
40 #include <setjmp.h>
41
42 #ifdef USE_SSL
43 #include <openssl/ssl.h>
44 #include <openssl/rand.h>
45 #include <openssl/err.h>
46 #endif
47
48 #include "port.h"
49
50 #define INTERVAL 5
51 #define TIMEOUT 15
52
53 #define max(a,b) ((a)>=(b)?(a):(b))
54 #define min(a,b) ((a)<=(b)?(a):(b))
55
56 static char* url;
57 static int url_protocol;
58 static char url_host[5000];
59 static unsigned short url_port;
60 static char* url_filename;
61
62 /* Protocol symbols. */
63 #define PROTO_HTTP 0
64 #ifdef USE_SSL
65 #define PROTO_HTTPS 1
66 #endif
67
68 static unsigned short port;
69 static int conn_fd;
70 #ifdef USE_SSL
71 static SSL* ssl;
72 #endif
73 static int conn_state, conn_state;
74 static int got_response;
75 static struct timeval started_at, connect_at, response_at, finished_at;
76 static long content_length;
77 static long bytes;
78
79 #define ST_BOL 0
80 #define ST_TEXT 1
81 #define ST_LF 2
82 #define ST_CR 3
83 #define ST_CRLF 4
84 #define ST_CRLFCR 5
85 #define ST_C 6
86 #define ST_CO 7
87 #define ST_CON 8
88 #define ST_CONT 9
89 #define ST_CONTE 10
90 #define ST_CONTEN 11
91 #define ST_CONTENT 12
92 #define ST_CONTENT_ 13
93 #define ST_CONTENT_L 14
94 #define ST_CONTENT_LE 15
95 #define ST_CONTENT_LEN 16
96 #define ST_CONTENT_LENG 17
97 #define ST_CONTENT_LENGT 18
98 #define ST_CONTENT_LENGTH 19
99 #define ST_CONTENT_LENGTH_COLON 20
100 #define ST_CONTENT_LENGTH_COLON_WHITESPACE 21
101 #define ST_CONTENT_LENGTH_COLON_WHITESPACE_NUM 22
102 #define ST_DATA 23
103
104 static char* argv0;
105 static int count;
106 static int interval;
107 static int quiet;
108 static int do_proxy;
109 static char* proxy_host;
110 static unsigned short proxy_port;
111
112 static int terminate;
113 static int count_started, count_completed, count_failures, count_timeouts;
114 static long total_bytes;
115 static jmp_buf jb;
116
117 static float min_total, min_connect, min_response, min_data;
118 static float max_total, max_connect, max_response, max_data;
119 static float sum_total, sum_connect, sum_response, sum_data;
120
121 #ifdef USE_SSL
122 static SSL_CTX* ssl_ctx = (SSL_CTX*) 0;
123 #endif
124
125
126 /* Forwards. */
127 static void usage( void );
128 static void parse_url( void );
129 static void init_net( void );
130 static int start_connection( void );
131 static void lookup_address( char* hostname, unsigned short port );
132 static int open_client_socket( void );
133 static int handle_read( void );
134 static void handle_term( int sig );
135 static void handle_alarm( int sig );
136 static void close_connection( void );
137 static long long delta_timeval( struct timeval* start, struct timeval* finish );
138
139
140 int
main(int argc,char ** argv)141 main( int argc, char** argv )
142 {
143 int argn;
144 float elapsed_total, elapsed_connect, elapsed_response, elapsed_data;
145
146 /* Parse args. */
147 argv0 = argv[0];
148 argn = 1;
149 count = -1;
150 interval = INTERVAL;
151 quiet = 0;
152 do_proxy = 0;
153 while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
154 {
155 if ( strncmp( argv[argn], "-count", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
156 {
157 count = atoi( argv[++argn] );
158 if ( count <= 0 )
159 {
160 (void) fprintf( stderr, "%s: count must be positive\n", argv0 );
161 exit( 1 );
162 }
163 }
164 else if ( strncmp( argv[argn], "-interval", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
165 {
166 interval = atoi( argv[++argn] );
167 if ( interval < 1 )
168 {
169 (void) fprintf( stderr, "%s: interval must be at least one\n", argv0 );
170 exit( 1 );
171 }
172 }
173 else if ( strncmp( argv[argn], "-quiet", strlen( argv[argn] ) ) == 0 )
174 {
175 quiet = 1;
176 }
177 else if ( strncmp( argv[argn], "-proxy", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
178 {
179 char* colon;
180 do_proxy = 1;
181 proxy_host = argv[++argn];
182 colon = strchr( proxy_host, ':' );
183 if ( colon == (char*) 0 )
184 proxy_port = 80;
185 else
186 {
187 proxy_port = (unsigned short) atoi( colon + 1 );
188 *colon = '\0';
189 }
190 }
191 else
192 usage();
193 ++argn;
194 }
195 if ( argn + 1 != argc )
196 usage();
197 url = argv[argn];
198
199 /* Parse the URL. */
200 parse_url();
201
202 /* Initialize the network stuff. */
203 init_net();
204
205 /* Initialize the statistics. */
206 count_started = count_completed = count_failures = count_timeouts = 0;
207 total_bytes = 0;
208 min_total = min_connect = min_response = min_data = 1000000000.0;
209 max_total = max_connect = max_response = max_data = -1000000000.0;
210 sum_total = sum_connect = sum_response = sum_data = 0.0;
211
212 /* Initialize the random number generator. */
213 #ifdef HAVE_SRANDOMDEV
214 srandomdev();
215 #else
216 srandom( (int) time( (time_t*) 0 ) ^ getpid() );
217 #endif
218
219 /* Initialize the rest. */
220 #ifdef HAVE_SIGSET
221 (void) sigset( SIGTERM, handle_term );
222 (void) sigset( SIGINT, handle_term );
223 (void) sigset( SIGPIPE, SIG_IGN );
224 (void) sigset( SIGALRM, handle_alarm );
225 #else /* HAVE_SIGSET */
226 (void) signal( SIGTERM, handle_term );
227 (void) signal( SIGINT, handle_term );
228 (void) signal( SIGPIPE, SIG_IGN );
229 (void) signal( SIGALRM, handle_alarm );
230 #endif /* HAVE_SIGSET */
231
232 /* Main loop. */
233 terminate = 0;
234 for (;;)
235 {
236 (void) setjmp( jb );
237 if ( count == 0 || terminate )
238 break;
239 if ( count > 0 )
240 --count;
241 ++count_started;
242 alarm( TIMEOUT );
243 if ( ! start_connection() )
244 ++count_failures;
245 else
246 {
247 if ( ! handle_read() )
248 ++count_failures;
249 else
250 {
251 ++count_completed;
252 elapsed_total =
253 delta_timeval( &started_at, &finished_at ) / 1000.0;
254 elapsed_connect =
255 delta_timeval( &started_at, &connect_at ) / 1000.0;
256 elapsed_response =
257 delta_timeval( &connect_at, &response_at ) / 1000.0;
258 elapsed_data =
259 delta_timeval( &response_at, &finished_at ) / 1000.0;
260 if ( ! quiet )
261 (void) printf(
262 "%ld bytes from %s: %g ms (%gc/%gr/%gd)\n",
263 bytes, url, elapsed_total, elapsed_connect,
264 elapsed_response, elapsed_data );
265 min_total = min( min_total, elapsed_total );
266 min_connect = min( min_connect, elapsed_connect );
267 min_response = min( min_response, elapsed_response );
268 min_data = min( min_data, elapsed_data );
269 max_total = max( max_total, elapsed_total );
270 max_connect = max( max_connect, elapsed_connect );
271 max_response = max( max_response, elapsed_response );
272 max_data = max( max_data, elapsed_data );
273 sum_total += elapsed_total;
274 sum_connect += elapsed_connect;
275 sum_response += elapsed_response;
276 sum_data += elapsed_data;
277 }
278 }
279 alarm( 0 );
280 if ( count == 0 || terminate )
281 break;
282 sleep( interval );
283 }
284
285 /* Report statistics. */
286 (void) printf( "\n" );
287 (void) printf( "--- %s http_ping statistics ---\n", url );
288 (void) printf(
289 "%d fetches started, %d completed (%d%%), %d failures (%d%%), %d timeouts (%d%%)\n",
290 count_started, count_completed, count_completed * 100 / count_started,
291 count_failures, count_failures * 100 / count_started,
292 count_timeouts, count_timeouts * 100 / count_started );
293 if ( count_completed > 0 )
294 {
295 (void) printf(
296 "total min/avg/max = %g/%g/%g ms\n",
297 min_total, sum_total / count_completed, max_total );
298 (void) printf(
299 "connect min/avg/max = %g/%g/%g ms\n",
300 min_connect, sum_connect / count_completed, max_connect );
301 (void) printf(
302 "response min/avg/max = %g/%g/%g ms\n",
303 min_response, sum_response / count_completed, max_response );
304 (void) printf(
305 "data min/avg/max = %g/%g/%g ms\n",
306 min_data, sum_data / count_completed, max_data );
307 }
308
309 /* Done. */
310 #ifdef USE_SSL
311 if ( ssl_ctx != (SSL_CTX*) 0 )
312 SSL_CTX_free( ssl_ctx );
313 #endif
314 exit( 0 );
315 }
316
317
318 static void
usage(void)319 usage( void )
320 {
321 (void) fprintf( stderr, "usage: %s [-count n] [-interval n] [-quiet] [-proxy host:port] url\n", argv0 );
322 exit( 1 );
323 }
324
325
326 static void
parse_url(void)327 parse_url( void )
328 {
329 char* http = "http://";
330 int http_len = strlen( http );
331 #ifdef USE_SSL
332 char* https = "https://";
333 int https_len = strlen( https );
334 #endif
335 int proto_len, host_len;
336 char* cp;
337
338 if ( strncmp( http, url, http_len ) == 0 )
339 {
340 proto_len = http_len;
341 url_protocol = PROTO_HTTP;
342 }
343 #ifdef USE_SSL
344 else if ( strncmp( https, url, https_len ) == 0 )
345 {
346 proto_len = https_len;
347 url_protocol = PROTO_HTTPS;
348 }
349 #endif
350 else
351 {
352 (void) fprintf( stderr, "%s: unknown protocol - %s\n", argv0, url );
353 exit( 1 );
354 }
355 for ( cp = url + proto_len;
356 *cp != '\0' && *cp != ':' && *cp != '/'; ++cp )
357 ;
358 host_len = cp - url;
359 host_len -= proto_len;
360 host_len = min( host_len, sizeof(url_host) - 1 );
361 strncpy( url_host, url + proto_len, host_len );
362 url_host[host_len] = '\0';
363 if ( *cp == ':' )
364 {
365 url_port = (unsigned short) atoi( ++cp );
366 while ( *cp != '\0' && *cp != '/' )
367 ++cp;
368 }
369 else
370 #ifdef USE_SSL
371 if ( url_protocol == PROTO_HTTPS )
372 url_port = 443;
373 else
374 url_port = 80;
375 #else
376 url_port = 80;
377 #endif
378 if ( *cp == '\0' )
379 url_filename = "/";
380 else
381 url_filename = cp;
382 }
383
384
385 static void
init_net(void)386 init_net( void )
387 {
388 char* host;
389
390 if ( do_proxy )
391 {
392 host = proxy_host;
393 port = proxy_port;
394 }
395 else
396 {
397 host = url_host;
398 port = url_port;
399 }
400 lookup_address( host, port );
401 }
402
403
404 static int
start_connection(void)405 start_connection( void )
406 {
407 char buf[600];
408 int b, r;
409
410 (void) gettimeofday( &started_at, (struct timezone*) 0 );
411 got_response = 0;
412 content_length = -1;
413 bytes = 0;
414
415 conn_fd = open_client_socket();
416 if ( conn_fd < 0 )
417 return 0;
418
419 #ifdef USE_SSL
420 if ( url_protocol == PROTO_HTTPS )
421 {
422 /* Complete the SSL connection. */
423 if ( ssl_ctx == (SSL_CTX*) 0 )
424 {
425 SSL_load_error_strings();
426 SSLeay_add_ssl_algorithms();
427 ssl_ctx = SSL_CTX_new( SSLv23_client_method() );
428 }
429 if ( ! RAND_status() )
430 {
431 unsigned char rb[1024];
432 int i;
433 for ( i = 0; i < sizeof(rb); ++i )
434 rb[i] = random() % 0xff;
435 RAND_seed( rb, sizeof(rb) );
436 }
437 ssl = SSL_new( ssl_ctx );
438 SSL_set_fd( ssl, conn_fd );
439 r = SSL_connect( ssl );
440 if ( r <= 0 )
441 {
442 (void) fprintf(
443 stderr, "%s: SSL connection failed - %d\n", argv0, r );
444 ERR_print_errors_fp( stderr );
445 close_connection();
446 return 0;
447 }
448 }
449 #endif
450 (void) gettimeofday( &connect_at, (struct timezone*) 0 );
451
452 /* Format the request. */
453 if ( do_proxy )
454 {
455 #ifdef USE_SSL
456 b = snprintf(
457 buf, sizeof(buf), "GET %s://%.500s:%d%.500s HTTP/1.0\r\n",
458 url_protocol == PROTO_HTTPS ? "https" : "http", url_host,
459 (int) url_port, url_filename );
460 #else
461 b = snprintf(
462 buf, sizeof(buf), "GET http://%.500s:%d%.500s HTTP/1.0\r\n",
463 url_host, (int) url_port, url_filename );
464 #endif
465 }
466 else
467 b = snprintf(
468 buf, sizeof(buf), "GET %.500s HTTP/1.0\r\n", url_filename );
469 b += snprintf( &buf[b], sizeof(buf) - b, "Host: %s\r\n", url_host );
470 b += snprintf( &buf[b], sizeof(buf) - b, "User-Agent: http_ping\r\n" );
471 b += snprintf( &buf[b], sizeof(buf) - b, "\r\n" );
472
473 /* Send the request. */
474 #ifdef USE_SSL
475 if ( url_protocol == PROTO_HTTPS )
476 r = SSL_write( ssl, buf, b );
477 else
478 r = write( conn_fd, buf, b );
479 #else
480 r = write( conn_fd, buf, b );
481 #endif
482 if ( r < 0 )
483 {
484 perror( "write" );
485 close_connection();
486 return 0;
487 }
488 conn_state = ST_BOL;
489 return 1;
490 }
491
492
493 #if defined(AF_INET6) && defined(IN6_IS_ADDR_V4MAPPED)
494 #define USE_IPV6
495 #endif
496
497 #ifdef USE_IPV6
498 static struct sockaddr_in6 sa;
499 #else /* USE_IPV6 */
500 static struct sockaddr_in sa;
501 #endif /* USE_IPV6 */
502 static int sa_len, sock_family, sock_type, sock_protocol;
503
504
505 static void
lookup_address(char * hostname,unsigned short port)506 lookup_address( char* hostname, unsigned short port )
507 {
508 #ifdef USE_IPV6
509 struct addrinfo hints;
510 char portstr[10];
511 int gaierr;
512 struct addrinfo* ai;
513 struct addrinfo* ai2;
514 struct addrinfo* aiv4;
515 struct addrinfo* aiv6;
516 #else /* USE_IPV6 */
517 struct hostent *he;
518 #endif /* USE_IPV6 */
519
520 (void) memset( (void*) &sa, 0, sizeof(sa) );
521
522 #ifdef USE_IPV6
523
524 (void) memset( &hints, 0, sizeof(hints) );
525 hints.ai_family = PF_UNSPEC;
526 hints.ai_socktype = SOCK_STREAM;
527 (void) snprintf( portstr, sizeof(portstr), "%d", (int) port );
528 if ( (gaierr = getaddrinfo( hostname, portstr, &hints, &ai )) != 0 )
529 {
530 (void) fprintf(
531 stderr, "%s: getaddrinfo %s - %s\n", argv0, hostname,
532 gai_strerror( gaierr ) );
533 exit( 1 );
534 }
535
536 /* Find the first IPv4 and IPv6 entries. */
537 aiv4 = (struct addrinfo*) 0;
538 aiv6 = (struct addrinfo*) 0;
539 for ( ai2 = ai; ai2 != (struct addrinfo*) 0; ai2 = ai2->ai_next )
540 {
541 switch ( ai2->ai_family )
542 {
543 case AF_INET:
544 if ( aiv4 == (struct addrinfo*) 0 )
545 aiv4 = ai2;
546 break;
547 case AF_INET6:
548 if ( aiv6 == (struct addrinfo*) 0 )
549 aiv6 = ai2;
550 break;
551 }
552 }
553
554 /* If there's an IPv4 address, use that, otherwise try IPv6. */
555 if ( aiv4 != (struct addrinfo*) 0 )
556 {
557 if ( sizeof(sa) < aiv4->ai_addrlen )
558 {
559 (void) fprintf(
560 stderr, "%s - sockaddr too small (%lu < %lu)\n",
561 hostname, (unsigned long) sizeof(sa),
562 (unsigned long) aiv4->ai_addrlen );
563 exit( 1 );
564 }
565 sock_family = aiv4->ai_family;
566 sock_type = aiv4->ai_socktype;
567 sock_protocol = aiv4->ai_protocol;
568 sa_len = aiv4->ai_addrlen;
569 (void) memmove( &sa, aiv4->ai_addr, sa_len );
570 freeaddrinfo( ai );
571 return;
572 }
573 if ( aiv6 != (struct addrinfo*) 0 )
574 {
575 if ( sizeof(sa) < aiv6->ai_addrlen )
576 {
577 (void) fprintf(
578 stderr, "%s - sockaddr too small (%lu < %lu)\n",
579 hostname, (unsigned long) sizeof(sa),
580 (unsigned long) aiv6->ai_addrlen );
581 exit( 1 );
582 }
583 sock_family = aiv6->ai_family;
584 sock_type = aiv6->ai_socktype;
585 sock_protocol = aiv6->ai_protocol;
586 sa_len = aiv6->ai_addrlen;
587 (void) memmove( &sa, aiv6->ai_addr, sa_len );
588 freeaddrinfo( ai );
589 return;
590 }
591
592 (void) fprintf(
593 stderr, "%s: no valid address found for host %s\n", argv0, hostname );
594 exit( 1 );
595
596 #else /* USE_IPV6 */
597
598 he = gethostbyname( hostname );
599 if ( he == (struct hostent*) 0 )
600 {
601 (void) fprintf( stderr, "%s: unknown host - %s\n", argv0, hostname );
602 exit( 1 );
603 }
604 sock_family = sa.sin_family = he->h_addrtype;
605 sock_type = SOCK_STREAM;
606 sock_protocol = 0;
607 sa_len = sizeof(sa);
608 (void) memmove( &sa.sin_addr, he->h_addr, he->h_length );
609 sa.sin_port = htons( port );
610
611 #endif /* USE_IPV6 */
612
613 }
614
615
616 static int
open_client_socket(void)617 open_client_socket( void )
618 {
619 int sockfd;
620
621 sockfd = socket( sock_family, sock_type, sock_protocol );
622 if ( sockfd < 0 )
623 {
624 perror( "socket" );
625 return -1;
626 }
627
628 if ( connect( sockfd, (struct sockaddr*) &sa, sa_len ) < 0 )
629 {
630 perror( "connect" );
631 (void) close( sockfd );
632 return -1;
633 }
634
635 return sockfd;
636 }
637
638
639 static int
handle_read(void)640 handle_read( void )
641 {
642 char buf[5000];
643 int bytes_to_read, bytes_read, bytes_handled;
644
645 for (;;)
646 {
647 bytes_to_read = sizeof(buf);
648 #ifdef USE_SSL
649 if ( url_protocol == PROTO_HTTPS )
650 bytes_read = SSL_read( ssl, buf, bytes_to_read );
651 else
652 bytes_read = read( conn_fd, buf, bytes_to_read );
653 #else
654 bytes_read = read( conn_fd, buf, bytes_to_read );
655 #endif
656 if ( bytes_read < 0 )
657 {
658 perror( "read" );
659 close_connection();
660 return 0;
661 }
662 if ( ! got_response )
663 {
664 got_response = 1;
665 (void) gettimeofday( &response_at, (struct timezone*) 0 );
666 }
667 if ( bytes_read == 0 )
668 {
669 close_connection();
670 (void) gettimeofday( &finished_at, (struct timezone*) 0 );
671 return 1;
672 }
673
674 for ( bytes_handled = 0; bytes_handled < bytes_read; ++bytes_handled )
675 {
676 switch ( conn_state )
677 {
678 case ST_BOL:
679 switch ( buf[bytes_handled] )
680 {
681 case '\n':
682 conn_state = ST_LF;
683 break;
684 case '\r':
685 conn_state = ST_CR;
686 break;
687 case 'C': case 'c':
688 conn_state = ST_C;
689 break;
690 default:
691 conn_state = ST_TEXT;
692 break;
693 }
694 break;
695
696 case ST_TEXT:
697 switch ( buf[bytes_handled] )
698 {
699 case '\n':
700 conn_state = ST_LF;
701 break;
702 case '\r':
703 conn_state = ST_CR;
704 break;
705 default:
706 break;
707 }
708 break;
709
710 case ST_LF:
711 switch ( buf[bytes_handled] )
712 {
713 case '\n':
714 conn_state = ST_DATA;
715 break;
716 case '\r':
717 conn_state = ST_CR;
718 break;
719 case 'C': case 'c':
720 conn_state = ST_C;
721 break;
722 default:
723 conn_state = ST_TEXT;
724 break;
725 }
726 break;
727
728 case ST_CR:
729 switch ( buf[bytes_handled] )
730 {
731 case '\n':
732 conn_state = ST_CRLF;
733 break;
734 case '\r':
735 conn_state = ST_DATA;
736 break;
737 case 'C': case 'c':
738 conn_state = ST_C;
739 break;
740 default:
741 conn_state = ST_TEXT;
742 break;
743 }
744 break;
745
746 case ST_CRLF:
747 switch ( buf[bytes_handled] )
748 {
749 case '\n':
750 conn_state = ST_DATA;
751 break;
752 case '\r':
753 conn_state = ST_CRLFCR;
754 break;
755 case 'C': case 'c':
756 conn_state = ST_C;
757 break;
758 default:
759 conn_state = ST_TEXT;
760 break;
761 }
762 break;
763
764 case ST_CRLFCR:
765 switch ( buf[bytes_handled] )
766 {
767 case '\n': case '\r':
768 conn_state = ST_DATA;
769 break;
770 case 'C': case 'c':
771 conn_state = ST_C;
772 break;
773 default:
774 conn_state = ST_TEXT;
775 break;
776 }
777 break;
778
779 case ST_C:
780 switch ( buf[bytes_handled] )
781 {
782 case 'O': case 'o':
783 conn_state = ST_CO;
784 break;
785 case '\n':
786 conn_state = ST_LF;
787 break;
788 case '\r':
789 conn_state = ST_CR;
790 break;
791 default:
792 conn_state = ST_TEXT;
793 break;
794 }
795 break;
796
797 case ST_CO:
798 switch ( buf[bytes_handled] )
799 {
800 case 'N': case 'n':
801 conn_state = ST_CON;
802 break;
803 case '\n':
804 conn_state = ST_LF;
805 break;
806 case '\r':
807 conn_state = ST_CR;
808 break;
809 default:
810 conn_state = ST_TEXT;
811 break;
812 }
813 break;
814
815 case ST_CON:
816 switch ( buf[bytes_handled] )
817 {
818 case 'T': case 't':
819 conn_state = ST_CONT;
820 break;
821 case '\n':
822 conn_state = ST_LF;
823 break;
824 case '\r':
825 conn_state = ST_CR;
826 break;
827 default:
828 conn_state = ST_TEXT;
829 break;
830 }
831 break;
832
833 case ST_CONT:
834 switch ( buf[bytes_handled] )
835 {
836 case 'E': case 'e':
837 conn_state = ST_CONTE;
838 break;
839 case '\n':
840 conn_state = ST_LF;
841 break;
842 case '\r':
843 conn_state = ST_CR;
844 break;
845 default:
846 conn_state = ST_TEXT;
847 break;
848 }
849 break;
850
851 case ST_CONTE:
852 switch ( buf[bytes_handled] )
853 {
854 case 'N': case 'n':
855 conn_state = ST_CONTEN;
856 break;
857 case '\n':
858 conn_state = ST_LF;
859 break;
860 case '\r':
861 conn_state = ST_CR;
862 break;
863 default:
864 conn_state = ST_TEXT;
865 break;
866 }
867 break;
868
869 case ST_CONTEN:
870 switch ( buf[bytes_handled] )
871 {
872 case 'T': case 't':
873 conn_state = ST_CONTENT;
874 break;
875 case '\n':
876 conn_state = ST_LF;
877 break;
878 case '\r':
879 conn_state = ST_CR;
880 break;
881 default:
882 conn_state = ST_TEXT;
883 break;
884 }
885 break;
886
887 case ST_CONTENT:
888 switch ( buf[bytes_handled] )
889 {
890 case '-':
891 conn_state = ST_CONTENT_;
892 break;
893 case '\n':
894 conn_state = ST_LF;
895 break;
896 case '\r':
897 conn_state = ST_CR;
898 break;
899 default:
900 conn_state = ST_TEXT;
901 break;
902 }
903 break;
904
905 case ST_CONTENT_:
906 switch ( buf[bytes_handled] )
907 {
908 case 'L': case 'l':
909 conn_state = ST_CONTENT_L;
910 break;
911 case '\n':
912 conn_state = ST_LF;
913 break;
914 case '\r':
915 conn_state = ST_CR;
916 break;
917 default:
918 conn_state = ST_TEXT;
919 break;
920 }
921 break;
922
923 case ST_CONTENT_L:
924 switch ( buf[bytes_handled] )
925 {
926 case 'E': case 'e':
927 conn_state = ST_CONTENT_LE;
928 break;
929 case '\n':
930 conn_state = ST_LF;
931 break;
932 case '\r':
933 conn_state = ST_CR;
934 break;
935 default:
936 conn_state = ST_TEXT;
937 break;
938 }
939 break;
940
941 case ST_CONTENT_LE:
942 switch ( buf[bytes_handled] )
943 {
944 case 'N': case 'n':
945 conn_state = ST_CONTENT_LEN;
946 break;
947 case '\n':
948 conn_state = ST_LF;
949 break;
950 case '\r':
951 conn_state = ST_CR;
952 break;
953 default:
954 conn_state = ST_TEXT;
955 break;
956 }
957 break;
958
959 case ST_CONTENT_LEN:
960 switch ( buf[bytes_handled] )
961 {
962 case 'G': case 'g':
963 conn_state = ST_CONTENT_LENG;
964 break;
965 case '\n':
966 conn_state = ST_LF;
967 break;
968 case '\r':
969 conn_state = ST_CR;
970 break;
971 default:
972 conn_state = ST_TEXT;
973 break;
974 }
975 break;
976
977 case ST_CONTENT_LENG:
978 switch ( buf[bytes_handled] )
979 {
980 case 'T': case 't':
981 conn_state = ST_CONTENT_LENGT;
982 break;
983 case '\n':
984 conn_state = ST_LF;
985 break;
986 case '\r':
987 conn_state = ST_CR;
988 break;
989 default:
990 conn_state = ST_TEXT;
991 break;
992 }
993 break;
994
995 case ST_CONTENT_LENGT:
996 switch ( buf[bytes_handled] )
997 {
998 case 'H': case 'h':
999 conn_state = ST_CONTENT_LENGTH;
1000 break;
1001 case '\n':
1002 conn_state = ST_LF;
1003 break;
1004 case '\r':
1005 conn_state = ST_CR;
1006 break;
1007 default:
1008 conn_state = ST_TEXT;
1009 break;
1010 }
1011 break;
1012
1013 case ST_CONTENT_LENGTH:
1014 switch ( buf[bytes_handled] )
1015 {
1016 case ':':
1017 conn_state = ST_CONTENT_LENGTH_COLON;
1018 break;
1019 case '\n':
1020 conn_state = ST_LF;
1021 break;
1022 case '\r':
1023 conn_state = ST_CR;
1024 break;
1025 default:
1026 conn_state = ST_TEXT;
1027 break;
1028 }
1029 break;
1030
1031 case ST_CONTENT_LENGTH_COLON:
1032 switch ( buf[bytes_handled] )
1033 {
1034 case ' ': case '\t':
1035 conn_state = ST_CONTENT_LENGTH_COLON_WHITESPACE;
1036 break;
1037 case '\n':
1038 conn_state = ST_LF;
1039 break;
1040 case '\r':
1041 conn_state = ST_CR;
1042 break;
1043 default:
1044 conn_state = ST_TEXT;
1045 break;
1046 }
1047 break;
1048
1049 case ST_CONTENT_LENGTH_COLON_WHITESPACE:
1050 switch ( buf[bytes_handled] )
1051 {
1052 case ' ': case '\t':
1053 break;
1054 case '0': case '1': case '2': case '3': case '4':
1055 case '5': case '6': case '7': case '8': case '9':
1056 content_length = buf[bytes_handled] - '0';
1057 conn_state = ST_CONTENT_LENGTH_COLON_WHITESPACE_NUM;
1058 break;
1059 case '\n':
1060 conn_state = ST_LF;
1061 break;
1062 case '\r':
1063 conn_state = ST_CR;
1064 break;
1065 default:
1066 conn_state = ST_TEXT;
1067 break;
1068 }
1069 break;
1070
1071 case ST_CONTENT_LENGTH_COLON_WHITESPACE_NUM:
1072 switch ( buf[bytes_handled] )
1073 {
1074 case '0': case '1': case '2': case '3': case '4':
1075 case '5': case '6': case '7': case '8': case '9':
1076 content_length =
1077 content_length * 10 + buf[bytes_handled] - '0';
1078 break;
1079 case '\n':
1080 conn_state = ST_LF;
1081 break;
1082 case '\r':
1083 conn_state = ST_CR;
1084 break;
1085 default:
1086 conn_state = ST_TEXT;
1087 break;
1088 }
1089 break;
1090
1091 case ST_DATA:
1092 bytes += bytes_read - bytes_handled;
1093 total_bytes += bytes_read - bytes_handled;
1094 bytes_handled = bytes_read;
1095 if ( content_length != -1 && bytes >= content_length )
1096 {
1097 close_connection();
1098 (void) gettimeofday( &finished_at, (struct timezone*) 0 );
1099 return 1;
1100 }
1101 break;
1102 }
1103 }
1104 }
1105 }
1106
1107
1108 static void
handle_term(int sig)1109 handle_term( int sig )
1110 {
1111 terminate = 1;
1112 }
1113
1114
1115 static void
handle_alarm(int sig)1116 handle_alarm( int sig )
1117 {
1118 close_connection();
1119 (void) fprintf( stderr, "%s: timed out\n", url );
1120 ++count_timeouts;
1121 longjmp( jb, 0 );
1122 }
1123
1124
1125 static void
close_connection(void)1126 close_connection( void )
1127 {
1128 #ifdef USE_SSL
1129 if ( url_protocol == PROTO_HTTPS )
1130 SSL_free( ssl );
1131 #endif
1132 (void) close( conn_fd );
1133 }
1134
1135
1136 static long long
delta_timeval(struct timeval * start,struct timeval * finish)1137 delta_timeval( struct timeval* start, struct timeval* finish )
1138 {
1139 long long delta_secs = finish->tv_sec - start->tv_sec;
1140 long long delta_usecs = finish->tv_usec - start->tv_usec;
1141 return delta_secs * (long long) 1000000L + delta_usecs;
1142 }
1143