<pre name="code" class="cpp">/* http_load - multiprocessing http test client
**
** Copyright © 1998,1999,2001 by Jef Poskanzer <jef@mail.acme.com>.
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE.
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <signal.h>
#ifdef USE_SSL
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#endif
#include "version.h"
#include "port.h"
#include "timers.h"
#if defined(AF_INET6) && defined(IN6_IS_ADDR_V4MAPPED)
#define USE_IPV6
#endif
#define max(a,b) ((a)>=(b)?(a):(b))
#define min(a,b) ((a)<=(b)?(a):(b))
/* How long a connection can stay idle before we give up on it. */
#define IDLE_SECS 60
/* Default max bytes/second in throttle mode. */
#define THROTTLE 3360
/* How often to show progress reports. */
#define PROGRESS_SECS 60
/* How many file descriptors to not use. */
#define RESERVED_FDS 3
typedef struct {
char* url_str;
int protocol;
char* hostname;
unsigned short port;
#ifdef USE_IPV6
struct sockaddr_in6 sa;
#else /* USE_IPV6 */
struct sockaddr_in sa;
#endif /* USE_IPV6 */
int sa_len, sock_family, sock_type, sock_protocol;
char* filename;
int got_bytes;
long bytes;
int got_checksum;
long checksum;
} url;
static url* urls;
static int num_urls, max_urls;
typedef struct {
char* str;
struct sockaddr_in sa;
} sip;
static sip* sips;
static int num_sips, max_sips;
/* Protocol symbols. */
#define PROTO_HTTP 0
#ifdef USE_SSL
#define PROTO_HTTPS 1
#endif
typedef struct {
int url_num;
struct sockaddr_in sa;
int sa_len;
int conn_fd;
#ifdef USE_SSL
SSL* ssl;
#endif
int conn_state, header_state;
int did_connect, did_response;
struct timeval started_at;
struct timeval connect_at;
struct timeval request_at;
struct timeval response_at;
Timer* idle_timer;
Timer* wakeup_timer;
long content_length;
long bytes;
long checksum;
int http_status;
} connection;
static connection* connections;
static int max_connections, num_connections, max_parallel;
static int http_status_counts[1000]; /* room for all three-digit statuses */
#define CNST_FREE 0
#define CNST_CONNECTING 1
#define CNST_HEADERS 2
#define CNST_READING 3
#define CNST_PAUSING 4
#define HDST_LINE1_PROTOCOL 0
#define HDST_LINE1_WHITESPACE 1
#define HDST_LINE1_STATUS 2
#define HDST_BOL 10
#define HDST_TEXT 11
#define HDST_LF 12
#define HDST_CR 13
#define HDST_CRLF 14
#define HDST_CRLFCR 15
#define HDST_C 20
#define HDST_CO 21
#define HDST_CON 22
#define HDST_CONT 23
#define HDST_CONTE 24
#define HDST_CONTEN 25
#define HDST_CONTENT 26
#define HDST_CONTENT_ 27
#define HDST_CONTENT_L 28
#define HDST_CONTENT_LE 29
#define HDST_CONTENT_LEN 30
#define HDST_CONTENT_LENG 31
#define HDST_CONTENT_LENGT 32
#define HDST_CONTENT_LENGTH 33
#define HDST_CONTENT_LENGTH_COLON 34
#define HDST_CONTENT_LENGTH_COLON_WHITESPACE 35
#define HDST_CONTENT_LENGTH_COLON_WHITESPACE_NUM 36
static char* argv0;
static int do_checksum, do_throttle, do_verbose, do_jitter, do_proxy;
static float throttle;
static int idle_secs;
static char* proxy_hostname;
static unsigned short proxy_port;
static struct timeval start_at;
static int fetches_started, connects_completed, responses_completed, fetches_completed;
static long long total_bytes;
static long long total_connect_usecs, max_connect_usecs, min_connect_usecs;
static long long total_response_usecs, max_response_usecs, min_response_usecs;
int total_timeouts, total_badbytes, total_badchecksums;
static long start_interval, low_interval, high_interval, range_interval;
#ifdef USE_SSL
static SSL_CTX* ssl_ctx = (SSL_CTX*) 0;
static char* cipher = (char*) 0;
#endif
/* Forwards. */
static void usage( void );
static void read_url_file( char* url_file );
static void lookup_address( int url_num );
static void read_sip_file( char* sip_file );
static void start_connection( struct timeval* nowP );
static void start_socket( int url_num, int cnum, struct timeval* nowP );
static void handle_connect( int cnum, struct timeval* nowP, int double_check );
static void handle_read( int cnum, struct timeval* nowP );
static void idle_connection( ClientData client_data, struct timeval* nowP );
static void wakeup_connection( ClientData client_data, struct timeval* nowP );
static void close_connection( int cnum );
static void progress_report( ClientData client_data, struct timeval* nowP );
static void start_timer( ClientData client_data, struct timeval* nowP );
static void end_timer( ClientData client_data, struct timeval* nowP );
static void finish( struct timeval* nowP );
static long long delta_timeval( struct timeval* start, struct timeval* finish );
static void* malloc_check( size_t size );
static void* realloc_check( void* ptr, size_t size );
static char* strdup_check( char* str );
static void check( void* ptr );
int
main( int argc, char** argv )
{
int argn;
int start;
#define START_NONE 0
#define START_PARALLEL 1
#define START_RATE 2
int start_parallel = -1, start_rate = -1;
int end;
#define END_NONE 0
#define END_FETCHES 1
#define END_SECONDS 2
int end_fetches = -1, end_seconds = -1;
int cnum;
char* url_file;
char* sip_file;
#ifdef RLIMIT_NOFILE
struct rlimit limits;
#endif /* RLIMIT_NOFILE */
fd_set rfdset;
fd_set wfdset;
struct timeval now;
int i, r;
max_connections = 64 - RESERVED_FDS; /* a guess */
#ifdef RLIMIT_NOFILE
/* Try and increase the limit on # of files to the maximum. */
if ( getrlimit( RLIMIT_NOFILE, &limits ) == 0 )
{
if ( limits.rlim_cur != limits.rlim_max )
{
if ( limits.rlim_max == RLIM_INFINITY )
limits.rlim_cur = 8192; /* arbitrary */
else if ( limits.rlim_max > limits.rlim_cur )
limits.rlim_cur = limits.rlim_max;
(void) setrlimit( RLIMIT_NOFILE, &limits );
}
max_connections = limits.rlim_cur - RESERVED_FDS;
}
#endif /* RLIMIT_NOFILE */
/* Parse args. */
argv0 = argv[0];
argn = 1;
do_checksum = do_throttle = do_verbose = do_jitter = do_proxy = 0;
throttle = THROTTLE;
sip_file = (char*) 0;
idle_secs = IDLE_SECS;
start = START_NONE;
end = END_NONE;
while ( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' )
{
if ( strncmp( argv[argn], "-checksum", strlen( argv[argn] ) ) == 0 )
do_checksum = 1;
else if ( strncmp( argv[argn], "-throttle", strlen( argv[argn] ) ) == 0 )
do_throttle = 1;
else if ( strncmp( argv[argn], "-Throttle", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
{
do_throttle = 1;
throttle = atoi( argv[++argn] ) / 10.0;
}
else if ( strncmp( argv[argn], "-verbose", strlen( argv[argn] ) ) == 0 )
do_verbose = 1;
else if ( strncmp( argv[argn], "-timeout", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
idle_secs = atoi( argv[++argn] );
else if ( strncmp( argv[argn], "-jitter", strlen( argv[argn] ) ) == 0 )
do_jitter = 1;
else if ( strncmp( argv[argn], "-parallel", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
{
start = START_PARALLEL;
start_parallel = atoi( argv[++argn] );
if ( start_parallel < 1 )
{
(void) fprintf(
stderr, "%s: parallel must be at least 1\n", argv0 );
exit( 1 );
}
if ( start_parallel > max_connections )
{
(void) fprintf(
stderr, "%s: parallel may be at most %d\n", argv0, max_connections );
exit( 1 );
}
}
else if ( strncmp( argv[argn], "-rate", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
{
start = START_RATE;
start_rate = atoi( argv[++argn] );
if ( start_rate < 1 )
{
(void) fprintf(
stderr, "%s: rate must be at least 1\n", argv0 );
exit( 1 );
}
if ( start_rate > 1000 )
{
(void) fprintf(
stderr, "%s: rate may be at most 1000\n", argv0 );
exit( 1 );
}
}
else if ( strncmp( argv[argn], "-fetches", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
{
end = END_FETCHES;
end_fetches = atoi( argv[++argn] );
if ( end_fetches < 1 )
{
(void) fprintf(
stderr, "%s: fetches must be at least 1\n", argv0 );
exit( 1 );
}
}
else if ( strncmp( argv[argn], "-seconds", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
{
end = END_SECONDS;
end_seconds = atoi( argv[++argn] );
if ( end_seconds < 1 )
{
(void) fprintf(
stderr, "%s: seconds must be at least 1\n", argv0 );
exit( 1 );
}
}
else if ( strncmp( argv[argn], "-sip", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
sip_file = argv[++argn];
#ifdef USE_SSL
else if ( strncmp( argv[argn], "-cipher", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
{
cipher = argv[++argn];
if ( strcasecmp( cipher, "fastsec" ) == 0 )
cipher = "RC4-MD5";
else if ( strcasecmp( cipher, "highsec" ) == 0 )
cipher = "DES-CBC3-SHA";
else if ( strcasecmp( cipher, "paranoid" ) == 0 )
cipher = "AES256-SHA";
}
#endif /* USE_SSL */
else if ( strncmp( argv[argn], "-proxy", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
{
char* colon;
do_proxy = 1;
proxy_hostname = argv[++argn];
colon = strchr( proxy_hostname, ':' );
if ( colon == (char*) 0 )
proxy_port = 80;
else
{
proxy_port = (unsigned short) atoi( colon + 1 );
*colon = '\0';
}
}
else
usage();
++argn;
}
if ( argn + 1 != argc )
usage();
if ( start == START_NONE || end == END_NONE )
usage();
if ( do_jitter && start != START_RATE )
usage();
url_file = argv[argn];
/* Read in and parse the URLs. */
read_url_file( url_file );
/* Read in the source IP file, if specified. */
if ( sip_file != (char*) 0 )
read_sip_file( sip_file );
/* Initialize the connections table. */
if ( start == START_PARALLEL )
max_connections = start_parallel;
connections = (connection*) malloc_check(
max_connections * sizeof(connection) );
for ( cnum = 0; cnum < max_connections; ++cnum )
connections[cnum].conn_state = CNST_FREE; // ¿¿¿¿¿¿¿¿¿¿¿¿
num_connections = max_parallel = 0;
/* Initialize the HTTP status-code histogram. */
for ( i = 0; i < 1000; ++i )
http_status_counts[i] = 0;
/* Initialize the statistics. */
fetches_started = 0;
connects_completed = 0;
responses_completed = 0;
fetches_completed = 0;
total_bytes = 0;
total_connect_usecs = 0;
max_connect_usecs = 0;
min_connect_usecs = 1000000000L;
total_response_usecs = 0;
max_response_usecs = 0;
min_response_usecs = 1000000000L;
total_timeouts = 0;
total_badbytes = 0;
total_badchecksums = 0;
/* Initialize the random number generator. */
#ifdef HAVE_SRANDOMDEV
srandomdev();
#else
srandom( (int) time( (time_t*) 0 ) ^ getpid() );
#endif
/* Initialize the rest. */
tmr_init();
(void) gettimeofday( &now, (struct timezone*) 0 );
start_at = now;
if ( do_verbose )
(void) tmr_create(
&now, progress_report, JunkClientData, PROGRESS_SECS * 1000L, 1 );
if ( start == START_RATE )
{
start_interval = 1000L / start_rate;
if ( do_jitter ) //¿¿¿jitter¿¿¿¿¿¿¿¿¿¿¿
{
low_interval = start_interval * 9 / 10;
high_interval = start_interval * 11 / 10;
range_interval = high_interval - low_interval + 1;
}
(void) tmr_create(
&now, start_timer, JunkClientData, start_interval, ! do_jitter );
}
if ( end == END_SECONDS )
(void) tmr_create(
&now, end_timer, JunkClientData, end_seconds * 1000L, 0 );
(void) signal( SIGPIPE, SIG_IGN );
/* Main loop. */
for (;;)
{
if ( end == END_FETCHES && fetches_completed >= end_fetches ) //¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿
finish( &now );
if ( start == START_PARALLEL )
{
/* See if we need to start any new connections; but at most 10. */
for ( i = 0;
i < 10 &&
num_connections < start_parallel &&
( end != END_FETCHES || fetches_started < end_fetches ); //¿¿¿¿¿¿¿¿¿¿¿¿
{
(void) fprintf( stderr, "i = %d \n", i);
start_connection( &now ); //¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿
(void) gettimeofday( &now, (struct timezone*) 0 );
tmr_run( &now );
}
}
/* Build the fdsets. */
FD_ZERO( &rfdset );
FD_ZERO( &wfdset );
for ( cnum = 0; cnum < max_connections; ++cnum )
switch ( connections[cnum].conn_state )
{
case CNST_CONNECTING: // ¿¿¿¿¿¿¿¿¿¿¿¿¿
FD_SET( connections[cnum].conn_fd, &wfdset ); // ¿¿¿¿¿¿¿¿¿¿¿¿¿
break;
case CNST_HEADERS: //¿¿¿¿¿¿¿¿¿¿ CNST_HEADERS¿¿
case CNST_READING: //¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿.¿¿¿¿¿¿¿¿¿¿
FD_SET( connections[cnum].conn_fd, &rfdset );
break;
}
r = select(
FD_SETSIZE, &rfdset, &wfdset, (fd_set*) 0, tmr_timeout( &now ) );
if ( r < 0 )
{
perror( "select" );
exit( 1 );
}
(void) gettimeofday( &now, (struct timezone*) 0 );
/* Service them. */
for ( cnum = 0; cnum < max_connections; ++cnum )
switch ( connections[cnum].conn_state )
{
case CNST_CONNECTING:
if ( FD_ISSET( connections[cnum].conn_fd, &wfdset ) )
handle_connect( cnum, &now, 1 ); //¿¿¿¿¿¿¿¿¿¿¿¿
break;
case CNST_HEADERS:
case CNST_READING:
if ( FD_ISSET( connections[cnum].conn_fd, &rfdset ) ) handle_read( cnum, &now );
break;
}
/* And run the timers. */
tmr_run( &now );
}
/* NOT_REACHED */
}
static void
usage( void )
{
(void) fprintf( stderr,
"usage: %s [-checksum] [-throttle] [-proxy host:port] [-verbose] [-timeout secs] [-sip sip_file]\n", argv0 );
#ifdef USE_SSL
(void) fprintf( stderr,
" [-cipher str]\n" );
#endif /* USE_SSL */
(void) fprintf( stderr,
" -parallel N | -rate N [-jitter]\n" );
(void) fprintf( stderr,
" -fetches N | -seconds N\n" );
(void) fprintf( stderr,
" url_file\n" );
(void) fprintf( stderr,
"One start specifier, either -parallel or -rate, is required.\n" );
(void) fprintf( stderr,
"One end specifier, either -fetches or -seconds, is required.\n" );
exit( 1 );
}
static void
read_url_file( char* url_file )
{
FILE* fp;
char line[5000], hostname[5000];
char* http = "http://";
int http_len = strlen( http );
#ifdef USE_SSL
char* https = "https://";
int https_len = strlen( https );
#endif
int proto_len, host_len;
char* cp;
fp = fopen( url_file, "r" );
if ( fp == (FILE*) 0 )
{
perror( url_file );
exit( 1 );
}
max_urls = 100;
urls = (url*) malloc_check( max_urls * sizeof(url) );
num_urls = 0;
while ( fgets( line, sizeof(line), fp ) != (char*) 0 )
{
/* Nuke trailing newline. */
if ( line[strlen( line ) - 1] == '\n' )
line[strlen( line ) - 1] = '\0';
/* Check for room in urls. */
if ( num_urls >= max_urls )
{
max_urls *= 2;
urls = (url*) realloc_check( (void*) urls, max_urls * sizeof(url) );
}
/* Add to table. */
urls[num_urls].url_str = strdup_check( line );
/* Parse it. */
if ( strncmp( http, line, http_len ) == 0 )
{
proto_len = http_len;
urls[num_urls].protocol = PROTO_HTTP;
}
#ifdef USE_SSL
else if ( strncmp( https, line, https_len ) == 0 )
{
proto_len = https_len;
urls[num_urls].protocol = PROTO_HTTPS;
}
#endif
else
{
(void) fprintf( stderr, "%s: unknown protocol - %s\n", argv0, line );
exit( 1 );
}
for ( cp = line + proto_len;
*cp != '\0' && *cp != ':' && *cp != '/'; ++cp )
;
host_len = cp - line;
host_len -= proto_len;
strncpy( hostname, line + proto_len, host_len );
hostname[host_len] = '\0';
urls[num_urls].hostname = strdup_check( hostname );
if ( *cp == ':' )
{
urls[num_urls].port = (unsigned short) atoi( ++cp );
while ( *cp != '\0' && *cp != '/' )
++cp;
}
else
#ifdef USE_SSL
if ( urls[num_urls].protocol == PROTO_HTTPS )
urls[num_urls].port = 443;
else
urls[num_urls].port = 80;
#else
urls[num_urls].port = 80;
#endif
if ( *cp == '\0' )
urls[num_urls].filename = strdup_check( "/" );
else
urls[num_urls].filename = strdup_check( cp );
lookup_address( num_urls );
urls[num_urls].got_bytes = 0;
urls[num_urls].got_checksum = 0;
++num_urls;
}
}
static void
lookup_address( int url_num )
{
char* hostname;
unsigned short port;
#ifdef USE_IPV6
struct addrinfo hints;
char portstr[10];
int gaierr;
struct addrinfo* ai;
struct addrinfo* ai2;
struct addrinfo* aiv4;
struct addrinfo* aiv6;
#else /* USE_IPV6 */
struct hostent *he;
#endif /* USE_IPV6 */
urls[url_num].sa_len = sizeof(urls[url_num].sa);
(void) memset( (void*) &urls[url_num].sa, 0, urls[url_num].sa_len );
if ( do_proxy )
{
hostname = proxy_hostname;
port = proxy_port;
}
else
{
hostname = urls[url_num].hostname;
port = urls[url_num].port;
}
#ifdef USE_IPV6
(void) memset( &hints, 0, sizeof(hints) );
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
(void) snprintf( portstr, sizeof(portstr), "%d", (int) port );
if ( (gaierr = getaddrinfo( hostname, portstr, &hints, &ai )) != 0 )
{
(void) fprintf(
stderr, "%s: getaddrinfo %s - %s\n", argv0, hostname,
gai_strerror( gaierr ) );
exit( 1 );
}
/* Find the first IPv4 and IPv6 entries. */
aiv4 = (struct addrinfo*) 0;
aiv6 = (struct addrinfo*) 0;
for ( ai2 = ai; ai2 != (struct addrinfo*) 0; ai2 = ai2->ai_next )
{
switch ( ai2->ai_family )
{
case AF_INET:
if ( aiv4 == (struct addrinfo*) 0 )
aiv4 = ai2;
break;
case AF_INET6:
if ( aiv6 == (struct addrinfo*) 0 )
aiv6 = ai2;
break;
}
}
/* If there's an IPv4 address, use that, otherwise try IPv6. */
if ( aiv4 != (struct addrinfo*) 0 )
{
if ( sizeof(urls[url_num].sa) < aiv4->ai_addrlen )
{
(void) fprintf(
stderr, "%s - sockaddr too small (%lu < %lu)\n", hostname,
(unsigned long) sizeof(urls[url_num].sa),
(unsigned long) aiv4->ai_addrlen );
exit( 1 );
}
urls[url_num].sock_family = aiv4->ai_family;
urls[url_num].sock_type = aiv4->ai_socktype;
urls[url_num].sock_protocol = aiv4->ai_protocol;
urls[url_num].sa_len = aiv4->ai_addrlen;
(void) memmove( &urls[url_num].sa, aiv4->ai_addr, aiv4->ai_addrlen );
freeaddrinfo( ai );
return;
}
if ( aiv6 != (struct addrinfo*) 0 )
{
if ( sizeof(urls[url_num].sa) < aiv6->ai_addrlen )
{
(void) fprintf(
stderr, "%s - sockaddr too small (%lu < %lu)\n", hostname,
(unsigned long) sizeof(urls[url_num].sa),
(unsigned long) aiv6->ai_addrlen );
exit( 1 );
}
urls[url_num].sock_family = aiv6->ai_family;
urls[url_num].sock_type = aiv6->ai_socktype;
urls[url_num].sock_protocol = aiv6->ai_protocol;
urls[url_num].sa_len = aiv6->ai_addrlen;
(void) memmove( &urls[url_num].sa, aiv6->ai_addr, aiv6->ai_addrlen );
freeaddrinfo( ai );
return;
}
(void) fprintf(
stderr, "%s: no valid address found for host %s\n", argv0, hostname );
exit( 1 );
#else /* USE_IPV6 */
he = gethostbyname( hostname );
if ( he == (struct hostent*) 0 )
{
(void) fprintf( stderr, "%s: unknown host - %s\n", argv0, hostname );
exit( 1 );
}
urls[url_num].sock_family = urls[url_num].sa.sin_family = he->h_addrtype;
urls[url_num].sock_type = SOCK_STREAM;
urls[url_num].sock_protocol = 0;
urls[url_num].sa_len = sizeof(urls[url_num].sa);
(void) memmove( &urls[url_num].sa.sin_addr, he->h_addr, he->h_length );
urls[url_num].sa.sin_port = htons( port );
#endif /* USE_IPV6 */
}
static void
read_sip_file( char* sip_file )
{
FILE* fp;
char line[5000];
fp = fopen( sip_file, "r" );
if ( fp == (FILE*) 0 )
{
perror( sip_file );
exit( 1 );
}
max_sips = 100;
sips = (sip*) malloc_check( max_sips * sizeof(sip) );
num_sips = 0;
while ( fgets( line, sizeof(line), fp ) != (char*) 0 )
{
/* Nuke trailing newline. */
if ( line[strlen( line ) - 1] == '\n' )
line[strlen( line ) - 1] = '\0';
/* Check for room in sips. */
if ( num_sips >= max_sips )
{
max_sips *= 2;
sips = (sip*) realloc_check( (void*) sips, max_sips * sizeof(sip) );
}
/* Add to table. */
sips[num_sips].str = strdup_check( line );
(void) memset( (void*) &sips[num_sips].sa, 0, sizeof(sips[num_sips].sa) );
if ( ! inet_aton( sips[num_sips].str, &sips[num_sips].sa.sin_addr ) )
{
(void) fprintf(
stderr, "%s: cannot convert source IP address %s\n",
argv0, sips[num_sips].str );
exit( 1 );
}
++num_sips;
}
}
static void
start_connection( struct timeval* nowP )
{
int cnum, url_num;
int huhu = 0 ;
/* Find an empty connection slot. */
for ( cnum = 0; cnum < max_connections; ++cnum )
if ( connections[cnum].conn_state == CNST_FREE ) // ¿¿¿¿¿¿¿¿¿¿¿¿¿
{
/* Choose a URL. */
url_num = ( (unsigned long) random() ) % ( (unsigned int) num_urls ); //¿¿¿¿¿¿¿
/* Start the socket. */
start_socket( url_num, cnum, nowP );
if ( connections[cnum].conn_state != CNST_FREE )
{
++num_connections;
if ( num_connections > max_parallel )
max_parallel = num_connections;
}
(void) fprintf( stderr, "num_connections = %d\n", num_connections);
(void) fprintf( stderr, "max_parallel = %d\n", max_parallel);
(void) fprintf( stderr, "max_connections = %d\n", max_connections);
++fetches_started;
return;
}
/* No slots left. */
(void) fprintf( stderr, "%s: ran out of connection slots\n", argv0 );
finish( nowP );
}
static void
start_socket( int url_num, int cnum, struct timeval* nowP ) //¿¿¿¿¿¿¿¿¿¿
{
ClientData client_data;
int flags;
int sip_num;
/* Start filling in the connection slot. */
connections[cnum].url_num = url_num;
connections[cnum].started_at = *nowP;
client_data.i = cnum;
connections[cnum].did_connect = 0;
connections[cnum].did_response = 0;
connections[cnum].idle_timer = tmr_create(
nowP, idle_connection, client_data, idle_secs * 1000L, 0 );
connections[cnum].wakeup_timer = (Timer*) 0;
connections[cnum].content_length = -1;
connections[cnum].bytes = 0;
connections[cnum].checksum = 0;
connections[cnum].http_status = -1;
/* Make a socket. */
connections[cnum].conn_fd = socket(
urls[url_num].sock_family, urls[url_num].sock_type,
urls[url_num].sock_protocol );
if ( connections[cnum].conn_fd < 0 ) //¿¿¿¿¿¿¿¿¿socket¿¿¿¿¿¿¿¿¿¿
{
perror( urls[url_num].url_str );
return;
}
/* Set the file descriptor to no-delay mode. */
flags = fcntl( connections[cnum].conn_fd, F_GETFL, 0 ); //¿¿¿¿¿¿¿¿¿¿¿
if ( flags == -1 )
{
perror( urls[url_num].url_str );
(void) close( connections[cnum].conn_fd );
return;
}
if ( fcntl( connections[cnum].conn_fd, F_SETFL, flags | O_NDELAY ) < 0 ) // ¿¿¿¿¿¿¿¿O_BLOCK
{
perror( urls[url_num].url_str );
(void) close( connections[cnum].conn_fd );
return;
}
if ( num_sips > 0 ) //¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿
{
/* Try a random source IP address. */
sip_num = ( (unsigned long) random() ) % ( (unsigned int) num_sips );
if ( bind(
connections[cnum].conn_fd,
(struct sockaddr*) &sips[sip_num].sa,
sizeof(sips[sip_num].sa) ) < 0 )
{
perror( "binding local address" );
(void) close( connections[cnum].conn_fd );
return;
}
}
// ¿¿0 ¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿<0 ¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿
/* Connect to the host. */
connections[cnum].sa_len = urls[url_num].sa_len;
(void) memmove(
(void*) &connections[cnum].sa, (void*) &urls[url_num].sa,
urls[url_num].sa_len );
connections[cnum].connect_at = *nowP;
if ( connect(
connections[cnum].conn_fd,
(struct sockaddr*) &connections[cnum].sa,
connections[cnum].sa_len ) < 0 )
{
if ( errno == EINPROGRESS ) //¿¿¿¿¿¿¿¿¿¿ [ peration now in progress ]
{
connections[cnum].conn_state = CNST_CONNECTING; //¿¿¿¿¿¿¿¿¿¿¿¿ -1 ¿¿
return;
}
else
{
perror( urls[url_num].url_str );
(void) close( connections[cnum].conn_fd );
return;
}
}
//¿¿¿¿< 0¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿
/* Connect succeeded instantly, so handle it now. */
(void) gettimeofday( nowP, (struct timezone*) 0 );
handle_connect( cnum, nowP, 0 );
}
//handle_connect :¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿select¿¿¿¿fd¿¿¿¿¿¿¿¿¿¿¿¿¿¿
static void
handle_connect( int cnum, struct timeval* nowP, int double_check )
{
int url_num;
char buf[600];
int bytes, r;
url_num = connections[cnum].url_num;
if ( double_check )
{
/* Check to make sure the non-blocking connect succeeded. */
int err, errlen;
if ( connect(
connections[cnum].conn_fd,
(struct sockaddr*) &connections[cnum].sa,
connections[cnum].sa_len ) < 0 ) //¿¿¿ <0 ¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿
{
switch ( errno )
{
case EISCONN:
/* Ok! */
break;
case EINVAL:
errlen = sizeof(err);
if ( getsockopt( connections[cnum].conn_fd, SOL_SOCKET, SO_ERROR, (void*) &err, &errlen ) < 0 )
(void) fprintf(
stderr, "%s: unknown connect error\n",
urls[url_num].url_str );
else
(void) fprintf(
stderr, "%s: %s\n", urls[url_num].url_str,
strerror( err ) );
close_connection( cnum );
return;
default:
perror( urls[url_num].url_str );
close_connection( cnum );
return;
}
}
}
// ¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿
#ifdef USE_SSL
if ( urls[url_num].protocol == PROTO_HTTPS )
{
int flags;
/* Make SSL connection. */
if ( ssl_ctx == (SSL_CTX*) 0 )
{
SSL_load_error_strings();
SSLeay_add_ssl_algorithms();
ssl_ctx = SSL_CTX_new( SSLv23_client_method() );
if ( cipher != (char*) 0 )
{
if ( ! SSL_CTX_set_cipher_list( ssl_ctx, cipher ) )
{
(void) fprintf(
stderr, "%s: cannot set cipher list\n", argv0 );
ERR_print_errors_fp( stderr );
close_connection( cnum );
return;
}
}
}
if ( ! RAND_status() )
{
unsigned char bytes[1024];
int i;
for ( i = 0; i < sizeof(bytes); ++i )
bytes[i] = random() % 0xff;
RAND_seed( bytes, sizeof(bytes) );
}
flags = fcntl( connections[cnum].conn_fd, F_GETFL, 0 );
if ( flags != -1 )
(void) fcntl(
connections[cnum].conn_fd, F_SETFL, flags & ~ (int) O_NDELAY );
connections[cnum].ssl = SSL_new( ssl_ctx );
SSL_set_fd( connections[cnum].ssl, connections[cnum].conn_fd );
r = SSL_connect( connections[cnum].ssl );
if ( r <= 0 )
{
(void) fprintf(
stderr, "%s: SSL connection failed - %d\n", argv0, r );
ERR_print_errors_fp( stderr );
close_connection( cnum );
return;
}
}
#endif
connections[cnum].did_connect = 1;
/* Format the request. */
if ( do_proxy )
{
#ifdef USE_SSL
bytes = snprintf(
buf, sizeof(buf), "GET %s://%.500s:%d%.500s HTTP/1.0\r\n",
urls[url_num].protocol == PROTO_HTTPS ? "https" : "http",
urls[url_num].hostname, (int) urls[url_num].port,
urls[url_num].filename );
#else
bytes = snprintf(
buf, sizeof(buf), "GET http://%.500s:%d%.500s HTTP/1.0\r\n",
urls[url_num].hostname, (int) urls[url_num].port,
urls[url_num].filename );
#endif
}
else
bytes = snprintf(
buf, sizeof(buf), "GET %.500s HTTP/1.0\r\n",
urls[url_num].filename );
bytes += snprintf(
&buf[bytes], sizeof(buf) - bytes, "Host: %s\r\n",
urls[url_num].hostname );
bytes += snprintf(
&buf[bytes], sizeof(buf) - bytes, "User-Agent: %s\r\n", VERSION );
bytes += snprintf( &buf[bytes], sizeof(buf) - bytes, "\r\n" );
/* Send the request. */
connections[cnum].request_at = *nowP;
#ifdef USE_SSL
if ( urls[url_num].protocol == PROTO_HTTPS )
r = SSL_write( connections[cnum].ssl, buf, bytes );
else
r = write( connections[cnum].conn_fd, buf, bytes );
#else
r = write( connections[cnum].conn_fd, buf, bytes );
#endif
if ( r < 0 )
{
perror( urls[url_num].url_str );
close_connection( cnum );
return;
}
connections[cnum].conn_state = CNST_HEADERS;
connections[cnum].header_state = HDST_LINE1_PROTOCOL;
}
//¿¿handle_connect¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿¿
static void
handle_read( int cnum, struct timeval* nowP )
{
char buf[30000]; /* must be larger than throttle / 2 */
int bytes_to_read, bytes_read, bytes_handled;
float elapsed;
ClientData client_data;
register long checksum;
tmr_reset( nowP, connections[cnum].idle_timer );
if ( do_throttle )
bytes_to_read = throttle / 2.0;
else
bytes_to_read = sizeof(buf);
if ( ! connections[cnum].did_response )
{
connections[cnum].did_response = 1;
connections[cnum].response_at = *nowP;
}
#ifdef USE_SSL
if ( urls[connections[cnum].url_num].protocol == PROTO_HTTPS )
bytes_read = SSL_read( connections[cnum].ssl, buf, bytes_to_read );
else
bytes_read = read( connections[cnum].conn_fd, buf, bytes_to_read );
#else
bytes_read = read( connections[cnum].conn_fd, buf, bytes_to_read );
#endif
if ( bytes_read <= 0 )
{
close_connection( cnum );
return;
}
for ( bytes_handled = 0; bytes_handled < bytes_read; )
{
switch ( connections[cnum].conn_state )
{
case CNST_HEADERS:
/* State machine to read until we reach the file part. Looks for
** Content-Length header too.
*/
for ( ; bytes_handled < bytes_read && connections[cnum].conn_state == CNST_HEADERS; ++bytes_handled )
{
switch ( connections[cnum].header_state )
{
case HDST_LINE1_PROTOCOL:
switch ( buf[bytes_handled] )
{
case ' ': case '\t':
connections[cnum].header_state = HDST_LINE1_WHITESPACE;
break;
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
}
break;
case HDST_LINE1_WHITESPACE:
switch ( buf[bytes_handled] )
{
case ' ': case '\t':
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
connections[cnum].http_status =
buf[bytes_handled] - '0';
connections[cnum].header_state = HDST_LINE1_STATUS;
break;
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_LINE1_STATUS:
switch ( buf[bytes_handled] )
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
connections[cnum].http_status =
connections[cnum].http_status * 10 +
buf[bytes_handled] - '0';
break;
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_BOL:
switch ( buf[bytes_handled] )
{
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
case 'C': case 'c':
connections[cnum].header_state = HDST_C;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_TEXT:
switch ( buf[bytes_handled] )
{
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
default:
break;
}
break;
case HDST_LF:
switch ( buf[bytes_handled] )
{
case '\n':
connections[cnum].conn_state = CNST_READING;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
case 'C': case 'c':
connections[cnum].header_state = HDST_C;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_CR:
switch ( buf[bytes_handled] )
{
case '\n':
connections[cnum].header_state = HDST_CRLF;
break;
case '\r':
connections[cnum].conn_state = CNST_READING;
break;
case 'C': case 'c':
connections[cnum].header_state = HDST_C;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_CRLF:
switch ( buf[bytes_handled] )
{
case '\n':
connections[cnum].conn_state = CNST_READING;
break;
case '\r':
connections[cnum].header_state = HDST_CRLFCR;
break;
case 'C': case 'c':
connections[cnum].header_state = HDST_C;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_CRLFCR:
switch ( buf[bytes_handled] )
{
case '\n': case '\r':
connections[cnum].conn_state = CNST_READING;
break;
case 'C': case 'c':
connections[cnum].header_state = HDST_C;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_C:
switch ( buf[bytes_handled] )
{
case 'O': case 'o':
connections[cnum].header_state = HDST_CO;
break;
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_CO:
switch ( buf[bytes_handled] )
{
case 'N': case 'n':
connections[cnum].header_state = HDST_CON;
break;
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_CON:
switch ( buf[bytes_handled] )
{
case 'T': case 't':
connections[cnum].header_state = HDST_CONT;
break;
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_CONT:
switch ( buf[bytes_handled] )
{
case 'E': case 'e':
connections[cnum].header_state = HDST_CONTE;
break;
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_CONTE:
switch ( buf[bytes_handled] )
{
case 'N': case 'n':
connections[cnum].header_state = HDST_CONTEN;
break;
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_CONTEN:
switch ( buf[bytes_handled] )
{
case 'T': case 't':
connections[cnum].header_state = HDST_CONTENT;
break;
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_CONTENT:
switch ( buf[bytes_handled] )
{
case '-':
connections[cnum].header_state = HDST_CONTENT_;
break;
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_CONTENT_:
switch ( buf[bytes_handled] )
{
case 'L': case 'l':
connections[cnum].header_state = HDST_CONTENT_L;
break;
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_CONTENT_L:
switch ( buf[bytes_handled] )
{
case 'E': case 'e':
connections[cnum].header_state = HDST_CONTENT_LE;
break;
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_CONTENT_LE:
switch ( buf[bytes_handled] )
{
case 'N': case 'n':
connections[cnum].header_state = HDST_CONTENT_LEN;
break;
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_CONTENT_LEN:
switch ( buf[bytes_handled] )
{
case 'G': case 'g':
connections[cnum].header_state = HDST_CONTENT_LENG;
break;
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_CONTENT_LENG:
switch ( buf[bytes_handled] )
{
case 'T': case 't':
connections[cnum].header_state = HDST_CONTENT_LENGT;
break;
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_CONTENT_LENGT:
switch ( buf[bytes_handled] )
{
case 'H': case 'h':
connections[cnum].header_state = HDST_CONTENT_LENGTH;
break;
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_CONTENT_LENGTH:
switch ( buf[bytes_handled] )
{
case ':':
connections[cnum].header_state = HDST_CONTENT_LENGTH_COLON;
break;
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_CONTENT_LENGTH_COLON:
switch ( buf[bytes_handled] )
{
case ' ': case '\t':
connections[cnum].header_state = HDST_CONTENT_LENGTH_COLON_WHITESPACE;
break;
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_CONTENT_LENGTH_COLON_WHITESPACE:
switch ( buf[bytes_handled] )
{
case ' ': case '\t':
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
connections[cnum].content_length = buf[bytes_handled] - '0';
connections[cnum].header_state = HDST_CONTENT_LENGTH_COLON_WHITESPACE_NUM;
break;
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
case HDST_CONTENT_LENGTH_COLON_WHITESPACE_NUM:
switch ( buf[bytes_handled] )
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
connections[cnum].content_length =
connections[cnum].content_length * 10 +
buf[bytes_handled] - '0';
break;
case '\n':
connections[cnum].header_state = HDST_LF;
break;
case '\r':
connections[cnum].header_state = HDST_CR;
break;
default:
connections[cnum].header_state = HDST_TEXT;
break;
}
break;
}
}
break;
case CNST_READING:
connections[cnum].bytes += bytes_read - bytes_handled;
if ( do_throttle )
{
/* Check if we're reading too fast. */
elapsed = delta_timeval( &connections[cnum].started_at, nowP ) / 1000000.0;
if ( elapsed > 0.01 && connections[cnum].bytes / elapsed > throttle )
{
connections[cnum].conn_state = CNST_PAUSING;
client_data.i = cnum;
connections[cnum].wakeup_timer = tmr_create(
nowP, wakeup_connection, client_data, 1000L, 0 );
}
}
if ( do_checksum )
{
checksum = connections[cnum].checksum;
for ( ; bytes_handled < bytes_read; ++bytes_handled )
{
if ( checksum & 1 )
checksum = ( checksum >> 1 ) + 0x8000;
else
checksum >>= 1;
checksum += buf[bytes_handled];
checksum &= 0xffff;
}
connections[cnum].checksum = checksum;
}
else
bytes_handled = bytes_read;
if ( connections[cnum].content_length != -1 &&
connections[cnum].bytes >= connections[cnum].content_length )
{
close_connection( cnum );
return;
}
break;
}
}
}
static void
idle_connection( ClientData client_data, struct timeval* nowP )
{
int cnum;
cnum = client_data.i;
connections[cnum].idle_timer = (Timer*) 0;
(void) fprintf(
stderr, "%s: timed out\n", urls[connections[cnum].url_num].url_str );
close_connection( cnum );
++total_timeouts;
}
static void
wakeup_connection( ClientData client_data, struct timeval* nowP )
{
int cnum;
cnum = client_data.i;
connections[cnum].wakeup_timer = (Timer*) 0;
connections[cnum].conn_state = CNST_READING;
}
static void
close_connection( int cnum )
{
int url_num;
#ifdef USE_SSL
if ( urls[connections[cnum].url_num].protocol == PROTO_HTTPS )
SSL_free( connections[cnum].ssl );
#endif
(void) close( connections[cnum].conn_fd );
connections[cnum].conn_state = CNST_FREE;
if ( connections[cnum].idle_timer != (Timer*) 0 )
tmr_cancel( connections[cnum].idle_timer );
if ( connections[cnum].wakeup_timer != (Timer*) 0 )
tmr_cancel( connections[cnum].wakeup_timer );
--num_connections;
++fetches_completed;
total_bytes += connections[cnum].bytes;
if ( connections[cnum].did_connect )
{
long long connect_usecs = delta_timeval(
&connections[cnum].connect_at, &connections[cnum].request_at );
total_connect_usecs += connect_usecs;
max_connect_usecs = max( max_connect_usecs, connect_usecs );
min_connect_usecs = min( min_connect_usecs, connect_usecs );
++connects_completed;
}
if ( connections[cnum].did_response )
{
long long response_usecs = delta_timeval(
&connections[cnum].request_at, &connections[cnum].response_at );
total_response_usecs += response_usecs;
max_response_usecs = max( max_response_usecs, response_usecs );
min_response_usecs = min( min_response_usecs, response_usecs );
++responses_completed;
}
if ( connections[cnum].http_status >= 0 && connections[cnum].http_status <= 999 )
++http_status_counts[connections[cnum].http_status];
url_num = connections[cnum].url_num;
if ( do_checksum )
{
if ( ! urls[url_num].got_checksum )
{
urls[url_num].checksum = connections[cnum].checksum;
urls[url_num].got_checksum = 1;
}
else
{
if ( connections[cnum].checksum != urls[url_num].checksum )
{
(void) fprintf(
stderr, "%s: checksum wrong\n", urls[url_num].url_str );
++total_badchecksums;
}
}
}
else
{
if ( ! urls[url_num].got_bytes )
{
urls[url_num].bytes = connections[cnum].bytes;
urls[url_num].got_bytes = 1;
}
else
{
if ( connections[cnum].bytes != urls[url_num].bytes )
{
(void) fprintf(
stderr, "%s: byte count wrong\n", urls[url_num].url_str );
++total_badbytes;
}
}
}
}
static void
progress_report( ClientData client_data, struct timeval* nowP )
{
float elapsed;
elapsed = delta_timeval( &start_at, nowP ) / 1000000.0;
(void) fprintf( stderr,
"--- %g secs, %d fetches started, %d completed, %d current\n",
elapsed, fetches_started, fetches_completed, num_connections );
}
static void
start_timer( ClientData client_data, struct timeval* nowP )
{
start_connection( nowP );
if ( do_jitter )
(void) tmr_create(
nowP, start_timer, JunkClientData,
(long) ( random() % range_interval ) + low_interval, 0 );
}
static void
end_timer( ClientData client_data, struct timeval* nowP )
{
finish( nowP );
}
static void
finish( struct timeval* nowP )
{
float elapsed;
int i;
/* Report statistics. */
elapsed = delta_timeval( &start_at, nowP ) / 1000000.0;
(void) printf(
"%d fetches, %d max parallel, %g bytes, in %g seconds\n",
fetches_completed, max_parallel, (float) total_bytes, elapsed );
if ( fetches_completed > 0 )
(void) printf(
"%g mean bytes/connection\n",
(float) total_bytes / (float) fetches_completed );
if ( elapsed > 0.01 )
{
(void) printf(
"%g fetches/sec, %g bytes/sec\n",
(float) fetches_completed / elapsed,
(float) total_bytes / elapsed );
}
if ( connects_completed > 0 )
(void) printf(
"msecs/connect: %g mean, %g max, %g min\n",
(float) total_connect_usecs / (float) connects_completed / 1000.0,
(float) max_connect_usecs / 1000.0,
(float) min_connect_usecs / 1000.0 );
if ( responses_completed > 0 )
(void) printf(
"msecs/first-response: %g mean, %g max, %g min\n",
(float) total_response_usecs / (float) responses_completed / 1000.0,
(float) max_response_usecs / 1000.0,
(float) min_response_usecs / 1000.0 );
if ( total_timeouts != 0 )
(void) printf( "%d timeouts\n", total_timeouts );
if ( do_checksum )
{
if ( total_badchecksums != 0 )
(void) printf( "%d bad checksums\n", total_badchecksums );
}
else
{
if ( total_badbytes != 0 )
(void) printf( "%d bad byte counts\n", total_badbytes );
}
(void) printf( "HTTP response codes:\n" );
for ( i = 0; i < 1000; ++i )
if ( http_status_counts[i] > 0 )
(void) printf( " code %03d -- %d\n", i, http_status_counts[i] );
tmr_destroy();
#ifdef USE_SSL
if ( ssl_ctx != (SSL_CTX*) 0 )
SSL_CTX_free( ssl_ctx );
#endif
exit( 0 );
}
static long long
delta_timeval( struct timeval* start, struct timeval* finish )
{
long long delta_secs = finish->tv_sec - start->tv_sec;
long long delta_usecs = finish->tv_usec - start->tv_usec;
return delta_secs * (long long) 1000000L + delta_usecs;
}
static void*
malloc_check( size_t size )
{
void* ptr = malloc( size );
check( ptr );
return ptr;
}
static void*
realloc_check( void* ptr, size_t size )
{
ptr = realloc( ptr, size );
check( ptr );
return ptr;
}
static char*
strdup_check( char* str )
{
str = strdup( str );
check( (void*) str );
return str;
}
static void
check( void* ptr )
{
if ( ptr == (void*) 0 )
{
(void) fprintf( stderr, "%s: out of memory\n", argv0 );
exit( 1 );
}
}
http load 源代码分析
最新推荐文章于 2023-07-05 00:00:51 发布