Skip to content
Browse files

* Added the debianization related files and also the cipher shorcut

  names header file.
  • Loading branch information...
1 parent 3a7ecba commit bd7f2ffe8325b447f54d5f4940005c90a61d8dee @perusio committed Aug 8, 2011
Showing with 2,595 additions and 3 deletions.
  1. +9 −0 debian/README
  2. +7 −0 debian/README.Debian
  3. +11 −0 debian/changelog
  4. +1 −0 debian/compat
  5. +20 −0 debian/control
  6. +56 −0 debian/copyright
  7. +2 −0 debian/dirs
  8. +1 −0 debian/docs
  9. +168 −0 debian/httpload.1.ronn
  10. +2,285 −0 debian/patches/debian-changes-0.1-1
  11. +1 −0 debian/patches/series
  12. +13 −0 debian/rules
  13. +1 −0 debian/source/format
  14. +4 −3 httpload.c
  15. +16 −0 ssl_ciphers.h
View
9 debian/README
@@ -0,0 +1,9 @@
+http-load for Debian
+--------------------
+
+<this file describes information about the source package, see Debian policy
+manual section 4.14. You WILL either need to modify or delete this file>
+
+
+
+
View
7 debian/README.Debian
@@ -0,0 +1,7 @@
+http-load for Debian
+--------------------
+
+This is a fork of the http_load tool by Jef Jef Poskanzer. The web
+page of this project is at: http://www.acme.com/software/http_load.
+
+ -- António P. P. Almeida <appa@perusio.net>, Sat, 6 Aug 2011 04:35:25 +0100
View
11 debian/changelog
@@ -0,0 +1,11 @@
+httpload (0.1-1) unstable; urgency=low
+
+ * Initial release.
+
+ * Debianized the package and fixed some errors.
+
+ * Updated the manpage now based on a ronn file.
+
+ * Provided basic README.
+
+ -- António P. P. Almeida <appa@perusio.net> Sun, 07 Aug 2011 15:12:03 +0100
View
1 debian/compat
@@ -0,0 +1 @@
+8
View
20 debian/control
@@ -0,0 +1,20 @@
+Source: httpload
+Section: web
+Priority: optional
+Maintainer: António P. P. Almeida <appa@perusio.net>
+Build-Depends: debhelper (>= 8.0.0)
+Standards-Version: 3.9.2
+Homepage: http://github.perusio.org/httpload
+Vcs-Git: git://github.com/perusio/httpload.git
+
+Package: httpload
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: Multiprocessing webserver load test tool.
+ httpload 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.
+ .
+ This version is SSL and IPv6 enabled.
View
56 debian/copyright
@@ -0,0 +1,56 @@
+This package was first debianized and packaged by Antonio P. P. Almeida
+<appa@perusio.net> on Sun Aug 7 15:33:35 2011.
+
+It was downloaded from https://github.com/perusio/httpload. It's based
+on http_load by Jef Poskanzer.
+
+Copyright:
+ (C) 2006 Jef Poskanzer <jef@mail.acme.com>
+ (C) 2011 Antonio P. P. Almeida <appa@perusio.net>
+
+License: BSD-3-Clause
+ 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.
+ 3. Neither the name of the University nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+ .
+ THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+
+The Debian packaging is Copyright (C) 2011 António P. P. Almeida
+<appa@perusio.net> and is licensed under the MIT license.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
2 debian/dirs
@@ -0,0 +1,2 @@
+usr/share/man/man1
+usr/bin
View
1 debian/docs
@@ -0,0 +1 @@
+README.md
View
168 debian/httpload.1.ronn
@@ -0,0 +1,168 @@
+httpload -- multiprocessing http test client
+=============================================
+
+## SYNOPSIS
+
+`httpload` [ options ] url_file
+
+ options: [ -checksum ] [ -throttle ] [ -proxy host:port ] [ -verbose ] [ -timeout secs ] [ -seed seed\_value ]
+ [ -sip sip\_file ] [ -cipher str ] ( -parallel N | -rate N [-jitter] ) ( -fetches N | -seconds N )
+
+
+## DESCRIPTION
+
+httpload 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.
+
+
+## OPTIONS
+
+ `-checksum` do checksums on the files fetched, to make sure they came across
+ ok. The checksums are computed the first time each URL gets fetched,
+ and then recomputed and compared on each subsequent fetch. Without
+ the `-checksum` flag only the byte count is checked.
+
+ `-throttle` flag tells httpload to throttle the data consumtion rate
+ to 33.6Kbps, to simulate access by modem users.
+
+ `-proxy host:port` run httpload through a web proxy.
+
+ `-verbose` writes a progress reports every minute on stderr.
+
+ `-timeout secs` time to wait on idle connections before giving
+ up. The default is 60 seconds.
+
+ `-seed integer` seed for random number generation.
+
+ `-sip sip_file` specifies a file containing numeric IP
+ addresses (not hostnames), one per line. These get used randomly as
+ the **source** address of connections. They must be real routable
+ addresses on your machine, created with ifconfig, in order for this to
+ work. The advantage of using this option is you can make one client
+ machine look like a whole bank of machines, as far as the server
+ knows.
+
+ `-cipher str` use the specified cipher suite. By default, httpload will
+ negotiate the highest security that the server has available, which is
+ often higher (and slower) than typical browsers will negotiate.
+
+ An example of a cipher set might be "CAMELLIA128-SHA" - this will run
+ considerably faster than the default. In addition to specifying a raw
+ cipher string, there are three built-in cipher sets accessible by
+ keywords:
+
+ + fastsec - fast security - CAMELLIA128-SHA
+ + highsec - high security - AES256-SHA
+ + paranoid - ultra high security - DHE-RSA-AES256-SHA
+
+Of course, not all servers are guaranteed to implement these
+combinations. The choice done is for ciphers SSLv3 HIGH strength. You
+should not be using anything SSLv2 or any MD5 or anonymous based
+cipher if you care about the privacy and integrity of your TLS
+sessions.
+
+ `-parallel N` start making N requests in parallel.
+
+ `-rate nbr_connections` start the specified number of connections
+ each second.
+
+ `-jitter` introduce a 10% jitter on the above specified rate.
+
+ `-fetches N` exit after N fetches have been completed.
+
+ `-seconds secs` exit after after the specified time has elapsed.
+
+The url_file is just a list of URLs, one per line. The URLs that get
+fetched are chosen randomly from this file.
+
+All flags may be abbreviated to a single letter.
+
+A start specifier, either `-parallel` or `-rate`, **must** be
+specified. The same applies to the end specifier, either `-fetches` or
+`-seconds`.
+
+Note that while the end specifier is obeyed precisely, the start
+specifier is only approximate. If you use the `-rate` option,
+httpload will make its best effort to start connections at that rate,
+but may not succeed. And if you use the `-parallel` option, httpload
+will attempt to keep that many simultaneous connections going, but may
+fail to keep up if the server is very fast.
+
+## EXAMPLES
+
+Load the server hitting the URLs defined in `test_urls.txt` during 60
+seconds creating 10 new connections each second.
+
+ httpload -rate 10 -seconds 60 test_urls.txt
+
+ 599 fetches, 1 max parallel, 25757 bytes, in 60 seconds
+ 43 mean bytes/connection
+ 9.98333 fetches/sec, 429.283 bytes/sec
+ msecs/connect: 0.144903 mean, 0.219 max, 0.054 min
+ msecs/first-response: 0.240796 mean, 0.333 max, 0.095 min
+ HTTP response codes:
+ code 200 -- 599
+
+
+Make 10 requests in parallel during 10 seconds using the URLs
+defined in `test_urls.txt`.
+
+ httpload -parallel 10 -seconds 10 test_urls.txt
+
+ 16191 fetches, 10 max parallel, 696213 bytes, in 10.0079 seconds
+ 43 mean bytes/connection
+ 1617.83 fetches/sec, 69566.6 bytes/sec
+ msecs/connect: 0.171786 mean, 1.208 max, 0.032 min
+ msecs/first-response: 0.453629 mean, 1.836 max, 0.233 min
+ HTTP response codes:
+ code 200 -- 16191
+
+Make 10 requests in parallel using the AES256-SHA cipher during 10
+seconds to the URLs defined in `secure_urls.txt`.
+
+ httpload -cipher highsec -parallel 10 -seconds 10 secure_urls.txt
+
+ 318 fetches, 10 max parallel, 13674 bytes, in 10.1627 seconds
+ 43 mean bytes/connection
+ 31.2908 fetches/sec, 1345.5 bytes/sec
+ msecs/connect: 30.9694 mean, 286.512 max, 0.033 min
+ msecs/first-response: 197.975 mean, 326.568 max, 28.925 min
+ HTTP response codes:
+ code 200 -- 318
+
+Make 10 requests in parallel, creating 10 connections each second
+using the ECDHE-RSA-AES256-SHA cipher during 10 seconds to the URLs
+defined in `secure_urls.txt`.
+
+ httpload -cipher ECDHE-RSA-AES256-SHA -rate 10 -parallel 10 -seconds 10 secure_urls.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
+
+## COPYRIGHT
+
+httpload is Copyright (C) 2006 Jef Poskanzer under the terms of a BSD
+License.
+
+## SEE ALSO
+
+ httping(1), siege(1), thttpd(8), openssl(1)
+
+## AVAILABILITY
+
+The latest version of httpload is available at the git repository for
+this project is at: https://github.com/perusio/httpload.
+
+## AUTHOR
+
+httpload was written by Jef Poskanzer \<jef@mail.acme.com\>.
+
+Manual page written by Antonio P. P. Almeida \<appa@perusio.net\>, for
+the Debian project (and may be used by others).
View
2,285 debian/patches/debian-changes-0.1-1
@@ -0,0 +1,2285 @@
+Description: Upstream changes introduced in version 0.1-1
+ This patch has been created by dpkg-source during the package build.
+ Here's the last changelog entry, hopefully it gives details on why
+ those changes were made:
+ .
+ httpload (0.1-1) unstable; urgency=low
+ .
+ * Initial release.
+ .
+ * Debianized the package and fixed some errors.
+ .
+ * Updated the manpage now based on a ronn file.
+ .
+ * Provided basic README.
+ .
+ The person named in the Author field signed this changelog entry.
+Author: António P. P. Almeida <appa@perusio.net>
+
+---
+The information above should follow the Patch Tagging Guidelines, please
+checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here
+are templates for supplementary fields that you might want to add:
+
+Origin: <vendor|upstream|other>, <url of original patch>
+Bug: <url in upstream bugtracker>
+Bug-Debian: http://bugs.debian.org/<bugnumber>
+Bug-Ubuntu: https://launchpad.net/bugs/<bugnumber>
+Forwarded: <no|not-needed|url proving that it has been forwarded>
+Reviewed-By: <name and email of someone who approved the patch>
+Last-Update: <YYYY-MM-DD>
+
+--- httpload-0.1.orig/FILES
++++ httpload-0.1/FILES
+@@ -1,10 +1,11 @@
++FILES
++httpload.1
++httpload.c
+ Makefile
+-README
+-http_load.1
+-http_load.c
+ make_test_files
+ port.h
++README
++test_url.txt
+ timers.c
+ timers.h
+ version.h
+-FILES
+--- httpload-0.1.orig/Makefile
++++ httpload-0.1/Makefile
+@@ -1,54 +1,84 @@
+-# Makefile for http_load
+-
+-# CONFIGURE: If you are using a SystemV-based operating system, such as
+-# Solaris, you will need to uncomment this definition.
+-#SYSV_LIBS = -lnsl -lsocket -lresolv
+-
+-# CONFIGURE: If you want to compile in support for https, uncomment these
+-# definitions. You will need to have already built OpenSSL, available at
+-# http://www.openssl.org/ Make sure the SSL_TREE definition points to the
+-# tree with your OpenSSL installation - depending on how you installed it,
+-# it may be in /usr/local instead of /usr/local/ssl.
+-#SSL_TREE = /usr/local/ssl
+-#SSL_DEFS = -DUSE_SSL
+-#SSL_INC = -I$(SSL_TREE)/include
+-#SSL_LIBS = -L$(SSL_TREE)/lib -lssl -lcrypto
+-
+-
+-BINDIR = /usr/local/bin
+-MANDIR = /usr/local/man/man1
+-CC = gcc -Wall
+-CFLAGS = -O $(SRANDOM_DEFS) $(SSL_DEFS) $(SSL_INC)
+-#CFLAGS = -g $(SRANDOM_DEFS) $(SSL_DEFS) $(SSL_INC)
+-LDFLAGS = -s $(SSL_LIBS) $(SYSV_LIBS)
+-#LDFLAGS = -g $(SSL_LIBS) $(SYSV_LIBS)
+-
+-all: http_load
+-
+-http_load: http_load.o timers.o
+- $(CC) $(CFLAGS) http_load.o timers.o $(LDFLAGS) -o http_load
+-
+-http_load.o: http_load.c timers.h port.h
+- $(CC) $(CFLAGS) -c http_load.c
+-
+-timers.o: timers.c timers.h
+- $(CC) $(CFLAGS) -c timers.c
+-
+-install: all
+- rm -f $(BINDIR)/http_load
+- cp http_load $(BINDIR)
+- rm -f $(MANDIR)/http_load.1
+- cp http_load.1 $(MANDIR)
++# The GPL applies to this program.
++# In addition, as a special exception, the copyright holders give
++# permission to link the code of portions of this program with the
++# OpenSSL library under certain conditions as described in each
++# individual source file, and distribute linked combinations
++# including the two.
++# You must obey the GNU General Public License in all respects
++# for all of the code used other than OpenSSL. If you modify
++# file(s) with this exception, you may extend this exception to your
++# version of the file(s), but you are not obligated to do so. If you
++# do not wish to do so, delete this exception statement from your
++# version. If you delete this exception statement from all source
++# files in the program, then also delete it here.
++
++TARGET=httpload
++
++WFLAGS=-Wall -W
++OFLAGS=-O2
++CFLAGS+=$(WFLAGS) $(OFLAGS) -DUSE_IPV6 -DUSE_SSL
++
++PACKAGE=$(TARGET)-$(VERSION)
++PREFIX=/usr
++BINDIR=$(PREFIX)/bin
++MANDIR=$(PREFIX)/share/man
++DOCDIR=$(PREFIX)/share/doc/$(TARGET)
++
++INSTALL=install
++INSTALLDIR=$(INSTALL) -m 0755 -d
++INSTALLBIN=$(INSTALL) -m 0755
++INSTALLMAN=$(INSTALL) -m 0644
++INSTALLDOC=$(INSTALL) -m 0644
++STRIP=/usr/bin/strip
++RMDIR=/bin/rm -rf
++MKDIR=/bin/mkdir
++ARCHIVE=/bin/tar cf -
++COMPRESS=/bin/gzip -9
++
++OBJS=httpload.o timers.o
++
++MANS=httpload.1
++
++DOCS=README.md
++
++ifeq ($(SSL),no)
++CFLAGS+=-DNO_SSL
++else
++LDFLAGS+=-lssl -lcrypto
++endif
++
++ifeq ($(DEBUG),yes)
++CFLAGS+=-D_DEBUG -g
++LDFLAGS+=-g
++endif
++
++ifeq ($(ARM),yes)
++CC=arm-linux-gcc
++endif
++
++all: $(TARGET)
++
++$(TARGET): $(OBJS)
++ $(CC) $(WFLAGS) $(OBJS) $(LDFLAGS) -o $(TARGET)
++
++install: $(TARGET)
++ $(INSTALLDIR) $(DESTDIR)/$(BINDIR)
++ $(INSTALLBIN) $(TARGET) $(DESTDIR)/$(BINDIR)
++ $(INSTALLDIR) $(DESTDIR)/$(MANDIR)/man1
++ $(INSTALLMAN) $(MANS) $(DESTDIR)/$(MANDIR)/man1
++ $(INSTALLDIR) $(DESTDIR)/$(DOCDIR)
++ $(INSTALLDOC) $(DOCS) $(DESTDIR)/$(DOCDIR)
++ifneq (DEBUG,yes)
++ $(STRIP) $(DESTDIR)/$(BINDIR)/$(TARGET)
++endif
+
+ clean:
+- rm -f http_load *.o core core.* *.core
++ $(RMDIR) $(OBJS) $(TARGET) *~ core
+
+-tar:
+- @name=`sed -n -e '/define VERSION /!d' -e 's,.*http_load ,http_load-,' -e 's,",,p' version.h` ; \
+- rm -rf $$name ; \
+- mkdir $$name ; \
+- tar cf - `cat FILES` | ( cd $$name ; tar xfBp - ) ; \
+- chmod 644 $$name/Makefile ; \
+- tar cf $$name.tar $$name ; \
+- rm -rf $$name ; \
+- gzip $$name.tar
++package: clean
++ # source package
++ $(RMDIR) $(PACKAGE)*
++ $(MKDIR) $(PACKAGE)
++ $(INSTALLDOC) *.c *.h Makefile version $(MANS) $(DOCS) $(PACKAGE)
++ $(ARCHIVE) $(PACKAGE) | $(COMPRESS) > $(PACKAGE).tgz
++ $(RMDIR) $(PACKAGE)
+--- httpload-0.1.orig/version.h
++++ httpload-0.1/version.h
+@@ -1,8 +1,8 @@
+-/* version.h - version define for http_load */
++/* version.h - version define for httpload */
+
+ #ifndef _VERSION_H_
+ #define _VERSION_H_
+
+-#define VERSION "http_load 12mar2006"
++#define VERSION "httpload 0.1"
+
+ #endif /* _VERSION_H_ */
+--- /dev/null
++++ httpload-0.1/test_url.txt
+@@ -0,0 +1 @@
++http://machaut:89
+--- /dev/null
++++ httpload-0.1/httpload.c
+@@ -0,0 +1,1908 @@
++/* 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 <time.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;
++ int start_seqn;
++} 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;
++
++/* log connections that take longer than this */
++static long long log_connect_usecs_thresh = 3*1000*1000;
++
++#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;
++ char* seed_arg = NULL;
++#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], "-seed", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
++ {
++ seed_arg = argv[++argn];
++ }
++ 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. */
++ if (seed_arg) {
++ if (do_verbose)
++ (void) fprintf( stderr, "Seed %d\n", atoi( seed_arg ) );
++ srandom( atoi( seed_arg ) );
++ }
++ else {
++#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 )
++ {
++ 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 );
++ ++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:
++ 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] [-seed N] [-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;
++
++ /* 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;
++ }
++ ++fetches_started;
++ connections[cnum].start_seqn = 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 )
++ {
++ 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 )
++ {
++ 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;
++ }
++ }
++
++ /* 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 )
++ {
++ connections[cnum].conn_state = CNST_CONNECTING;
++ return;
++ }
++ else
++ {
++ perror( urls[url_num].url_str );
++ (void) close( connections[cnum].conn_fd );
++ return;
++ }
++ }
++
++ /* Connect succeeded instantly, so handle it now. */
++ (void) gettimeofday( nowP, (struct timezone*) 0 );
++ handle_connect( cnum, nowP, 0 );
++}
++
++
++static void
++handle_connect( int cnum, struct timeval* nowP, int double_check )
++{
++ int url_num;
++ char buf[5000];
++ int r;
++ unsigned bytes;
++
++ url_num = connections[cnum].url_num;
++ if ( double_check )
++ {
++ /* Check to make sure the non-blocking connect succeeded. */
++ int err;
++ socklen_t errlen;
++
++ if ( connect(
++ connections[cnum].conn_fd,
++ (struct sockaddr*) &connections[cnum].sa,
++ connections[cnum].sa_len ) < 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://%s:%d%s 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://%s:%d%s 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 %s 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" );
++
++ if (bytes >= sizeof(buf)-1) {
++ (void) fprintf(
++ stderr, "request skipped, too long: %s\n", urls[url_num].filename );
++ close_connection( cnum );
++ return;
++ }
++
++ /* 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;
++}
++
++
++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;
++ long long connect_usecs = -1;
++ long long response_usecs = -1;
++ int sock_port = -1;
++
++ if (1) {
++ struct sockaddr_in sa;
++ socklen_t sa_len = sizeof(sa);
++ if (getsockname(connections[cnum].conn_fd, (struct sockaddr *)&sa, &sa_len) == 0)
++ sock_port = ntohs(sa.sin_port);
++ else printf("getsockname: %s\n", strerror(errno));
++ }
++
++#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 )
++ {
++ 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 )
++ {
++ 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];
++
++ /* log slow connections */
++ if (1 && connect_usecs > log_connect_usecs_thresh) {
++ printf("%d\t%d\t%d\t%lld\t%lld\t%d\n",
++ connections[cnum].start_seqn,
++ 0,
++ sock_port,
++ connect_usecs, response_usecs,
++ connections[cnum].http_status
++ );
++ /* automatically use this as the new threshold */
++ log_connect_usecs_thresh = connect_usecs;
++ }
++
++ 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 );
++ }
++}
+--- /dev/null
++++ httpload-0.1/httpload.1
+@@ -0,0 +1,159 @@
++.TH http_load 1 "15 November 2001"
++.SH NAME
++http_load - multiprocessing http test client
++.SH SYNOPSIS
++.B http_load
++.RB [ -checksum ]
++.RB [ -throttle ]
++.RB [ -proxy
++.IR host:port ]
++.RB [ -verbose ]
++.RB [ -timeout
++.IR secs ]
++.RB [ -sip
++.IR sip_file ]
++.RB [ -cipher
++.IR str ]
++.RI (
++.BI -parallel
++.IR N
++.RI |
++.BI -rate
++.IR N
++.RB [ -jitter ]
++.RI )
++.RI (
++.BI -fetches
++.IR N
++.RI |
++.BI -seconds
++.IR N
++.RI )
++.I url_file
++.SH DESCRIPTION
++.PP
++.I 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.
++.PP
++The -checksum flag tells
++.I http_load
++to do checksums on the files fetched, to make sure they came across ok.
++The checksums are computed the first time each URL gets fetched,
++and then recomputed and compared on each subsequent fetch.
++Without the -checksum flag only the byte count is checked.
++.PP
++The -throttle flag tells
++.I http_load
++to throttle its consumption of data to 33.6Kbps, to simulate access
++by modem users.
++.PP
++The -proxy flag lets you run http_load through a web proxy.
++.PP
++The -verbose flag tells
++.I http_load
++to put out progress reports every minute on stderr.
++.PP
++The -timeout flag specifies how long to wait on idle connections before
++giving up.
++The default is 60 seconds.
++.PP
++The -sip flag lets you specify a file containing numeric IP addresses
++(not hostnames), one per line.
++These get used randomly as the *source* address of connections.
++They must be real routable addresses on your machine, created
++with ifconfig, in order for this to work.
++The advantage of using this option is you can make one client machine
++look like a whole bank of machines, as far as the server knows.
++.PP
++The -cipher flag is only available if you have SSL support compiled in.
++It specifies a cipher set to use.
++By default, http_load will negotiate the highest security that the server
++has available, which is often higher (and slower) than typical browsers
++will negotiate.
++An example of a cipher set might be "RC4-MD5" - this will run
++considerably faster than the default.
++In addition to specifying a raw cipher string, there are three built-in
++cipher sets accessible by keywords:
++.nf
++ * fastsec - fast security - RC4-MD5
++ * highsec - high security - DES-CBC3-SHA
++ * paranoid - ultra high security - AES256-SHA
++.fi
++Of course, not all servers are guaranteed to implement these combinations.
++.PP
++One start specifier, either -parallel or -rate, is required.
++-parallel tells
++.I http_load
++to keep that many parallel fetches going simultaneously.
++-rate tells
++.I http_load
++to start that many new connections each second.
++If you use the -rate start specifier, you can also give the -jitter
++flag, telling
++.I http_load
++to vary the rate randomly by about 10%.
++.PP
++One end specifier, either -fetches or -seconds, is required.
++-fetches tells
++.I http_load
++to quit when that many fetches have been completed.
++-seconds tells
++.I http_load
++to quit after that many seconds have elapsed.
++.PP
++The url_file is just a list of URLs, one per line.
++The URLs that get fetched are chosen randomly from this file.
++.PP
++All flags may be abbreviated to a single letter.
++.PP
++Note that while the end specifier is obeyed precisely, the start specifier
++is only approximate.
++If you use the -rate flag,
++.I http_load
++will make its best effort to start connections at that rate, but may
++not succeed.
++And if you use the -parallel flag,
++.I http_load
++will attempt to keep that many simultaneous connections going, but
++may fail to keep up if the server is very fast.
++.PP
++Sample run:
++.nf
++ % http_load -rate 2 -seconds 300 urls
++ 591 fetches, 8 max parallel, 5.33606e+06 bytes, in 300 seconds
++ 9028.87 mean bytes/connection
++ 1.97 fetches/sec, 17786.9 bytes/sec
++ msecs/connect: 28.8932 mean, 44.243 max, 24.488 min
++ msecs/first-response: 63.5362 mean, 81.624 max, 57.803 min
++ HTTP response codes:
++ code 200 -- 591
++.fi
++.SH "SEE ALSO"
++http_ping(1)
++.SH AUTHOR
++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.
+--- /dev/null
++++ httpload-0.1/sip.txt
+@@ -0,0 +1 @@
++127.0.0.1:6760
+--- /dev/null
++++ httpload-0.1/test_url_s.txt
+@@ -0,0 +1 @@
++https://machaut:489
View
1 debian/patches/series
@@ -0,0 +1 @@
+debian-changes-0.1-1
View
13 debian/rules
@@ -0,0 +1,13 @@
+#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+%:
+ dh $@
View
1 debian/source/format
@@ -0,0 +1 @@
+3.0 (quilt)
View
7 httpload.c
@@ -45,6 +45,7 @@
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/err.h>
+#include "ssl_ciphers.h"
#endif
#include "version.h"
@@ -347,11 +348,11 @@ main( int argc, char** argv )
{
cipher = argv[++argn];
if ( strcasecmp( cipher, "fastsec" ) == 0 )
- cipher = "CAMELLIA128-SHA";
+ cipher = FASTSEC_SSL_CIPHER;
else if ( strcasecmp( cipher, "highsec" ) == 0 )
- cipher = "AES256-SHA";
+ cipher = HIGHSEC_SSL_CIPHER;
else if ( strcasecmp( cipher, "paranoid" ) == 0 )
- cipher = "DHE-RSA-AES256-SHA";
+ cipher = PARANOID_SSL_CIPHER;
}
#endif /* USE_SSL */
else if ( strncmp( argv[argn], "-proxy", strlen( argv[argn] ) ) == 0 && argn + 1 < argc )
View
16 ssl_ciphers.h
@@ -0,0 +1,16 @@
+/**
+ * @file ssl_ciphers.h
+ * @author António P. P. Almeida <appa@perusio.net>
+ * @date Mon Aug 8 00:22:09 2011
+ *
+ * @brief Header file for defining the cipher shortcuts of httpload.
+ *
+ */
+#ifndef _SSL_CIPHERS_H_
+#define _SSL_CIPHERS_H_
+
+#define FASTSEC_SSL_CIPHER "CAMELLIA128-SHA"
+#define HIGHSEC_SSL_CIPHER "AES256-SHA"
+#define PARANOID_SSL_CIPHER "DHE-RSA-AES256-SHA"
+
+#endif /* _SSL_CIPHERS_H_ */

0 comments on commit bd7f2ff

Please sign in to comment.
Something went wrong with that request. Please try again.