Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

* README created and updated the ronn file for the manpage.

  • Loading branch information...
commit 1717431465b1fabefa6281368362b7b7a46cd428 1 parent bd7f2ff
António P. P. Almeida authored
Showing with 2,028 additions and 1,874 deletions.
  1. +0 −29 README
  2. +110 −0 README.md
  3. +2 −2 debian/httpload.1.ronn
  4. +1,667 −1,593 httpload.c
  5. +222 −224 timers.c
  6. +27 −26 timers.h
29 README
View
@@ -1,29 +0,0 @@
- http_load - multiprocessing http test client
- version of 12mar2006
-
-http_load runs multiple http fetches in parallel, to test the
-throughput of a web server. However unlike most such test clients,
-it runs in a single process, so it doesn't bog down the client
-machine. It can be configured to do https fetches as well.
-
-See the manual entry for more details.
-
-Files in this distribution:
-
- README this
- Makefile guess
- http_load.c source file
- http_load.1 manual entry
- timers.c timers package
- timers.h headers for timers package
- make_test_files simple script to create a set of test files
-
-To build: If you're on a SysV-like machine (which includes old Linux systems
-but not new Linux systems), edit the Makefile and uncomment the SYSV_LIBS
-line. If you're doing SSL, uncomment those lines too. Otherwise, just do
-a make.
-
-Feedback is welcome - send bug reports, enhancements, checks, money
-orders, etc. to the addresses below.
-
- Jef Poskanzer jef@mail.acme.com http://www.acme.com/jef/
110 README.md
View
@@ -0,0 +1,110 @@
+# httpload: event loop based multiprocessing HTTP load test tool
+
+## Introduction
+
+**httpload** is a tool for testing web sites. It issues a series of
+HTTP requests in parallel. The URLs to be hit are read from a file and
+tried randomly. It runs a single process. It's particularly adequate
+for testing asynchronous web servers in multi-core/multi-cpu machines
+like [Nginx](http://nginx.org).
+
+The [tool](http://www.acme.com/software/http_load/) was created by
+[Jef Poskanzer](http://www.acme.com/jef/).
+
+This version is IPv6 and SSL/TLS enabled.
+
+## Vanilla installation
+
+ 1. Clone the repo from git://github.com/perusio/httpload.git
+ or grab the tarball from [downloads](https://github.com/perusio/httpload/archives/master).
+
+ 2. Issue `make` & `make install`. By default the binary will be
+ installed under `/usr/bin` and the man page under `/usr/share/man/man1`.
+
+ 3. Done
+
+## Debian package installation
+
+ 1. Either download the binary package from
+ [debian.perusio.net](http://debian.perusio.net/unstable) or download the
+ source package and build from source while following the
+ [instructions](http://debian.perusio.net).
+
+ 2. Done.
+
+## Usage
+
+`httpload` requires you to specify an option for **starting** the
+connection and one for **terminating** the connection.
+
+ * `-parallel` or `-rate` for starting.
+
+ * `-seconds` or `-fetches` for terminating.
+
+ Additionally a `-jitter` option specifies that the rate given in
+ `-rate` varies up to 10%.
+
+The URLs in the URL file are visited in random order. You can specify
+the seed of the random number generator with:
+
+ * `-seed integer`
+
+The `-seed` option was added by
+[Tim Bunce](https://github.com/timbunce/http_load/commit/eaac0cab647dfa68b863565082f70c8ddcc5ee85).
+
+`httpload` can query HTTPS based servers. If you don't specify the
+cipher using:
+
+ * `-ciphers str` where `str` is a
+ [openssl](http://www.openssl.org/docs/apps/ciphers.html) cipher
+ specification strings.
+
+ An example is `ECDHE-RSA-AES256-SHA`.
+
+A complete example of querying the URLs specified in file
+`test_url_s.txt` using the specified cipher.
+
+ httpload -cipher ECDHE-RSA-AES256-SHA -parallel 10 -seconds 10 test_url_s.txt
+
+ 279 fetches, 10 max parallel, 11997 bytes, in 10.0124 seconds
+ 43 mean bytes/connection
+ 27.8654 fetches/sec, 1198.21 bytes/sec
+ msecs/connect: 33.6593 mean, 347.213 max, 0.037 min
+ msecs/first-response: 247.849 mean, 378.282 max, 31.069 min
+ HTTP response codes:
+ code 200 -- 279
+
+The tool accepts three shortcut names for a *fast*, *high* and
+*paranoid* cipher. Notwithstanding the *fast* cipher is not that
+fast, since it only accepts SSLv3+ ciphers. There are speed diferences
+between them. The shortcuts are:
+
+ * `fastsec` - CAMELLIA128-SHA
+ * `highsec` - AES256-SHA
+ * `paranoid` - DHE-RSA-AES256-SHA
+
+The complete usage documentation is on the
+[manpage](http://github.perusio.org).
+
+## Comparison with other testing tools
+
+ * [`ab`](http://httpd.apache.org/docs/2.2/programs/ab.html):
+ `httpload` differs significantly from `ab`, since uses the
+ [Apache Portable Runtime](http://apr.apache.org/docs/apr/1.4/) which
+ uses threads instead of a single process (loop) like `httpload`. `ab`
+ is **CPU bound**.
+
+ * [`siege`](http://www.joedog.org/siege-home): also relies on
+ **threads** contrary to `httpload` that uses a single process
+ (loop).
+
+## TODO
+
+ 1. Implement CSV output for statistical and graphical treatment of
+ the test results.
+
+ 2. Implement a single URL target given on the command line.
+
+ 3. Implement
+ [`epoll(7)`](https://secure.wikimedia.org/wikipedia/en/wiki/Epoll)
+ I/O event notification in Linux as an alternative to [`select(2)`](https://secure.wikimedia.org/wikipedia/en/wiki/Select_\(Unix\)).
4 debian/httpload.1.ronn
View
@@ -1,5 +1,5 @@
-httpload -- multiprocessing http test client
-=============================================
+httpload -- event loop based multiprocessing http test client
+=============================================================
## SYNOPSIS
3,260 httpload.c
View
1,667 additions, 1,593 deletions not shown
446 timers.c
View
@@ -34,295 +34,293 @@
#define HASH_SIZE 67
-static Timer* timers[HASH_SIZE];
-static Timer* free_timers = (Timer*) 0;
+static Timer *timers[HASH_SIZE];
+static Timer *free_timers = (Timer *) 0;
ClientData JunkClientData;
static unsigned int
-hash( Timer* t )
+hash (Timer * t)
{
- /* We can hash on the trigger time, even though it can change over
- ** the life of a timer via either the periodic bit or the tmr_reset()
- ** call. This is because both of those guys call l_resort(), which
- ** recomputes the hash and moves the timer to the appropriate list.
- */
- return (
- (unsigned int) t->time.tv_sec ^
- (unsigned int) t->time.tv_usec ) % HASH_SIZE;
+ /* We can hash on the trigger time, even though it can change over
+ ** the life of a timer via either the periodic bit or the tmr_reset()
+ ** call. This is because both of those guys call l_resort(), which
+ ** recomputes the hash and moves the timer to the appropriate list.
+ */
+ return ((unsigned int) t->time.tv_sec ^
+ (unsigned int) t->time.tv_usec) % HASH_SIZE;
}
static void
-l_add( Timer* t )
+l_add (Timer * t)
{
- int h = t->hash;
- register Timer* t2;
- register Timer* t2prev;
-
- t2 = timers[h];
- if ( t2 == (Timer*) 0 )
- {
- /* The list is empty. */
- timers[h] = t;
- t->prev = t->next = (Timer*) 0;
- }
- else
- {
- if ( t->time.tv_sec < t2->time.tv_sec ||
- ( t->time.tv_sec == t2->time.tv_sec &&
- t->time.tv_usec <= t2->time.tv_usec ) )
- {
- /* The new timer goes at the head of the list. */
- timers[h] = t;
- t->prev = (Timer*) 0;
- t->next = t2;
- t2->prev = t;
- }
- else
- {
- /* Walk the list to find the insertion point. */
- for ( t2prev = t2, t2 = t2->next; t2 != (Timer*) 0;
- t2prev = t2, t2 = t2->next )
- {
- if ( t->time.tv_sec < t2->time.tv_sec ||
- ( t->time.tv_sec == t2->time.tv_sec &&
- t->time.tv_usec <= t2->time.tv_usec ) )
- {
- /* Found it. */
- t2prev->next = t;
- t->prev = t2prev;
- t->next = t2;
- t2->prev = t;
- return;
- }
- }
- /* Oops, got to the end of the list. Add to tail. */
- t2prev->next = t;
- t->prev = t2prev;
- t->next = (Timer*) 0;
- }
- }
+ int h = t->hash;
+ register Timer *t2;
+ register Timer *t2prev;
+
+ t2 = timers[h];
+ if (t2 == (Timer *) 0)
+ {
+ /* The list is empty. */
+ timers[h] = t;
+ t->prev = t->next = (Timer *) 0;
+ }
+ else
+ {
+ if (t->time.tv_sec < t2->time.tv_sec ||
+ (t->time.tv_sec == t2->time.tv_sec &&
+ t->time.tv_usec <= t2->time.tv_usec))
+ {
+ /* The new timer goes at the head of the list. */
+ timers[h] = t;
+ t->prev = (Timer *) 0;
+ t->next = t2;
+ t2->prev = t;
+ }
+ else
+ {
+ /* Walk the list to find the insertion point. */
+ for (t2prev = t2, t2 = t2->next; t2 != (Timer *) 0;
+ t2prev = t2, t2 = t2->next)
+ {
+ if (t->time.tv_sec < t2->time.tv_sec ||
+ (t->time.tv_sec == t2->time.tv_sec &&
+ t->time.tv_usec <= t2->time.tv_usec))
+ {
+ /* Found it. */
+ t2prev->next = t;
+ t->prev = t2prev;
+ t->next = t2;
+ t2->prev = t;
+ return;
+ }
+ }
+ /* Oops, got to the end of the list. Add to tail. */
+ t2prev->next = t;
+ t->prev = t2prev;
+ t->next = (Timer *) 0;
+ }
+ }
}
static void
-l_remove( Timer* t )
+l_remove (Timer * t)
{
- int h = t->hash;
-
- if ( t->prev == (Timer*) 0 )
- timers[h] = t->next;
- else
- t->prev->next = t->next;
- if ( t->next != (Timer*) 0 )
- t->next->prev = t->prev;
+ int h = t->hash;
+
+ if (t->prev == (Timer *) 0)
+ timers[h] = t->next;
+ else
+ t->prev->next = t->next;
+ if (t->next != (Timer *) 0)
+ t->next->prev = t->prev;
}
static void
-l_resort( Timer* t )
+l_resort (Timer * t)
{
- /* Remove the timer from its old list. */
- l_remove( t );
- /* Recompute the hash. */
- t->hash = hash( t );
- /* And add it back in to its new list, sorted correctly. */
- l_add( t );
+ /* Remove the timer from its old list. */
+ l_remove (t);
+ /* Recompute the hash. */
+ t->hash = hash (t);
+ /* And add it back in to its new list, sorted correctly. */
+ l_add (t);
}
void
-tmr_init( void )
+tmr_init (void)
{
- int h;
+ int h;
- for ( h = 0; h < HASH_SIZE; ++h )
- timers[h] = (Timer*) 0;
+ for (h = 0; h < HASH_SIZE; ++h)
+ timers[h] = (Timer *) 0;
}
-Timer*
-tmr_create(
- struct timeval* nowP, TimerProc* timer_proc, ClientData client_data,
- long msecs, int periodic )
+Timer *
+tmr_create (struct timeval *nowP, TimerProc * timer_proc,
+ ClientData client_data, long msecs, int periodic)
{
- Timer* t;
-
- if ( free_timers != (Timer*) 0 )
- {
- t = free_timers;
- free_timers = t->next;
- }
- else
- {
- t = (Timer*) malloc( sizeof(Timer) );
- if ( t == (Timer*) 0 )
- return (Timer*) 0;
- }
-
- t->timer_proc = timer_proc;
- t->client_data = client_data;
- t->msecs = msecs;
- t->periodic = periodic;
- if ( nowP != (struct timeval*) 0 )
- t->time = *nowP;
- else
- (void) gettimeofday( &t->time, (struct timezone*) 0 );
- t->time.tv_sec += msecs / 1000L;
- t->time.tv_usec += ( msecs % 1000L ) * 1000L;
- if ( t->time.tv_usec >= 1000000L )
- {
- t->time.tv_sec += t->time.tv_usec / 1000000L;
- t->time.tv_usec %= 1000000L;
- }
- t->hash = hash( t );
- /* Add the new timer to the proper active list. */
- l_add( t );
-
- return t;
+ Timer *t;
+
+ if (free_timers != (Timer *) 0)
+ {
+ t = free_timers;
+ free_timers = t->next;
+ }
+ else
+ {
+ t = (Timer *) malloc (sizeof (Timer));
+ if (t == (Timer *) 0)
+ return (Timer *) 0;
+ }
+
+ t->timer_proc = timer_proc;
+ t->client_data = client_data;
+ t->msecs = msecs;
+ t->periodic = periodic;
+ if (nowP != (struct timeval *) 0)
+ t->time = *nowP;
+ else
+ (void) gettimeofday (&t->time, (struct timezone *) 0);
+ t->time.tv_sec += msecs / 1000L;
+ t->time.tv_usec += (msecs % 1000L) * 1000L;
+ if (t->time.tv_usec >= 1000000L)
+ {
+ t->time.tv_sec += t->time.tv_usec / 1000000L;
+ t->time.tv_usec %= 1000000L;
+ }
+ t->hash = hash (t);
+ /* Add the new timer to the proper active list. */
+ l_add (t);
+
+ return t;
}
-struct timeval*
-tmr_timeout( struct timeval* nowP )
+struct timeval *
+tmr_timeout (struct timeval *nowP)
{
- long msecs;
- static struct timeval timeout;
-
- msecs = tmr_mstimeout( nowP );
- if ( msecs == INFTIM )
- return (struct timeval*) 0;
- timeout.tv_sec = msecs / 1000L;
- timeout.tv_usec = ( msecs % 1000L ) * 1000L;
- return &timeout;
+ long msecs;
+ static struct timeval timeout;
+
+ msecs = tmr_mstimeout (nowP);
+ if (msecs == INFTIM)
+ return (struct timeval *) 0;
+ timeout.tv_sec = msecs / 1000L;
+ timeout.tv_usec = (msecs % 1000L) * 1000L;
+ return &timeout;
}
long
-tmr_mstimeout( struct timeval* nowP )
+tmr_mstimeout (struct timeval *nowP)
{
- int h;
- int gotone;
- long msecs, m;
- register Timer* t;
-
- gotone = 0;
- msecs = 0; /* make lint happy */
- /* Since the lists are sorted, we only need to look at the
- ** first timer on each one.
- */
- for ( h = 0; h < HASH_SIZE; ++h )
- {
- t = timers[h];
- if ( t != (Timer*) 0 )
- {
- m = ( t->time.tv_sec - nowP->tv_sec ) * 1000L +
- ( t->time.tv_usec - nowP->tv_usec ) / 1000L;
- if ( ! gotone )
- {
- msecs = m;
- gotone = 1;
- }
- else if ( m < msecs )
- msecs = m;
- }
- }
- if ( ! gotone )
- return INFTIM;
- if ( msecs <= 0 )
- msecs = 0;
- return msecs;
+ int h;
+ int gotone;
+ long msecs, m;
+ register Timer *t;
+
+ gotone = 0;
+ msecs = 0; /* make lint happy */
+ /* Since the lists are sorted, we only need to look at the
+ ** first timer on each one.
+ */
+ for (h = 0; h < HASH_SIZE; ++h)
+ {
+ t = timers[h];
+ if (t != (Timer *) 0)
+ {
+ m = (t->time.tv_sec - nowP->tv_sec) * 1000L +
+ (t->time.tv_usec - nowP->tv_usec) / 1000L;
+ if (!gotone)
+ {
+ msecs = m;
+ gotone = 1;
+ }
+ else if (m < msecs)
+ msecs = m;
+ }
+ }
+ if (!gotone)
+ return INFTIM;
+ if (msecs <= 0)
+ msecs = 0;
+ return msecs;
}
void
-tmr_run( struct timeval* nowP )
+tmr_run (struct timeval *nowP)
{
- int h;
- Timer* t;
- Timer* next;
-
- for ( h = 0; h < HASH_SIZE; ++h )
- for ( t = timers[h]; t != (Timer*) 0; t = next )
- {
- next = t->next;
- /* Since the lists are sorted, as soon as we find a timer
- ** that isn't ready yet, we can go on to the next list.
- */
- if ( t->time.tv_sec > nowP->tv_sec ||
- ( t->time.tv_sec == nowP->tv_sec &&
- t->time.tv_usec > nowP->tv_usec ) )
- break;
- (t->timer_proc)( t->client_data, nowP );
- if ( t->periodic )
- {
- /* Reschedule. */
- t->time.tv_sec += t->msecs / 1000L;
- t->time.tv_usec += ( t->msecs % 1000L ) * 1000L;
- if ( t->time.tv_usec >= 1000000L )
- {
- t->time.tv_sec += t->time.tv_usec / 1000000L;
- t->time.tv_usec %= 1000000L;
- }
- l_resort( t );
- }
- else
- tmr_cancel( t );
- }
+ int h;
+ Timer *t;
+ Timer *next;
+
+ for (h = 0; h < HASH_SIZE; ++h)
+ for (t = timers[h]; t != (Timer *) 0; t = next)
+ {
+ next = t->next;
+ /* Since the lists are sorted, as soon as we find a timer
+ ** that isn't ready yet, we can go on to the next list.
+ */
+ if (t->time.tv_sec > nowP->tv_sec ||
+ (t->time.tv_sec == nowP->tv_sec &&
+ t->time.tv_usec > nowP->tv_usec))
+ break;
+ (t->timer_proc) (t->client_data, nowP);
+ if (t->periodic)
+ {
+ /* Reschedule. */
+ t->time.tv_sec += t->msecs / 1000L;
+ t->time.tv_usec += (t->msecs % 1000L) * 1000L;
+ if (t->time.tv_usec >= 1000000L)
+ {
+ t->time.tv_sec += t->time.tv_usec / 1000000L;
+ t->time.tv_usec %= 1000000L;
+ }
+ l_resort (t);
+ }
+ else
+ tmr_cancel (t);
+ }
}
void
-tmr_reset( struct timeval* nowP, Timer* t )
+tmr_reset (struct timeval *nowP, Timer * t)
{
- t->time = *nowP;
- t->time.tv_sec += t->msecs / 1000L;
- t->time.tv_usec += ( t->msecs % 1000L ) * 1000L;
- if ( t->time.tv_usec >= 1000000L )
- {
- t->time.tv_sec += t->time.tv_usec / 1000000L;
- t->time.tv_usec %= 1000000L;
- }
- l_resort( t );
+ t->time = *nowP;
+ t->time.tv_sec += t->msecs / 1000L;
+ t->time.tv_usec += (t->msecs % 1000L) * 1000L;
+ if (t->time.tv_usec >= 1000000L)
+ {
+ t->time.tv_sec += t->time.tv_usec / 1000000L;
+ t->time.tv_usec %= 1000000L;
+ }
+ l_resort (t);
}
void
-tmr_cancel( Timer* t )
+tmr_cancel (Timer * t)
{
- /* Remove it from its active list. */
- l_remove( t );
- /* And put it on the free list. */
- t->next = free_timers;
- free_timers = t;
- t->prev = (Timer*) 0;
+ /* Remove it from its active list. */
+ l_remove (t);
+ /* And put it on the free list. */
+ t->next = free_timers;
+ free_timers = t;
+ t->prev = (Timer *) 0;
}
void
-tmr_cleanup( void )
+tmr_cleanup (void)
{
- Timer* t;
-
- while ( free_timers != (Timer*) 0 )
- {
- t = free_timers;
- free_timers = t->next;
- free( (void*) t );
- }
+ Timer *t;
+
+ while (free_timers != (Timer *) 0)
+ {
+ t = free_timers;
+ free_timers = t->next;
+ free ((void *) t);
+ }
}
void
-tmr_destroy( void )
+tmr_destroy (void)
{
- int h;
+ int h;
- for ( h = 0; h < HASH_SIZE; ++h )
- while ( timers[h] != (Timer*) 0 )
- tmr_cancel( timers[h] );
- tmr_cleanup();
+ for (h = 0; h < HASH_SIZE; ++h)
+ while (timers[h] != (Timer *) 0)
+ tmr_cancel (timers[h]);
+ tmr_cleanup ();
}
53 timers.h
View
@@ -38,69 +38,70 @@
** can use it for whatever, and it gets passed to the callback when the
** timer triggers.
*/
-typedef union {
- void* p;
- int i;
- long l;
+typedef union
+{
+ void *p;
+ int i;
+ long l;
} ClientData;
-extern ClientData JunkClientData; /* for use when you don't care */
+extern ClientData JunkClientData; /* for use when you don't care */
/* The TimerProc gets called when the timer expires. It gets passed
** the ClientData associated with the timer, and a timeval in case
** it wants to schedule another timer.
*/
-typedef void TimerProc( ClientData client_data, struct timeval* nowP );
+typedef void TimerProc (ClientData client_data, struct timeval *nowP);
/* The Timer struct. */
-typedef struct TimerStruct {
- TimerProc* timer_proc;
- ClientData client_data;
- long msecs;
- int periodic;
- struct timeval time;
- struct TimerStruct* prev;
- struct TimerStruct* next;
- int hash;
+typedef struct TimerStruct
+{
+ TimerProc *timer_proc;
+ ClientData client_data;
+ long msecs;
+ int periodic;
+ struct timeval time;
+ struct TimerStruct *prev;
+ struct TimerStruct *next;
+ int hash;
} Timer;
/* Initialize the timer package. */
-extern void tmr_init( void );
+extern void tmr_init (void);
/* Set up a timer, either periodic or one-shot. Returns (Timer*) 0 on errors. */
-extern Timer* tmr_create(
- struct timeval* nowP, TimerProc* timer_proc, ClientData client_data,
- long msecs, int periodic );
+extern Timer *tmr_create (struct timeval *nowP, TimerProc * timer_proc,
+ ClientData client_data, long msecs, int periodic);
/* Returns a timeout indicating how long until the next timer triggers. You
** can just put the call to this routine right in your select(). Returns
** (struct timeval*) 0 if no timers are pending.
*/
-extern struct timeval* tmr_timeout( struct timeval* nowP );
+extern struct timeval *tmr_timeout (struct timeval *nowP);
/* Returns a timeout in milliseconds indicating how long until the next timer
** triggers. You can just put the call to this routine right in your poll().
** Returns INFTIM (-1) if no timers are pending.
*/
-extern long tmr_mstimeout( struct timeval* nowP );
+extern long tmr_mstimeout (struct timeval *nowP);
/* Run the list of timers. Your main program needs to call this every so often,
** or as indicated by tmr_timeout().
*/
-extern void tmr_run( struct timeval* nowP );
+extern void tmr_run (struct timeval *nowP);
/* Reset the clock on a timer, to current time plus the original timeout. */
-extern void tmr_reset( struct timeval* nowP, Timer* timer );
+extern void tmr_reset (struct timeval *nowP, Timer * timer);
/* Deschedule a timer. Note that non-periodic timers are automatically
** descheduled when they run, so you don't have to call this on them.
*/
-extern void tmr_cancel( Timer* timer );
+extern void tmr_cancel (Timer * timer);
/* Clean up the timers package, freeing any unused storage. */
-extern void tmr_cleanup( void );
+extern void tmr_cleanup (void);
/* Cancel all timers and free storage, usually in preparation for exitting. */
-extern void tmr_destroy( void );
+extern void tmr_destroy (void);
#endif /* _TIMERS_H_ */
Please sign in to comment.
Something went wrong with that request. Please try again.