File httperf-0.9.0+git.20201206.obscpio of Package httperf

07070100000000000081A40000000000000000000000015FCD0002000000FC000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/.gitignore/data/
/nbproject/
*.o
*.a
.deps/
Makefile
Makefile.in
/src/httperf
/depcomp
/install-sh
/stamp-h1
/config.status
/config.h
/config.guess
/config.sub
/configure
/aclocal.m4
/compile
/ltmain.sh
/config.h.in
/config.log
/missing
/libtool
/autom4te.cache/07070100000001000081ED0000000000000000000000015FCD0002000001D1000000000000000000000000000000000000002300000000httperf-0.9.0+git.20201206/AUTHORSDavid Mosberger
  main sources
  wsesslog crash fix

Martin Arlitt
  SSL support
  timer reset fix

Ted Bullock
  compiler warning fixes
  gnu auto tool build system
  man page --wset typo fix regarding --uri
  unallocated timer memory leak fix

Tai Jin
  zero-length content fix

Stephane Eranian
  wlog URI generator

Richard Carter
  wsesslog workload generator

Andrew Hately
  variable rates of period between sessions/requests

Adrian Chadd
  port to libevent07070100000002000081ED0000000000000000000000015FCD000200003B1C000000000000000000000000000000000000002500000000httperf-0.9.0+git.20201206/COPYRIGHT		    GNU GENERAL PUBLIC LICENSE
		       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

		    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

  5. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded.  In such case, this License incorporates
the limitation as if written in the body of this License.

  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

			    NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

		     END OF TERMS AND CONDITIONS
07070100000003000081ED0000000000000000000000015FCD000200007A4F000000000000000000000000000000000000002500000000httperf-0.9.0+git.20201206/ChangeLog2011-02-10  Ted Bullock <tbullock@comlore.com>

	* Replaced bundled version of getopt with flavour from openbsd
	  (smaller code and more liberal redistribution license)

2011-02-07  Ted Bullock <tbullock@comlore.com>

	* Adjusted help text 

2009-12-16  Ted Bullock <tbullock@comlore.com>

	* Remove libevent: Remove libevent (for now) from HEAD due to stability

2008-10-03  Ted Bullock <tbullock@canada.com>

	* src/timer.c: Fix build on opensolaris with Sun Studio compiler
	* src/lib/heap.c: Fix build on opensolaris with Sun Studio compiler

2008-07-14  Adrian Chadd <adrian@creative.net.au>

	* Assorted .c and .h files:  Use libevent for the communications backend

2008-04-11  Ted Bullock <tbullock@canada.com>

	* configure.ac, src/Makefile.am: Make automake compilation optional,
	use --enable-idleconn. This will require libevent.

2008-01-31  Ted Bullock <tbullock@canada.com>

	* src/idleconn.c: Complete rewrite to use libevent backend; slight
	performance improvement thanks to use of libevent notification system

2007-11-16  Ted Bullock <tbullock@canada.com>

	* configure.ac: Enable libevent dependancy
	* src/httperf.c: Print libevent version event notification mechanism

2007-11-15  Ted Bullock <tbullock@canada.com>

	* autogen.sh: Remove in lieu of autoreconf -i

2007-09-23  Ted Bullock <tbullock@canada.com>

	* httperf.c: Add a check to return value of timer_schedule
	* timer.c: Fix a crash associated with rescheduling a timer
	* lib/list.c: NEW Generic linked list data structure implementation
	* lib/list.h: NEW Generic linked list data structure API

2007-09-18  Ted Bullock <tbullock@canada.com>

	* timer.c: Remove the last of the old timer logic code and replace it
	with simpler functionality

2007-09-11  Ted Bullock <tbullock@canada.com>

	* timer.c and timer.h: Use new heap and queue data 
	structures to re-implement timers to use memory caching and 
	try to fix memory leak associated with timers not being freed

2007-09-11  Ted Bullock <tbullock@canada.com>

	* lib/heap.c, lib/heap.h: New generic priority queue data structure

2007-07-15  Ted Bullock <tbullock@canada.com>

	* lib/queue.c, lib/queue.h: New generic queue data structure

2007-07-15  Ted Bullock <tbullock@canada.com>

	* lib/generic_types.h: New File - Migrate various generic types
	out of httperf.h in preparation for a generic queue data structure
	* httperf.h: Removed unneccessary #include commands to minimize
	header inclusion blocks and duplicates
	* *.c: Added #include statements to use new generic_types.h

2007-07-15  Ted Bullock <tbullock@canada.com>

	* timer.c, timer.h, httperf.c: Fixed memory leak associated with timers not being
	freed

2007-06-03  Ted Bullock <tbullock@canada.com>

	* event.c and event.h: Moved to localevent.c and localevent.h for integration with
	libevent (which has a default header of event.h)

2007-06-03  Ted Bullock <tbullock@canada.com>

	* configure.ac: Add configuration to use libevent

2007-06-03  Ted Bullock <tbullock@canada.com>

	* configure.ac: Remove unnecessary configuration cruft and configure httperf
	to be built in c99 mode

2007-05-27  Ted Bullock <tbullock@canada.com>

	* man/httperf.1: Fixed man page typo for --wset option (needed --uri, not --wset)

2007-05-27  Andrew Hateley <andrew.hateley@gmail.com>

	* man/httperf.1: New documentation regarding the variable periodic rates
	* src/httperf.c: New functionality for variable periodic rates
	* src/httperf.h: ditto
	* src/timer.c: ditto

2007-03-31  Ted Bullock <tbullock@canada.com>

	* *.c and *.h: Adjusted license to explicitly list HP copyright as
	Copyright 2000-2007 Hewlett-Packard Company

2007-02-07  Ted Bullock  <tbullock@canada.com>

	* AUTHORS: wsseslog crash fix was actually written by David Mosberger

2007-02-07  Ted Bullock  <tbullock@canada.com>

	* AUTHORS: Removed Durval Menezes from AUTHORS file since his fix
	has been re-written by Theodore Bullock

2007-02-07  Ted Bullock  <tbullock@canada.com>

	* *.c and *.h: Slightly adjusted license to explicitly point out AUTHORS file
	as official list of authors

2007-02-07  Ted Bullock  <tbullock@canada.com>

	* httperf.1: Added correction to man page to include a missing
	--wsess identification

2007-01-26  Ted Bullock  <tbullock@canada.com>

	* getopt.c and getopt1.c: Re-Added the to the build process
	httperf now compiles on Linux, OpenBSD, FreeBSD, Solaris, HP-UX 11i
	with cc and gcc and (with cc and gcc)

2007-01-26  Ted Bullock  <tbullock@canada.com>

	* Makefile.am: Revised Makefile.am for src directories to explicitly include the
	.h files to allow make distcheck to pass

2007-01-26  Ted Bullock  <tbullock@canada.com>

	* getopt.c and getopt1.c: Removed from build process

	* httperf.c: Wrapped call to getopt.h in pre-processor checks

2007-01-25  Ted Bullock  <tbullock@canada.com>

	* wsesslog.c: changed a sprintf call to the more secure snprintf

	* getopt.c: changed a sprintf call to the more secure snprintf

2007-01-20  Ted Bullock  <tbullock@canada.com>

	* autogen.sh: Included the shell script autogen.sh to compile the necessary 
	autotool components.
	
	* autogen.sh: Removed redundant configuration files from CVS, regenerate these
	with the autogen.sh script (recommend autoconf 2.60 and above)

	* ttest.c: Removed the ttest.c file from CVS

2007-01-16  Ted Bullock <tbullock@canada.com>
	* Build System: Updated build system to a more typical directory
	layout and wrote missing makefile.am files for automake

	* idleconn: Now installed along with the httperf executable

	* ttest: Remove program from build

2007-01-02  Ted Bullock <tbullock@canada.com>

	*License: Included openssl linking exception

2006-12-08  Ted Bullock <tbullock@canada.com>
	
	* Bug Fixes: Added a number of fixes from the mailing lists
	over the last 6 years.

	* compiler warnings: Compiler warnings in core.c and wsesslog.c
	
	* configuration: Fixes a problem with the "configure" setup
	where the "--prefix=" option wasn't being obeyed

	* zero length: Fixes bug in http.c (parse_header)with zero
	length content

	* Host Header: Identify machine by FQD rather than hostname

	* Persistent Connections: Added Persistent connections to HTTP 1.0
	requests

	* Robust alloca.h: Robust way to handle the header file that
	defines the alloca function <alloca.h> for linux and stdlib.h for
	BSD and others

	* Time Discrepancy: Fixed a discrepancy between the data produced
	by httperf and the documentation in regards to timing.

	* wsesslog Crash: Fixed a problem with the wsesslog crashing
	httperf due to an unhandled session failure from a burdened server

2000-10-31  David Mosberger  <davidm@hpl.hp.com>

	* Version 0.8 released.

	* core.c (do_recv) [!HAVE_SSL]: Avoid referncing param.use_ssl.
	(do_send): Ditto.

	* core.c (core_ssl_connect): Print error message if SSL connect
	fails.  ERR_print_errors_fp() may not print anything if there
	error is due to, e.g., trying to connect to a non-existent port.

	* httperf.c (main): Initialize port to -1.  Once parameters have
	been processed, port defaults to 443 if SSL is in use, 80
	otherwise.

2000-10-30  David Mosberger  <davidm@hpl.hp.com>

	* httperf.man: Update man-page with new options (based on Martin's
	descriptions.

	* httperf.c (usage): Mention --port.

2000-10-27  David Mosberger  <davidm@hpl.hp.com>

	* httperf.man: Mention request summary in description of
	--print-request.

	* stat/print_reply.c (print_request): Print request header/content
	summary line.

	* httperf.c (usage): Bring in sync with implemented options
	(Martin's patch).
	(longopts): Order in alphabetical order (Martin's patch).

2000-10-25  David Mosberger  <davidm@hpl.hp.com>

	* stat/print_reply.c (flush_print_buf): New.
	(Call_Private_Data): New.
	(CALL_PRIVATE_DATA): New.
	(call_private_data_offset): New.
	(print_buf): Rewrite to support line-buffering and new output
	format.
	(call_destroyed): New.
	(print_reply_hdr): New (based on Martin's original version).
	(send_raw_data): Ditto.
	(recv_raw_data): Ditto.
	(recv_stop): Print reply summary (based on Martin's version).
	(init): Update to support the new print options.

	* httperf.c: Replace --print-replies/--print-request with more
	general versions of these options.

	* httperf.man: Add info on new --print-reply and --print-request
	options.

2000-10-24  David Mosberger  <davidm@hpl.hp.com>

	* httperf.c (main): Initialize ssl_ctx.
	(main): Use SSLv3_client_method() instead of SSLv23_client_method().
	The latter doesn't work.

	* httperf.h (ssl_ctx): New global variable.

	* conn.h [HAVE_SSL]: Drop unnecessary includes of <openssl/rsa.h>,
	<openssl/crypto.h>, <openssl/x509.h>, and <openssl/pem.h>.  Drop
	per-connect "ctx" member (it's global now).
	
	* conn.c (conn_init): Create SSL connection info and set cipher
	list (if necessary).

	* core.c (core_ssl_connect): Don't create SSL connection info
	here (done in conn.c now).  Always tell SSL about the
	file descriptor of the socket.

	* sess.c (sess_deinit) [HAVE_SSL]: Free SSL info if it exists.

	* gen/session.c (create_conn): If param.ssl_reuse is in effect,
	re-use SSL session if we have one or update session info with
	newly created SSL session.

	* httperf.c [HAVE_SSL]: Include <openssl/rand.h>.
	(main): Call RAND_seed() with a zero buffer.

2000-10-23  David Mosberger  <davidm@hpl.hp.com>

	* conn.c (conn_init): Don't initialize conn->ssl to zero---that
	has been done by object_new() already.

	* gen/session.c (create_conn): If reusing SSL session ids, create
	SSL structure and save SSL info in session structure.

	* sess.c (sess_deinit) [HAVE_SSL]: Free SSL session if it's
	non-NULL.

	* httperf.h: Declare "use_ssl" only if HAVE_SSL is defined.
	Add ssl_no_reuse and ssl_cipher_list.

	* httperf.c (struct longopts): Add --ssl-no-reuse and ssl-ciphers.
	(main): Handle --ssl-cipher_list and --ssl-no-reuse options.

2000-10-19  David Mosberger  <davidm@hpl.hp.com>

	* core.c (core_ssl_connect): Bring debug messages in sync with
	rest of httperf.  If s->ssl exists already, skip right to
	SSL_connect().  If SSL_connect() returns SSL_ERROR_WANT_READ or
	SSL_ERROR_WANT_WRITE, mark the appropriate fd set as active and
	return immediately to wait for more data.  Use
	ERR_print_errors_fp() to print SSL-related errors.
	(core_loop): Check for S_CONNECTING state before doing anything
	else.

	* README: Document configuration option "--enable-debug".

	* configure.in: Add support for --enable-debug.

2000-10-16  David Mosberger  <davidm@hpl.hp.com>

	* configure.in: Add AC_TYPE_LONG_LONG check (defines u_wide as
	"unsigned long long" if the compiler can grok it, "unsigned long"
	otherwise).

	* aclocal.m4: New file.

	* stat/basic.c: Include "config.h".
	(struct basic): Change type of hdr_bytes_received,
	reply_bytes_received, and footer_bytes_received from
	size_t to u_wide.
	(dump): Change type of total_size to u_wide.

2000-10-11  David Mosberger  <davidm@hpl.hp.com>

	* httperf.h (VERSION): Change to 0.8beta.

	* configure.in: New file.
	* Makefile.in: Ditto.
	* stat/Makefile.in: Ditto.
	* gen/Makefile.in: Ditto.
	* lib/Makefile.in: Ditto.

2000-08-29  David Mosberger  <davidm@hpl.hp.com>

	* httperf.c (main): Call core_exit() on a SIGINT.

2000-08-21  David Mosberger  <davidm@hpl.hp.com>

	* core.c (core_init): Bound maximum number of open files to
	FD_SETSIZE.

2000-04-25  David Mosberger  <davidm@hpl.hp.com>

	* httperf.c (main): Add a call to fpsetmask(0) to get non-finite
	IEEE arithmetic to work on older versions of FreeBSD.

1998-12-21  David Mosberger  <davidm@hpl.hp.com>

	* Version 0.7 released.

	* Makefile (install): New make target.

	* httperf.h (VERSION): Define as "0.7".

	* gen/wsesslog.c (parse_config): Remove blank in sscanf() format
	since this caused problems on FreeBSD and NetBSD.  This bug was
	reported by Stephane Eranian.

1998-12-08  David Mosberger  <david_mosberger@hp.com>

	* httperf.c (struct longopts): Add option "method".
	(usage): Document option --method.
	(main): Handle --method.

	* httperf.h (struct Cmdline_Params): New member "method".

	* gen/misc.c: Renamed from add_header.c.  This file now implements
	both --add-header and --method.

1998-11-23  David Mosberger  <david_mosberger@hp.com>

	* httperf.c (longopts): New option "add-header".
	(main): Handle --add-header.

	* httperf.h (struct Cmdline_Params): New member
	"additional_header".

	* gen/add_header.c: New file.

1998-09-18  David Mosberger  <davidm@hpl.hp.com>

	* gen/session.c (call_done):
	(conn_failed): Call create_conn() instead of sess_failure() in
	case param.retry_on_failure is set and the connection has not been
	successful (received at least one good response)

	* gen/wsess.c (call_destroyed): Call sess_dec_ref() only if
	session didn't fail.

1998-09-14  David Mosberger  <davidm@hpl.hp.com>

	* README: Replace "session" with "connection" and "call" with
	"request" to reflect current terminology (reported by Christian
	Petit).  Also fixed various other typos and grammatical problems.

1998-09-11  David Mosberger  <davidm@hpl.hp.com>

	* gen/session.c (call_done): When param.retry_on_failure is set,
	re-issue call instead of causing session to fail.

	* httperf.c (usage): Add --retry-on-failure option.
	(struct longopts): Ditto.

	* httperf.man: Document --retry-on-failure option.

1998-07-23  David Mosberger  <davidm@hpl.hp.com>

	* stat/sess_stat.c: Use sess->failed instead of maintaining
	private session-failure flag.

1998-07-21  David Mosberger  <davidm@hpl.hp.com>

	* sess.c (sess_failure): Don't signal EV_SESS_FAILED more than
	once per session.

	* core.c (do_send): Clear c->recvq_next.  This is necessary now
	because the same call may be issued multiple times (due to
	connection failures).  Hence there is no longer a guarantee that
	c->recvq_next has been cleared by object_new().
	(do_send): Rename s to conn and c to call.

1998-07-20  David Mosberger-Tang  <David.Mosberger@acm.org>

	* core.c (do_recv): Set s->state to S_REPLY_DONE when done with an
	HTTP/1.0 response (mirrors code in http_process_reply_bytes()).
	(do_recv): No need to check for nread == 0.

	* stat/basic.c (init): Print basic.conn_lifetime_min only if
	basic.num_lifetimes > 0.

	* core.c (core_close): Fix typo (call -> all).

1998-07-08  David Mosberger  <davidm@hpl.hp.com>

	* core.c (do_send): Set s->state to S_REPLY_STATUS only if this is
	the first send---we don't want to interfere with the parsing of an
	in-progress reply.

1998-06-19  David Mosberger  <davidm@hpl.hp.com>

	* Version 0.6 released.

	* gen/wsesslog.c (issue_calls): Limit `to_create' by the number
	of calls that the session can queue.

	* gen/session.c (session_max_qlen): New function.
	(session_current_qlen): Ditto.
	(max_qlen): New variable.

	* httperf.man: Fix typos.

1998-06-18  David Mosberger  <davidm@hpl.hp.com>

	* gen/session.c (MAX_CONN): Up MAX_CONN to 16.

	* object.c (object_new): panic() instead of assert(0) when
	encountering an unknown object type.

	* gen/wsesslog.c (user_think_time_expired): Remove stale comment.

	* gen/sess_cookie.c (struct Call_Private_Data): New member cookie.
	(call_recv_hdr): Remove padding bogosity.
	(call_issue): Copy cookie from session to call object.

1998-06-17  David Mosberger  <davidm@hpl.hp.com>

	* timer.c (timer_cancel): Instead of an assertion failure, print a
	message on stderr when timer_cancel(t) is called from within a
	timeout handler t.

	* Feedback from Dick Carter:
	* gen/wsesslog.c (parse_config): Remove stale comment.
	* httperf.h: Fix typo in comment for min_iat.
	* httperf.c (usage): Fix typo in --period args.
	* call.c (call_append_request_header): Fix typo in debug message.

1998-06-09  David Mosberger  <davidm@hpl.hp.com>

	* stat/print_reply.c: New file.
	* stat/sess_stat.c: Ditto.

	* stat/wsess_stat.c: Remove.

	* stat/basic.c: Move stats macros into stats.h.
	(RATE_INTERVAL): Remove.
	(interval_start): Ditto.
	(perf_sample): Rename from sample_rate().
	(conn_fail): Count EPIPE as a `connection reset' error.
	(init): Register perf_sample as handler for EV_PERF_SAMPLE.
	Remove scheduling of performance sampling timer.
	(dump): Print connection lifetime histogram only if verbose > 1.
	Print average connection length in number of replies/connection.
	
	* stat/stats.h: New file.

	* stat/basic.c: Add copyright message.

	* stat/Makefile (libstat.a): Mention sess_stat.o and
	print_reply.o.

	* object.c: New file.
	* object.h: Ditto.
	* sess.c: Ditto.
	* sess.h: Ditto.

	* httperf.h (VERSION): Up version number to 0.6.
	(object_is_conn): Remove (now in object.h).
	(object_is_call): Ditto.
	(enum Object_Type): Ditto.
	(struct Object): Ditto.
	(object_type_size): Ditto.
	(struct Cmdline_Params): New mebers max_piped, max_conns,
	print_replies, and session_cookies.

	* httperf.c (perf_sample_start): New variable to keep track of
	when latest sample interval started.
	(struct longopts): Add entries for max-connections,
	max-piped-calls, print-replies, and session-cookies.
	(usage): Update usage() message.
	(perf_sample): New function.
	(main): Remove uri_wsesslog generator.  Add sess_stat stats
	collector.  Handle --max-connections, --max-piped, --print-replies
	and --session-cookies. Start performance sampling timer.

	* http.c (get_line): Use sizeof (s->line_buf) instead of
	s->line_buf_size.
	(parse_status_line): Do strcmp() to find out whether call
	was a HEAD request.  Ignore `100 Continue' replies.

	* gen/wsesslog.h: Remove.

	* gen/wsess.c: Modify to take advantage of session manager
	(gen/session.c).

	* gen/wsesslog.c: Modify to take advantage of session manager
	and merge with uri_wsesslog.c.

	* gen/uri_wlog.c (set_uri): Use call_set_uri().
	* gen/uri_wset.c (set_uri): Ditto.

	* gen/uri_fixed.c (uri_len): New variable.
	(set_uri): Use call_set_uri().
	(init): Initialize uri_len.

	* gen/session.c: New file.
	* gen/session.h: Ditto.

	* gen/sess_cookie.c: New file (based on wsess.c).

	* gen/conn_rate.c: File renamed from sess_rate.c.

	* gen/call_seq.c: Modify to take advantage of reference counting.

	* gen/Makefile (libgen.a): Remove uri_wsesslog (it's part of
	wsesslog now).

	* gen/uri_wsesslog.c: Remove.
	* gen/uri_wsesslog.h: Ditto.
	
	* event.h (Event_Type): New member EV_PERF_SAMPLE for performance
	sampling (invoked once every 5 seconds).  Rename EV_USER_NEW to
	EV_SESS_NEW and EV_USER_DESTROYED to EV_SESS_DESTROYED.  New
	member EV_SESS_FAILED that is signalled when a session fails.

	* core.c (do_send): Remove left-over assert()ion.
	(do_send): Preserve io vector element that is being sent in
	iov_saved.  Call call_dec_ref() when the connection is closing.
	(recv_done): Rename `c' to `call' and invoke call_dec_ref().
	(do_recv): Call conn_failure() only if errno != EAGAIN (for Linux).
	(core_send): Add `conn' argument.  Fill in req.iov[IE_HOST] and
	req.iov[IE_PROTL] only (rest has been filled in by call_set_* macros).
	Call call_inc_ref() once the call is ready.
	(core_close): Rename `s' to `conn'.  Destroy pending calls calls
	on the sendq and recvq by calling call_dec_ref().  Call conn_dec_ref()
	at the end.
	(core_loop): Obtain a reference to the connection object for the
	duration of read & write processing.

	* conn.h: Include <object.h>.
	(MAX_HDR_LINE_LEN): New macro.
	(struct Conn): Remove member first_owned and line_size.  Replace
	line_buf pointer with line_buf array.
	(conn_init): Declare.
	(conn_deinit): Ditto.
	(conn_new): New macro.
	(conn_inc_ref): Ditto.
	(conn_dec_ref): Ditto.

	* conn.c: Remove include of <errno.h> and <event.h>.
	(conn_new): Remove.
	(conn_destroy): Ditto.
	(conn_init): New function.
	(conn_deinit): Ditto.

	* call.h: (enum HTTP_Method): Remove.
	Include <object.h>.
	(struct Call): Remove members loc_len, loc, uri_len, uri,
	orig_host, extra_hdrs, contents_len, contents.  New member:
	iov_saved.
	(call_init): Declare.
	(call_deinit): Ditto.
	(call_new): New macro.
	(call_inc_ref): Ditto.
	(call_dec_ref): Ditto.
	(call_set_method): Ditto.
	(call_set_location): Ditto.
	(call_set_uri): Ditto.
	(call_set_contents): Ditto.

	* call.c (call_method_name): Remove.
	(free_call_list): Remove.
	(call_new): Remove.
	(call_init): New function.
	(call_deinit): Ditto.
	(call_append_request_header): Fill req.iov[IE_FIRST_HEADER +
	num_hdrs] instead of req.extra_hdrs[num_hdrs].

	* Makefile (HTTPERF): Mention object and sess.

1998-05-29  David Mosberger  <davidm@hpl.hp.com>

	* Version 0.5 released.

	* core.c (core_send): Fix setup of IE_METHOD and insert blank
	character in IE_BLANK.

	* call.h (enum IOV_Element): Add IE_BLANK.

1998-05-27  David Mosberger  <davidm@hpl.hp.com>

	* timer.c: Rename t_next to t_curr.
	(timer_tick): Set t_next _after_ invoking the callback.  This
	allows the callback to cancel all other timers (except itself).
	(timer_cancel): Add assertion to ensure that timer_cancel() is
	not called from within the handler of a given timer.

	* httperf.h (enum Dist_Type): New type.
	(struct Rate_Info): Ditto.
	(struct Cmdline_Params): Change type of RATE member from Time to
	Rate_Info.  New member WSESSLOG.
	(panic): Declare.

	* httperf.h (VERSION): Up version number to 0.5.

	* httperf.c: Include <ctype.h> and <stdarg.h>.  New options:
	close-with-reset, period, wsesslog.
	(usage): Add usage message for new options.
	(panic): New function.
	(main): Declare wsesslog, uri_wsesslog, and wsesslog_stat.
	Initialize param.rate.dist with DETERMINISTIC.  Handle --period
	and --wsesslog options.  Fix typo: user think time is 3rd, not 4th
	parameter in wsess parameter list.
	(main): Call timer_tick() after initializating the statistic
	collectors and workload generators so our notion of the current
	time is reasonably accurate.

	* gen/wsess.c (req_create): Use call_append_request_header().
	(start): Initialize rg_sess.rate with &param.rate.

	* gen/uri_wsesslog.h: New file (by Dick Carter).
	* gen/uri_wsesslog.c: Ditto.
	* gen/wsesslog.h: Ditto.
	* gen/wsesslog.c: Ditto.

	* gen/uri_wlog.c: Drop inclusion of <stdarg.h>.
	Move panic() from here to httperf.c.

	* gen/sess_rate.c (start): Initialize rg.rate with &param.rate.

	* gen/rate.h: New member next_interarrival_time.  New member
	xsubi.  Replace PERIOD member with RATE member.
	(rate_generator_stop): Drop second argument (RATE).

	* gen/rate.c: Include <math.h>, <stdio.h>, and <stdlib.h>.
	(next_arrival_time_det): New function.
	(next_arrival_time_uniform): Ditto.
	(next_arrival_time_exp): Ditto.
	(tick): Invoke (*next_arrival_time)() to determine next delay.
	(rate_generator_start): Initialize rg->xsubi based on
	param.client.id.  Initialize rg->next_interarrival_time based on
	desired distribution.

	* gen/Makefile (libgen.a): Mention wsesslog.o and uri_wsesslog.o

	* core.c (core_connect): Don't rely on realloc(0, SIZE) doing the
	right thing.
	(core_connect): Clear newly allocated sd_to_conn elements after
	allocating them.
	(core_send): Use call_method_name.  Copy in defined
	c->req.extra_hdrs[i] iovecs.  Copy in IE_CONTENT iovec.

	* call.h: New value HM_LEN.
	(MAX_EXTRA_HEADERS): New macro.  Replace IE_HEADERS with
	IE_FIRST_HEADER and IE_LAST_HEADER.  Make extra_hdrs an array of
	`struct iovec'.  New member num_extra_hdrs.
	(call_append_request_header): Declare new function.

	* call.c: New array.
	(call_append_request_header): New function.

1998-05-26  David Mosberger  <davidm@hpl.hp.com>

	* COPYRIGHT: Add 1998 as copyright year.

	* stat/basic.c (dump): If no connections have been created, print
	0.0 for minimm connection lifetime.  This avoids a core-dump under
	FreeBSD, which seems to have a problem printing IEEE infinity.

1998-05-22  David Mosberger  <davidm@hpl.hp.com>

	* call.c (method_name): New variable.

1998-04-27  David Mosberger  <davidm@hpl.hp.com>

	* core.c (do_recv): Ignore EAGAIN errors---Linux 2.0.32 sometimes
	returns EAGAIN on a read() if a previous write() resulted in
	EAGAIN.

	* AUTHORS: New file.

1998-03-24  David Mosberger  <davidm@hpl.hp.com>

	* gen/wsess.c (call_done): Also fall back to non-persistent
	session if c->req.version < HTTP/1.1.

1998-03-18  David Mosberger  <davidm@hpl.hp.com>

	* httperf.c (usage): Fix typo: --recv-buffer -> --send-buffer.

	* gen/wsess.c (dump): Rename "Sess" to "Session" in statistics
	output.

	* core.c (core_connect): Turn on closing with RESET only when
	param.close_with_reset is set.

1998-03-17  David Mosberger  <davidm@hpl.hp.com>

	* stat/basic.c (dump): In "Total" line, rename "call" to "req".

	* Version 0.41 released.

	* core.c: Include <sys/resource.h> after <sys/types.h> to get it
	to compile under FreeBSD (which seems to have a broken libc).

	* stat/basic.c (dump): Complete renaming of "session" to "call".
	Rename "Call" to "Request".

1998-03-16  David Mosberger  <davidm@hpl.hp.com>

	* Version 0.4 released.

	* httperf.h (VERSION): Change version number to 0.4.

	* httperf.c: (longopts): Add --http-version.
	(main): Initialize param.http_version to HTTP/1.  Support parsing
	of --http-version option.  Echo --http-version option if version
	other than 1.1 is selected.

	* httperf.h (struct Cmdline_Params): New member HTTP_VERSION.

	* core.c (core_send): Support generation of HTTP/1.0 requests.

	* Patches by Stephane Eranian:

	* httperf.man: Mention --wlog parameter.

	* Makefile (clean): Use $(MAKE) instead of make.

1998-03-12  David Mosberger  <davidm@hpl.hp.com>

	* httperf.c: Renamed Session to Conn to avoid confusion between
	user sessions and TCP connections.

1998-03-02  David Mosberger  <davidm@hpl.hp.com>

	* gen/wsess.c (user_destroy): Signal EV_USER_DESTROYED.
	(user_create): Signal EV_USER_NEW.
	(start): Register rate-generator with event EV_USER_DESTROYED
	instead of EV_SESSION_DESTROYED.

	* event.h: New events EV_USER_NEW and EV_USER_DESTROYED.
	* event.c: Ditto.

1998-02-27  David Mosberger  <davidm@hpl.hp.com>

	* session.h: Remove unused first_call member.

1998-02-26  David Mosberger  <davidm@hpl.hp.com>

	* gen/wsess.c (call_recv_hdr): Always check for Connection: and
	Set-Cookie: headers (not just for first call).
	(req_create): Modify to support bursts.
	(user_connect): Ditto.
	(sess_destroyed): Ditto.
	(call_destroyed): Ditto.

1998-02-25  David Mosberger  <davidm@hpl.hp.com>

	* core.c (lffs): Fix lffs() so it returns 0 when no bits at all
	are set in a 64 bit long.
	(set_active): Set recvq timeout if timeout==0.0 (the old code failed
	to timeout connections that were waiting for replies).

1998-02-23  David Mosberger  <davidm@hpl.hp.com>

	* Version 0.3 released.

	* timer.c (timer_now_forced): New function.

	* httperf.h (Cmdline_params): New member burst_len.

	* httperf.c: New option --burst-length.  At present, only
	gen/call_seq.c supports this (gen/wsess does not).

	* gen/wsess.c (struct User): New members: found_close_hdr,
	conn_start, call_start, and cookie.
	(req_create): Insert cookie as extra header if there is one.
	(call_recv_hdr): New function.  Looks for "Connection: close" and
	"Set-Cookie:" headers.

	* gen/call_seq.c (Sess_Private_Data): New member "num_finished".
	(issue_calls): Add support for --burst-len parameter.
	(call_destroyed): Ditto.

	* core.c (SYSCALL): Call timer_now_forced() instead of timer_now().
	(core_connect): Keep track of longest back-to-back burst.
	(core_connect): Set SO_LINGER option so that TCP RST is sent when
	closing the socket.  This avoids the client melting down on
	HP-UX 10.20 due to a large number of TCP connections in TIME_WAIT
	state (HP-UX is broken as far as bind()/connect() is concerned when
	there are many TCP control blocks).
	(core_exit): Print maximum connect burst length.

	* core.c (max_burst_len): New variable to keep track of longest
	back-to-back burst generated (large bursts are usually a sign of
	the client being overloaded).

1998-02-13  David Mosberger  <davidm@hpl.hp.com>

	* stat/basic.c (sess_fail): Print one unexpected error number per
	execution, independent of DBG.

1998-02-06  David Mosberger  <davidm@hpl.hp.com>

	* core.c (do_send): Maintain sendq_tail & recvq_tail.
	(core_send): Ditto.
	(recv_done): Ditto.

	* session.h (Session): New members SENDQ_TAIL and RECVQ_TAIL.

	* core.c (set_active): Set watchdog timer to the minimum of the
	timeout of the request being sent and that being received (if such
	requests exist).
	(do_send): Set c->timeout.
	(core_connect): Ditto.
	(core_send): Ditto.

	* call.h: Replace (unused) WATCHDOG member with TIMEOUT.

	* timer.c (timer_schedule): Handle negative delays gracefully.

1998-01-28  David Mosberger  <davidm@hpl.hp.com>

	* Version 0.2 released to SixFlags team.

1998-01-26  David Mosberger  <davidm@hpl.hp.com>

	* gen/call_seq.c (sess_closed): New function.
	(init): Register sess_closed() so we notice when session closes
	prematurely.

1998-01-21  David Mosberger  <davidm@hpl.hp.com>

	* httperf.c (main): Don't quit after printing version
	information---it's useful to run a test and have this info as
	well.

1998-01-14  David Mosberger  <davidm@hpl.hp.com>

	* stat/basic.c (dump): Add num_sock_reset to total error count.

1998-01-12  David Mosberger  <davidm@hpl.hp.com>

	* stat/basic.c (struct basic): New member num_sock_reset.
	(sess_fail): Increment num_sock_reset for ECONNRESET.
	(dump): Print num_sock_reset.

	* core.c (do_recv): Call session_failure() when reading < 0 bytes.

	* stat/basic.c (sess_fail): Print error number when getting an
	unexpected errno and DBG > 0.

1998-01-09  David Mosberger  <davidm@hpl.hp.com>

	* httperf.c (main): Print only one newline (second newline is
	printed by stat collectors).

	* stat/basic.c (dump): Print session lifetime histogram first
	(so it goes right after the rate output).

1998-01-08  David Mosberger  <davidm@hpl.hp.com>

	* core.c (port_get): Recode so port numbers are returned in
	strictly increasing order (until a wraparound after 65535 occurs).
	This avoids problems with NT 4.0 which disallows reusing the port
	pair of a properly shut down TCP connection (which creates
	problems when testing an NT server with clients running on HP-UX
	or similar platforms).

1998-01-07  David Mosberger  <davidm@hpl.hp.com>

	* gen/sess_rate.c: Adapt to use rate.c.

	* timer.c (timer_schedule): Adjust "delay" if realtime has grown
	bigger than next_tick (meaning that "spoke" is behind).

	* call.c (call_destroy): prev wasn't updated in the search loop.

1998-01-06  David Mosberger  <davidm@hpl.hp.com>

	* stat/basic.c (dump): Print minmum rate as zero if there are no
	rate samples.

1997-12-23  David Mosberger  <davidm@hpl.hp.com>

	* stat/basic.c (struct basic): Add members num_reply_rates,
	reply_rate_sum, reply_rate_sum2, reply_rate_min, and
	reply_rate_max.
	(interval_start, num_replies): New vars.
	(sample_rate): New function to sample reply rate.
	(init): Schedule rate sampler.
	(dump): Print min, avg, max, and stddev of sampled reply rate.

	* httperf.c: Add --client and --think-timeout options.

1997-12-17  David Mosberger  <davidm@hpl.hp.com>

	* http.c (parse_data): Return TRUE to indicate that all bytes
	(of present chunk) have been received (instead of setting
	state to S_REPLY_DONE).

	* httperf.h (ALIGN): Fix macro (need AND with _complement_...).

	* http.c (xfer_chunked): Fix typo: check for content_bytes >=
	content_length (not vice versa).

	* uri_wset.c (set_uri): Fix uri_len computation.

	* sess_rate.c (destroyed): Fix typo: test for param.rate <= 0 (not
	<0).

07070100000004000081ED0000000000000000000000015FCD000200000045000000000000000000000000000000000000002700000000httperf-0.9.0+git.20201206/Makefile.amAUTOMAKE_OPTIONS = foreign
SUBDIRS = src man
ACLOCAL_AMFLAGS = -I m4
07070100000005000081ED0000000000000000000000015FCD000200000B01000000000000000000000000000000000000002000000000httperf-0.9.0+git.20201206/NEWS* New in version 0.9.1:
** timer re-write to reduce memory and fix memory leaks 
** Generic data structures and implementations (heap, queue, linked list)
** New options (see man-page for details):
	--period=vT1,D1,T2,D2...Tn,Dn
** Complete re-write of idleconn.c to use libevent notification system
** idleconn is now optional (--enable-idleconn)

* New in version 0.9.0:
** Re-Factored build system now builds on the following platforms
   HP-UX 11i (64-bit PA-RISC and IA-64)
   Red Hat Enterprise Linux AS (AMD64 and IA-64)
   SUSE Linux 10.1 (i386)
   openSUSE 10.2 (i386)
   OpenBSD 4.0 (i386)
   FreeBSD 6.0 (AMD64)
   Solaris 8 (UltraSparc 64-bit)

* New in version 0.8.1:
** Numerous bug fixes. See ChangeLog for details

* New in version 0.8:
** httperf is now released under the GNU General Public License (GPL).
** Preliminary support for SSL (Secure Socket Layer).  See README for details.
** New options (see man-page for details):
	--print-reply (replaced --print-replies)
	--print-request
	--ssl
	--ssl-ciphers
	--ssl-no-reuse

* New in version 0.7:
** New options (see man-page for details):
	--add-header
	--method
	--retry-on-failure
** Bug fixes
	- fixed some segfaults in the session workload generator
	- specifying option --session-cookie when not using a session
	  workload generator now prints an error message instead of
	  core dumping

* New in version 0.6:
** New options (see man-page for details):
	--max-connections
	--max-piped-calls
	--print-replies
	--session-cookies
** Cookie support now must be requested explicitly when using a session-based
   workload generator.  To do this, specify option --session-cookie.

* New in version 0.5:

** Normal connection closing is the default again.  To request closing TCP
   connections with a RESET, specify option --close-with-reset.
** --wsesslog option added to support log-file based session
   specification (contributed by Dick Carter).
** --period option added to allow a more flexible way to specify
   session/connection interarrival time.  Unlike the --rate
   argument, this allows deterministic (fixed), uniform, and
   exponentially distributed interarrival times (contributed by Dick
   Carter).
** Various bug fixes (see ChangeLog for details).

* New in version 0.41:
** In basic statistic, rename "call" to "request".

* New in version 0.4:
** Option --http-version can be used to select the HTTP protocol version
   used in sending requests. 1.0 and 1.0 are the only allowed values
   for this option at this point.
** What used to be called a "session" is now called a "connection".  This
   reduces confusion between TCP connections and user sessions.
** Stephane's log-file based URL generated has been added.
** The session workload generator now supports the --burst-length
   parameter to generate bursty session.
07070100000006000081ED0000000000000000000000015FCD000200004919000000000000000000000000000000000000002500000000httperf-0.9.0+git.20201206/README.md
# httperf

httperf is a tool for measuring web server performance. It provides a flexible facility for generating various HTTP workloads and for measuring server performance.

The focus of httperf is not on implementing one particular benchmark but on providing a robust, high-performance tool that facilitates the construction of both micro- and macro-level benchmarks. The three distinguishing characteristics of httperf are its robustness, which includes the ability to generate and sustain server overload, support for the HTTP/1.1 and SSL protocols, and its extensibility to new workload generators and performance measurements.

## Building httperf

This release of httperf is using the standard GNU configuration
mechanism.  The following steps can be used to build it:

In this example, SRCDIR refers to the httperf source directory.  The
last step may have to be executed as "root".

First, some tools which are required for the build process need to be installed.

	$ sudo apt install automake libtool

Then, run the following steps in order to build. Note that some of these might have to be executed as "root", i.e., with sudo.

	$ autoconf
	$ libtoolize --force
	$ autoreconf -i
	$ automake
	$ ./configure
	$ make
	$ sudo make install

This step may need to be run as root:

	make install

Since httperf 0.9.1, the the idleconn program is no longer built by
default.  Using the configure option --enable-idleconn will instruct
the build system to compile the tool.

To build httperf with debug support turned on, invoke configure with
option "--enable-debug".

By default, the httperf binary is installed in /usr/local/bin/httperf
and the man-page is installed in /usr/local/man/man1/httperf.  You can
change these defaults by passing appropriate options to the
"configure" script.  See "configure --help" for details.

This release of httperf has preliminary SSL support.  To enable it,
you need to have OpenSSL (http://www.openssl.org/) already installed
on your system.  The configure script assumes that the OpenSSH header
files and libraries can be found in standard locations (e.g.,
/usr/include and /usr/lib).  If the files are in a different place,
you need to tell the configure script where to find them.  This can be
done by setting environment variables CPPFLAGS and LDFLAGS before
invoking "configure".  For example, if the SSL header files are
installed in /usr/local/ssl/include and the SSL libraries are
installed in /usr/local/ssl/lib, then the environment variables should
be set like this:

	CPPFLAGS="-I/usr/local/ssl/include"
	LDFLAGS="-L/usr/local/ssl/lib"

With these settings in place, "configure" can be invoked as usual and
SSL should now be found.  If SSL has been detected, the following
three checks should be answered with "yes":

	checking for main in -lcrypto... yes
	checking for SSL_version in -lssl... yes
		:
	checking for openssl/ssl.h... yes

Note: you may have to delete "config.cache" to ensure that "configure"
re-evaluates those checks after changing the settings of the
environment variables.

  WARNING:
	httperf uses a deterministic seed for the random number
	generator used by SSL.  Thus, the SSL encrypted data is
	likely to be easy to crack.  In other words, do not assume
	that SSL data transferred when using httperf is (well)
	encrypted!

This release of httperf has been tested under the following operating systems:
HP-UX 11i (64-bit PA-RISC and IA-64)
Red Hat Enterprise Linux AS (AMD64 and IA-64)
SUSE Linux 10.1 (i386)
openSUSE 10.2 (i386)
OpenBSD 4.0 (i386)
FreeBSD 6.0 (AMD64)
Solaris 8 (UltraSparc 64-bit)

It should be straight-forward to build httperf on other platforms, please report
any build problems to the mailing list along with the platform specifications.

## Mailing list

A mailing list has been set up to encourage discussions among the
httperf user community.  This list is managed by majordomo.  To
subscribe to the list, send a mail containing the body:

	subscribe httperf

to majordomo@linux.hpl.hp.com.  To post an article to the list, send
it directly to httperf@linux.hpl.hp.com.

## Running httperf

IMPORTANT: It is crucial to run just one copy of httperf per client
machine.  httperf sucks up all available CPU time on a machine.  It is
therefore important not to run any other (CPU-intensive) tasks on a
client machine while httperf is running.  httperf is a CPU hog to
ensure that it can generate the desired workload with good accuracy,
so do not try to change this without fully understanding what the
issues are.

### Examples

The simplest way to invoke httperf is with a command line of the form:

> httperf --server wailua --port 6800

This command results in httperf attempting to make one request for URL
http://wailua:6800/.  After the reply is received, performance
statistics will be printed and the client exits (the statistics are
explained below).

A list of all available options can be obtained by specifying the
--help option (all option names can be abbreviated as long as they
remain unambiguous).

A more realistic test case might be to issue 100 HTTP requests at a
rate of 10 requests per second.  This can be achieved by additionally
specifying the --num-conns and --rate options.  When specifying the
--rate option, it's generally a good idea to also specify a timeout
value using the --timeout option.  In the example below, a timeout of
one second is specified (the ramification of this option will be
explained later):

> httperf --server wailua --port 6800 --num-conns 100 --rate 10 --timeout 1

The performance statistics printed by httperf at the end of the test
might look like this:

    Total: connections 100 requests 100 replies 100 test-duration 9.905 s

    Connection rate: 10.1 conn/s (99.1 ms/conn, <=1 concurrent connections)
    Connection time [ms]: min 4.6 avg 5.6 max 19.9 median 4.5 stddev 2.0
    Connection time [ms]: connect 1.4
    Connection length [replies/conn]: 1.000

    Request rate: 10.1 req/s (99.1 ms/req)
    Request size [B]: 57.0

    Reply rate [replies/s]: min 10.0 avg 10.0 max 10.0 stddev 0.0 (1 samples)
    Reply time [ms]: response 4.1 transfer 0.0
    Reply size [B]: header 219.0 content 204.0 footer 0.0 (total 423.0)
    Reply status: 1xx=0 2xx=100 3xx=0 4xx=0 5xx=0

    CPU time [s]: user 2.71 system 7.08 (user 27.4% system 71.5% total 98.8%)
    Net I/O: 4.7 KB/s (0.0*10^6 bps)

    Errors: total 0 client-timo 0 socket-timo 0 connrefused 0 connreset 0
    Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0

There are six groups of statistics: overall results ("Total"),
connection related results ("Connection"), results relating to the
issuing of HTTP requests ("Request"), results relating to the replies
received from the server ("Reply"), miscellaneous results relating to
the CPU time and network bandwidth used, and, finally, a summary of
errors encountered ("Errors").  Let's discuss each in turn:

## "Total" Results

The "Total" line summarizes how many TCP connections were initiated by
the client, how many requests it sent, how many replies it received,
and what the total test duration was.  The line below shows that 100
connections were initiated, 100 requests were performed and 100
replies were received.  It also shows that total test-duration was
9.905 seconds meaning that the average request rate was almost exactly
10 request per second.

    Total: connections 100 requests 100 replies 100 test-duration 9.905 s

## "Connection" Results

These results convey information related to the TCP connections that
are used to communicate with the web server.

Specifically, the line below show that new connections were initiated
at a rate of 10.1 connections per second.  This rate corresponds to a
period of 99.1 milliseconds per connection.  Finally, the last number
shows that at most one connection was open to the server at any given
time.

    Connection rate: 10.1 conn/s (99.1 ms/conn, <=1 concurrent connections)

The next line in the output gives lifetime statistics for successful
connections.  The lifetime of a connection is the time between a TCP
connection was initiated and the time the connection was closed.  A
connection is considered successful if it had at least one request
that resulted in a reply from the server.  The line shown below
indicates that the minimum ("min") connection lifetime was 4.6
milliseconds, the average ("avg") lifetime was 5.6 milliseconds, the
maximum ("max") was 19.9 milliseconds, the median ("median") lifetime
was 4.5 milliseconds, and that the standard deviation of the lifetimes
was 2.0 milliseconds.

    Connection time [ms]: min 4.6 avg 5.6 max 19.9 median 4.5 stddev 2.0

To compute the median time, httperf collects a histogram of connection
lifetimes.  The granularity of this histogram is currently 1
milliseconds and the maximum connection lifetime that can be
accommodated with the histogram is 100 seconds (these numbers can be
changed by editing macros BIN_WIDTH and MAX_LIFETIME in stat/basic.c).
This implies that the granularity of the median time is 1 millisecond
and that at least 50% of the lifetime samples must have a lifetime of
less than 100 seconds.

The next statistic in this section is the average time it took to
establish a TCP connection to the server (all successful TCP
connections establishments are counted, even connections that may have
failed eventually).  The line below shows that, on average, it took
1.4 milliseconds to establish a connection.

    Connection time [ms]: connect 1.4

The final line in this section gives the average number of replies
that were received per connection.  With regular HTTP/1.0, this value
is at most 1.0 (when there are no failures), but with HTTP Keep-Alives
or HTTP/1.1 persistent connections, this value can be arbitrarily
high, indicating that the same connection was used to receive multiple
responses.

    Connection length [replies/conn]: 1.000

## "Request" Results

The first line in the "Request"-related results give the rate at which
HTTP requests were issued and the period-length that the rate
corresponds to.  In the example below, the request rate was 10.1
requests per second, which corresponds to 99.1 milliseconds per
request.

    Request rate: 10.1 req/s (99.1 ms/req)

As long as no persistent connections are employed, the "Request"
results are typically very similar or identical to the "Connection"
results.  However, when persistent connections are used, several
requests can be issued on a single connection in which case the
results would be different.

The next line gives the average size of the HTTP request in bytes.  In
the line show below, the average request size was 57 bytes.

    Request size [B]: 57.0


## "Reply" Results

For simple measurements, the section with the "Reply" results is
probably the most interesting one.  The first line gives statistics on
the reply rate:

    Reply rate [replies/s]: min 10.0 avg 10.0 max 10.0 stddev 0.0 (1 samples)

The line above indicates that the minimum ("min"), average ("avg"),
and maximum ("max") reply rate was ten replies per second.  Given
these numbers, the standard deviation is, of course, zero.  The last
number shows that only one reply rate sample was acquired.  The
present version of httperf collects one rate sample about once every
five seconds.  To obtain a meaningful standard deviation, it is
recommended to run each test long enough so at least thirty samples
are obtained---this would correspond to a test duration of at least
150 seconds, or two and a half minutes.

The next line gives information on how long it took for the server to
respond and how long it took to receive the reply.  The line below
shows that it took 4.1 milliseconds between sending the first byte of
the request and receiving the first byte of the reply.  The time to
"transfer", or read, the reply was too short to be measured, so it
shows up as zero (as we'll see below, the entire reply fit into a
single TCP segment and that's why the transfer time was measured as
zero).

    Reply time [ms]: response 4.1 transfer 0.0

Next follow some statistics on the size of the reply---all numbers are
reported in bytes.  Specifically, the average length of reply headers,
the average length of the content, and the average length of reply
footers are given (HTTP/1.1 uses footers to realize the "chunked"
transfer encoding).  For convenience, the average total number of
bytes in the replies is also given.  In the example below, the average
header length ("header") was 219 bytes, the average content length
("content") was 204 bytes, and there were no footers ("footer"),
yielding a total reply length of 423 bytes on average.

    Reply size [B]: header 219.0 content 204.0 footer 0.0 (total 423.0)

The final piece in this section is a histogram on the status codes
received in the replies.  The example below shows that all 100 replies
were "successful" replies as they contained a status code of 200
(presumably):

    Reply status: 1xx=0 2xx=100 3xx=0 4xx=0 5xx=0


## Miscellaneous Results

This section starts with a summary of the CPU time the client
consumed.  The line below shows that 2.71 seconds were spent executing
in user mode ("user"), 7.08 seconds were spent executing in system
mode ("system") and that this corresponds to 27.4% user mode execution
and 71.5% system execution.  The total utilization was almost exactly
100%, which is expected given that httperf is a CPU hog:

    CPU time [s]: user 2.71 system 7.08 (user 27.4% system 71.5% total 98.8%)

Note that any time the total CPU utilization is significantly less
than 100%, some other processes must have been running on the client
machine while httperf was executing.  This makes it likely that the
results are "polluted" and the test should be rerun.

The next line gives the average network throughput in kilobytes per
second (where a kilobyte is 1024 bytes) and in megabits per second
(where a megabit is 10^6 bit).  The line below shows an average
network bandwidth of about 4.7 kilobyte per second.  The megabit per
second number is zero due to rounding errors.

    Net I/O: 4.7 KB/s (0.0*10^6 bps)

The network bandwidth is computed from the number of bytes sent and
received on TCP connections.  This means that it accounts for the
network payload only (i.e., it doesn't account for protocol headers)
and does not take into account retransmissions that may occur at the
TCP level.

## "Errors"

The final section contains statistics on the errors that occurred
during the test.  The "total" figure shows the total number of errors
that occurred.  The two lines below show that in our example run there
were no errors:

    Errors: total 0 client-timo 0 socket-timo 0 connrefused 0 connreset 0
    Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0

The meaning of each error is described below:

 total:
	The sum of all following error counts.

 client-timo:
	Each time a request is made to the server, a watchdog timer
	is started.  If no (partial) response is received by the time
	the watchdog timer expires, httperf times out that request
	a increments this error counter.  This is the most common error
	when driving a server into overload.

 socket-timo
	The number of times a TCP connection failed with a
	socket-level time out (ETIMEDOUT).

 connrefused
	The number of times a TCP connection attempt failed with
	a "connection refused by server" error (ECONNREFUSED).

 connreset
	The number of times a TCP connection failed due to a reset
	(close) by the server.

 fd-unavail
	The number of times the httperf client was out of file
	descriptors.  Whenever this count is bigger than
	zero, the test results are meaning less because the client
	was overloaded (see discussion on setting --timeout below).

 addrunavail
	The number of times the client was out of TCP port numbers
	(EADDRNOTAVAIL).  This error should never occur.  If it
	does, the results should be discarded.

 ftab-full
	The number of times the system's file descriptor table
	was full.  Again, this error should never occur.  If it
	does, the results should be discarded.

 other
	The number of times other errors occurred.  Whenever this
	occurs, it is necessary to track down the actual error
	reason.  This can be done by compiling httperf with
	debug support and specifying option --debug 1.


## Selecting appropriate timeout values

Since the client machine has only a limited set of resource available,
it cannot sustain arbitrarily high HTTP request rates.  One limit is
that there are only roughly 60,000 TCP port numbers that can be in use
at any given time.  Since, on HP-UX, it takes one minute for a TCP
connection to be fully closed (leave the TIME_WAIT state), the maximum
rate a client can sustain is about 1,000 requests per second.

The actual sustainable rate is typically lower than this because
before running out of TCP ports, a client is likely to run out of file
descriptors (one file descriptor is required per open TCP connection).
By default, HP-UX 10.20 allows 1024 file descriptors per process.
Without a watchdog timer, httperf could potentially quickly use up all
available file descriptors, at which point it could not induce any new
load on the server (this would primarily happen when the server is
overloaded).  To avoid this problem, httperf requires that the web
server must respond within the time specified by option --timeout.  If
it does not respond within that time, the client considers the
connection to be "dead" and closes it (and increases the "client-timo"
error count).  The only exception to this rule is that after sending a
request, httperf allows the server to take some additional time before
it starts responding (to accommodate HTTP requests that take a long
time to complete on the server).  This additional time is called the
"server think time" and can be specified by option --think-timeout.
By default, this additional think time is zero, so by default the
server has to be able to respond within the time allowed by the
--timeout option.

In practice, we found that with a --timeout value of 1 second, an HP
9000/735 machine running HP-UX 10.20 can sustain a rate of about 700
connections per second before it starts to run out of file descriptor
(the exact rate depends, of course, on a number of factors).  To
achieve web server loads bigger than that, it is necessary to employ
several independent machines, each running one copy of httperf.  A
timeout of one second effectively means that "slow" connections will
typically timeout before TCP even gets a chance to retransmit (the
initial retransmission timeout is on the order of 3 seconds).  This is
usually OK, except that one should keep in mind that it has the effect
of truncating the connection life time distribution.
07070100000007000081ED0000000000000000000000015FCD000200000647000000000000000000000000000000000000002000000000httperf-0.9.0+git.20201206/TODOSome ideas (contributions/patches welcome):

- port to libevent to improve scalability and deal with the file descriptor cap
- Add option to output results as rdf (xml), cvs or default
- Add ability to read entire POST and GET messages from logs and send them
- wsesspage: don't fetch same object more than once (assume the existence
  of a cache)---this avoids trouble with recursive pages
- make httperf easier to use; some ideas:
	o Make httperf into a network daemon that is controlled by
	  an httperf frontend.  This would allow running tests with
	  a single command line even when multiple clients are involved.
	  The performance results should be reported both on a
	  per-client basis and in a summarized form reporting overall
	  server behavior.
	o Provide (default) scripts to run certain benchmarks.
	o Provide (default) scripts to produce performance graphs.
- use cycle registers to get time on CPUs that have such registers
  (IA-64, PA-RISC, x86, Alpha, at least)
- randomize the uri's generated by --wset so we can defeat file prefetching
  that the server (or the server's OS) may attempt to do

Done:

+ Specifying --session-cookie without specifying a session workload causes
  httperf to core-dump (reported by Dick Carter, 10/13/98)
+ elevate `Session' to same level as Call and Connection
+ sample session throughput when using sessions (not just rate throughput)
+ integrate Dick's wsesslog
+ write man-page
+ chunked replies fail after the first reply (at least on HP-UX)
+ core.c does not fully support pipelined requests yet; would require
  changes to the watchdog management
07070100000008000081ED0000000000000000000000015FCD000200000A45000000000000000000000000000000000000002800000000httperf-0.9.0+git.20201206/configure.ac#                                               -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ(2.60)
AC_INIT(httperf, 0.9.1, httperf@linux.hpl.hp.com)
AC_CONFIG_SRCDIR([src/httperf.c])
AC_CONFIG_HEADER([config.h])

AC_CONFIG_MACRO_DIR([m4])

AC_CANONICAL_TARGET

AM_INIT_AUTOMAKE(httperf, 0.9.1)

# Checks for programs.
AC_GNU_SOURCE
AC_PROG_CC([cc gcc])
AC_PROG_CC_C99

AC_PROG_INSTALL
AC_PROG_LIBTOOL

# Are we building the idleconn program?
AC_ARG_ENABLE([idleconn],
	AS_HELP_STRING([--enable-idleconn],
		[Build the idleconn program (default=no), requires libevent]),
	[enable_idleconn=$enableval],
	[enable_idleconn=no])

if test "$enable_idleconn" = "yes"; then
AC_CHECK_LIB([event],
	[event_init],
	,
	[AC_MSG_ERROR([libevent is required to build idleconn])])
fi


AM_CONDITIONAL(IDLECONN, 
	[test "x$enable_idleconn" = xyes])

# Checks for libraries.
AC_CHECK_LIB(m, sqrt)
AC_CHECK_LIB(crypto, main)
AC_CHECK_LIB(ssl, SSL_version, , AC_MSG_WARN([SSL Disabled]) )
# The following checks are for solaris and its ilk
AC_SEARCH_LIBS([getsockopt], [socket])
AC_SEARCH_LIBS([socket], [socket nsl])
AC_SEARCH_LIBS([gethostbyname], [socket nsl])
AC_SEARCH_LIBS([inet_aton], [resolv])

# Checks for header files.
AC_FUNC_ALLOCA
AC_HEADER_TIME
AC_CHECK_HEADERS([openssl/ssl.h getopt.h])

if test "$ac_cv_header_openssl_ssl_h" = "yes" \
	-a "$ac_cv_lib_ssl_SSL_version" = "yes" \
	-a "$ac_cv_lib_crypto_main" = "yes"; then
  CFLAGS="${CFLAGS} -DHAVE_SSL"
  LDFLAGS="${LDFLAGS}"  
fi

# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T
AC_CHECK_TYPE(u_char, unsigned char)
AC_CHECK_TYPE(u_short, unsigned short)
AC_CHECK_TYPE(u_int, unsigned int)
AC_CHECK_TYPE(u_long, unsigned long)

# Check for unsigned long long int (stat/basic.c)
AC_TYPE_UNSIGNED_LONG_LONG_INT
if test "$ac_cv_type_unsigned_long_long_int" != "yes" ; then
	AC_CHECK_TYPE(u_wide, unsigned long)
else
	AC_CHECK_TYPE(u_wide, unsigned long long)
fi

# Checks for library functions.
AC_FUNC_MMAP
AC_FUNC_SELECT_ARGTYPES
AC_TYPE_SIGNAL
AC_FUNC_STRTOD
AC_FUNC_VPRINTF
AC_CHECK_FUNCS([getopt_long])

# Turn on Debug if necessary
AC_ARG_ENABLE(debug,
 [  --enable-debug          enable debug support])

if test "$enable_debug" = yes; then
      CFLAGS="${CFLAGS} -DDEBUG"
fi

AC_ARG_WITH(epoll, AS_HELP_STRING([--with-epoll], [use epoll if available]))
AS_IF([test "$with_epoll" != "no"],
	AC_CHECK_FUNC(epoll_create,
		AC_DEFINE([HAVE_EPOLL], 1, [#undef HAVE_EPOLL])))

AC_OUTPUT(Makefile man/Makefile src/stat/Makefile src/lib/Makefile src/gen/Makefile src/Makefile)
07070100000009000041ED0000000000000000000000025FCD000200000000000000000000000000000000000000000000002300000000httperf-0.9.0+git.20201206/contrib0707010000000A000041ED0000000000000000000000025FCD000200000000000000000000000000000000000000000000003400000000httperf-0.9.0+git.20201206/contrib/idleconn-manpage0707010000000B000081ED0000000000000000000000015FCD000200000444000000000000000000000000000000000000004100000000httperf-0.9.0+git.20201206/contrib/idleconn-manpage/genallman.sh#!/bin/bash

# Generate several manpages at the same time.
# Copyright (C) 2014-2015 Joao Eriberto Mota Filho <eriberto@debian.org>
# v0.3, available at https://github.com/eribertomota/genallman
#
# You can use this code in the same terms of the BSD-3-clause license or,
# optionally, in the same terms of the license used in debian/ directory
# when packaging for Debian or similar.
#
# This script uses txt2man. You need 2 files: program_name.txt and
# program_name.header.
#
# The program_name.header must be use this structure:
#
# .TH <program_name> "<manpage_level>" "<date>" "<program_name_upper_case> <program_version>" "<program_description>"
#
# Example:
#
# .TH mac-robber "1" "May 2013" "MAC-ROBBER 1.02" "collects data about allocated files in mounted filesystems"

[ -f /usr/bin/txt2man ] || { echo "ERROR: txt2man not found."; exit; }

for NAME in $(ls | grep header | cut -d'.' -f1)
do
    LEVEL=$(cat $NAME.header | cut -d" " -f3 | tr -d '"')
    cat $NAME.header > $NAME.$LEVEL
    txt2man $NAME.txt | grep -v '^.TH ' >> $NAME.$LEVEL
    echo "Generated $NAME.$LEVEL."
done
0707010000000C000081A40000000000000000000000015FCD00020000005F000000000000000000000000000000000000004400000000httperf-0.9.0+git.20201206/contrib/idleconn-manpage/idleconn.header.TH idleconn "1" "Mar 2016" "IDLECONN 0.9.0" "tool for opening any number of idle connections"
0707010000000D000081A40000000000000000000000015FCD00020000050B000000000000000000000000000000000000004100000000httperf-0.9.0+git.20201206/contrib/idleconn-manpage/idleconn.txtNAME
 idleconn - tool for opening any number of idle connections

SYNOPSIS
 idleconn <server> <port> <numidle>

DESCRIPTION
 idleconn is part of httperf suite and is useful to simulate a large number
 of concurrent and idle connections. It can establish a set of persistent
 connections, each of which generated periodic requests to the server. The
 effect is that at all times, some of the connections were active while the
 rest were idle, and these active and idle connection sets kept changing with
 time. (This paragraph was extracted and adapted from the article "Scalability
 of Linux Event-Dispatch Mechanisms" (HPL-2000-174), written by Abhishek
 Chandra and David Mosberger).

OPTIONS
 server   IP of the server to connect.
 port     Port used by server.
 numidle  Number of idle process to be generated.

EXAMPLE
 This is a simple example how to use idleconn:

    $ ./idleconn 192.168.1.1 80 100

 It would open and maintain 100 idle connections to a web server, listening on
 port 80, using the IP address 192.168.1.1.

SEE ALSO
 httperf(1)

AUTHOR
  The httperf was written by David Mosberger-Tang, Hewlett-Packard Company and Contributors.

  This manual page was written by Joao Eriberto Mota Filho <eriberto@debian.org>
  for the Debian project (but may be used by others).
0707010000000E000041ED0000000000000000000000025FCD000200000000000000000000000000000000000000000000001F00000000httperf-0.9.0+git.20201206/man0707010000000F000081ED0000000000000000000000015FCD000200000039000000000000000000000000000000000000002B00000000httperf-0.9.0+git.20201206/man/Makefile.amman_MANS = httperf.1 idleconn.1
EXTRA_DIST = $(man_MANS)
07070100000010000081ED0000000000000000000000015FCD00020000A2EB000000000000000000000000000000000000002900000000httperf-0.9.0+git.20201206/man/httperf.1.\" .IX httperf
.TH "httperf" "1" "01 Feb 2008" "" ""
.SH "NAME"
httperf \- HTTP performance measurement tool
.SH "SYNOPSIS"
.B httperf
.RB [ \-\-add\-header
.I R S ]
.RB [ \-\-burst\-length
.I R N ]
.RB [ \-\-client
.I R I / N ]
.RB [ \-\-close\-with\-reset ]
.RB [ \-d | \-\-debug
.I R N ]
.RB [ \-\-failure\-status
.I R N ]
.RB [ \-h | \-\-help ]
.RB [ \-\-hog ]
.RB [ \-\-http\-version
.I R S ]
.RB [ \-\-max\-connections
.I R N ]
.RB [ \-\-max\-piped\-calls
.I R N ]
.RB [ \-\-method
.I R S ]
.RB [ \-\-no\-host\-hdr ]
.RB [ \-\-num\-calls 
.I R N ]
.RB [ \-\-num\-conns
.I R N ]
.RB [ \-\-period " [" d | u | e ] \fIT1\fR [ ,\fIT2\fR ]]
.RB [ \-\-port
.I R N ]
.RB [ \-\-print\-reply " [" header | body ] ]
.RB [ \-\-print\-request " [" header | body ] ]
.RB [ \-\-rate
.I R X ]
.RB [ \-\-recv\-buffer
.I R N ]
.RB [ \-\-retry\-on\-failure ]
.RB [ \-\-send\-buffer
.I R N ]
.RB [ \-\-server
.I R S ]
.RB [ \-\-server\-name
.I R S ]
.RB [ \-\-session\-cookie ]
.RB [ \-\-ssl ]
.RB [ \-\-ssl\-ciphers
.I R L ]
.RB [ \-\-ssl\-no\-reuse ]
.RB [ \-\-think\-timeout
.I R X ]
.RB [ \-\-timeout
.I R X ]
.RB [ \-\-uri
.I R S ]
.RB [ \-v | \-\-verbose ]
.RB [ \-V | \-\-version ]
.RB [ "\-\-wlog y" | n, \fIF\fR]
.RB [ \-\-wsess
.I R N , N , X ]
.RB [ \-\-wsesslog
.I R N , X , F ]
.RB [ \-\-wset
.I R N , X ]
.SH "DESCRIPTION"
.B httperf
is a tool to measure web server performance.  It speaks the HTTP
protocol both in its HTTP/1.0 and HTTP/1.1 flavors and offers a
variety of workload generators. While running, it keeps track of a
number of performance metrics that are summarized in the form of
statistics that are printed at the end of a test run.  The most basic
operation of
.B httperf
is to generate a fixed number of HTTP GET requests and to measure how
many replies (responses) came back from the server and at what rate
the responses arrived.

.B IMPORTANT:
To obtain correct results, it is necessary to run at most one
.B httperf
process per client machine.  Also, there should be as few background
processes as possible both on the client and server machines.

.SH "EXAMPLES"
.TP 
httperf \-\-hog \-\-server www
This command causes
.B httperf
to create a connection to host www, send a request for the root
document (http://www/), receive the reply, close the connection, and
then print some performance statistics.
.TP 
httperf \-\-hog \-\-server www \-\-num\-conn 100 \-\-ra 10 \-\-timeout 5
Like above, except that a total of 100 connections are created and
that connections are created at a fixed rate of 10 per second.  Note
that option ``\-\-rate'' has been abbreviated to ``\-\-ra''.
.TP 
httperf \-\-hog \-\-server=www \-\-wsess=10,5,2 \-\-rate 1 \-\-timeout 5
Causes
.B httperf
to generate a total of 10 sessions at a rate of 1 session per second.
Each session consists of 5 calls that are spaced out by 2 seconds.
.TP 
httperf \-\-hog \-\-server=www \-\-wsess=10,5,2 \-\-rate=1 \-\-timeout=5 \-\-ssl
Like above, except that
.B httperf
contacts server www via SSL at port 443 (the default port for SSL
connections).
.TP 
httperf \-\-hog \-\-server www \-\-wsess=10,5,2 \-\-rate=1 \-\-timeout=5 \-\-ssl \-\-ssl\-ciphers=EXP\-RC4\-MD5:EXP\-RC2\-CBC\-MD5 \-\-ssl\-no\-reuse \-\-http\-version=1.0
Like above, except that
.B httperf
will inform the server that it can only select from two cipher suites
(EXP\-RC4\-MD5 or EXP\-RC2\-CBC\-MD5); furthermore,
.B httperf
will use HTTP version 1.0 which requires a new TCP connection for each
request.  Also, SSL session ids are not reused, so the entire SSL
connection establishment process (known as the SSL handshake) occurs
for each connection.
.SH "OPTIONS"
The operation of
.B httperf
can be controlled through a number of options.  The tool supports both
short (one\-character) and long (arbitrary\-length) option names.  Short
options are prefixed with a single leading dash (\-), long
options with a double\-dash (\-\-).  Multiple short options can be
grouped together (e.g.,
.RB `` \-vV ''
is equivalent to
.RB `` "\-v \-V" '')
and long options can be abbreviated so long as they remain unique.
Parameters to options can be specified either by following the long
option name with an equal sign and the parameter value (e.g.,
.BR \-\-burst=10 )
or by separating the option name and value with whitespace (e.g.,
.BR "\-\-burst 10" ).
.TP 
.BI \-\-add\-header= S
Specifies to include string
.I S
as an additional request header.  It is necessary to specify the
terminating carriage\-return/line\-feed sequence explicitly.  This can
be done by using the escape sequence ``\\n''.  This makes it possible
to include multiple request headers.  For example, ``\-\-add\-header
"Referer: foo\\nAuth: secret\\n"'' would add two request headers
(``Referer'' and ``Auth'') to each request.  Other supported escape
sequences are ``\\r'' (carriage\-return), ``\\a'' (line\-feed), ``\\\\''
(backslash), and ``\\N'' where N is the code the character to be
inserted (in octal).
.TP 
.BI \-\-burst\-length= N
Specifies the length of bursts.  Each burst consists of
.I N
calls to the server.  The exact meaning of this parameter depends on
the workload generator.  For regular request\-oriented workloads, see the
description of option
.BR \-\-wsess .
.TP 
.BR \-\-no\-host\-hdr
Specifies that the "Host:" header should not be included when issuing
an HTTP request.
.TP 
.BR \-\-num\-calls .
For session\-oriented workloads, see the description of option
.BR \-\-wsess .
.TP 
.BI \-\-client= I / N
Specifies that the machine
.B httperf
is running on is client
.I I
out of a total of
.I N
clients.
.I I
should be in the range from 0 to
.I R N "\-1."
Some of the workload generators (e.g.,
.BR \-\-wset)
use the client identity as a bias value to ensure that not all clients
generate perfectly identical workloads.  When performing a test that
involves several client machines, it is generally a good idea to
specify this option.
.TP 
.BI \-\-close\-with\-reset
Requests that
.B httperf
closes TCP connections by sending a RESET instead of going through the
normal TCP connection shutdown handshake.  Turning on this option can
have ill effects such as data corruption, stuck TCP control blocks, or
wrong results.  For this reason, the option should not be used unless
absolutely necessary and even then it should not be used unless its
implications are fully understood.
.TP 
.BI \-d= N
.TP 
.BI \-\-debug= N
Set debug level to
.I R N .
Larger values of
.I N
will result in more output.
.TP 
.BI \-\-failure\-status= N
Specifies that an HTTP response status code of
.I N
should be treated as a failure (i.e., treated as if the request had
timed out, for example).  For example, with
.RB `` \-\-failure\-status=504 ''
responses with an HTTP status of ``504 Gateway Time\-out'' would be
considered failures.  Caveat: this option is currently supported
for session workloads only (see the
.B \-\-wsess
and
.B \-\-wsesslog
options).
.TP 
.B \-h
.TP 
.B \-\-help
Prints a summary of available options and their parameters.
.TP 
.BI \-\-hog
This option requests to use up as many TCP ports as necessary.
Without this option,
.B httperf
is typically limited to using ephemeral ports (in the range from 1024
to 5000).  This limited port range can quickly become a bottleneck so
it is generally a good idea to specify this option for serious
testing.  Also, this option must be specified when measuring NT
servers since it avoids a TCP incompatibility between NT and UNIX
machines.
.TP 
.BI \-\-http\-version= S
Specifies the version string that should be included in the requests
sent to the server.  By default, version string ``1.1'' is used.  This
option can be set to ``1.0'' to force the generation of HTTP/1.0
requests.  Setting this option to any value other than ``1.0'' or ``1.1''
may result in undefined behavior.
.TP 
.BI \-\-max\-connections= N
Specifies that at most
.I N
connections are opened for each session.  This option is meaningful in
conjunction with options
.B \-\-wsess
and
.B \-\-wsesslog
only.
.TP 
.BI \-\-max\-piped\-calls= N
Specifies that at most
.I N
pipelined calls are issued on each connection.  This option is
meaningful in conjunction with options
.B \-\-wsess
and
.B \-\-wsesslog
only.
.TP 
.BI \-\-method= S
Specifies the method that should be used when issuing an HTTP request.
If this option is not specified, the GET method is used.  The method
.I S
can be an arbitrary string but is usually one of GET, HEAD, PUT, POST,
etc.
.TP 
.BI \-\-num\-calls= N
This option is meaningful for request\-oriented workloads only.  It
specifies the total number of calls to issue on each connection before
closing it.  If
.I N
is greater than 1, the server must support persistent connections.
The default value for this option is 1.  If
.BR \-\-burst\-length
is set to
.I R B ,
then the
.I N
calls are issued in bursts of
.I B
pipelined calls each.  Thus, the total number of such bursts will
be
.I N/B
(per connection).
.TP 
.BI \-\-num\-conns= N
This option is meaningful for request\-oriented workloads only.  It
specifies the total number of connections to create.  On each
connection, calls are issued as specified by options
.B \-\-num\-calls
and
.BR \-\-burst\-length .
A test stops as soon as the
.I N
connections have either completed or failed.  A connection is
considered to have failed if any activity on the connection fails to
make forward progress for more than the time specified by the timeout
options
.B \-\-timeout
and
.BR \-\-think\-timeout .
The default value for this option is 1.
.TP 
.BI \-\-period= [D]T1[,T2]
Specifies the time interval between the creation of connections or sessions.
Connections are created by default, sessions if option
.B \-\-wsess
or
.B \-\-wsesslog
has been specified.
This connection/session ``interarrival time'' can alternatively be specified by
the 
.B \-\-rate
option, although more flexibility is available with
.B \-\-period.
The
.I D
parameter specifies the interarrival time distribution.
If omitted or set to
.RB `` d '',
a deterministic (i.e., fixed) period is used as specified
by parameter
.I R T1
in units of seconds.
If
.I D
is set to 
.RB `` e '',
an exponential (i.e., Poisson) distribution is used with
a mean interarrival time of
.I R T1 .
If
.I D
is set to 
.RB `` u '',
a uniform distribution over the interval
.RI [ T1 , T2 )
is used for the interarrival time.
Finally, if
.I D
is set to
.RB ``v'',
a number of rates can be specified as follows:
.B \-\-period=vT1,D1,T2,D2...Tn,Dn 
Where n <= NUM_RATES in httperf.h and
.I Ti,Di
represent the period time (i.e., 1/rate) and duration to
maintain that rate (i.e.,
.B \-\-period=v1,2,0.5,4 
will generate 1 request/seconds for 2 seconds then
2 requests/seconds for 4 seconds).  
In all cases, a period of 0 results in connections
or sessions being generated sequentially (a new connection/session is
initiated as soon as the previous one completes).  The default value
for this option is 0.  Note that specifying, for example,
.B \-\-rate=5
is equivalent to specifying
.B \-\-period=d0.2
or
.BR \-\-period=0.2 .
By specifying
.BR \-\-period=u1,3 ,
the interarrival times will be randomly chosen from the interval
between 1 and 3 seconds.  The specific sequence of (pseudo\-)random
interarrival times are identical from one
.B httperf
run to another as long as the values for the
.B \-\-period
and
.B \-\-client
options are identical.
.TP 
.BI \-\-port= N
This option specifies the port number
.I N
on which the web server is listening for HTTP requests.  By default,
.B httperf
uses port number 80.
.TP 
.BR \-\-print\-reply [ = [ header | body ]]
Requests the printing of the reply headers, body, and summary.  The
output is directed to standard output.  Reply header lines are
prefixed by "RH", reply body lines are prefixed by "RB", and the
reply\-size summary is prefixed by "RS".  The prefix is followed by a
serial number that uniquely identifies the call that the reply line is
for and a colon (":") character that marks the beginning of the actual
reply line.  To print only reply headers, pass argument
.B header
to this option.  To print only the reply body, pass argument
.B body
to this option.
.TP 
.BR \-\-print\-request [ = [ header | body ]]
Requests the printing of the request headers, body (if one is
present), and summary.  The output is directed to standard output.
Request header lines are prefixed by "SH", request body lines are
prefixed by "SB", and the request summary is prefixed by "SS".  The
prefix is followed by the call's serial number and a colon (":")
character that marks the beginning of the actual reply line.  To print
only request headers, pass argument
.B header
to this option.  To print only the request body, pass argument
.B body
to this option.
.TP 
.BI \-\-rate= X
Specifies the fixed rate at which connections or sessions are created.
Connections are created by default, sessions if option
.B \-\-wsess
or
.B \-\-wsesslog
has been specified.  In both cases a rate of 0 results in connections
or sessions being generated sequentially (a new session/connection is
initiated as soon as the previous one completes).  The default value
for this option is 0.
.TP 
.BI \-\-recv\-buffer= N
Specifies the maximum size of the socket receive buffers used to
receive HTTP replies.  By default, the limit is 16KB.  A smaller value
may help memory\-constrained clients whereas a larger value may be
necessary when communicating with a server over a high\-bandwidth,
high\-latency connection.
.TP 
.BI \-\-retry\-on\-failure
This option is meaningful for session workloads only (see the
.B \-\-wsess
and
.B \-\-wsesslog
options).  If specified, a call that results in a failure response (as
defined by the
.B \-\-failure\-status
option) is retried immediately instead of causing the session to fail.
.TP 
.BI \-\-send\-buffer= N
Specifies the maximum size of the socket send buffers used to send
HTTP requests.  By default, the limit is 4KB.  A smaller value may
help memory\-constrained clients whereas a larger value may be
necessary when generating large requests to a server connected via a
high\-bandwidth, high\-latency connection.
.TP 
.BI \-\-server= S
Specifies the IP hostname of the server.  By default, the hostname
``localhost'' is used.  This option should always be specified as it
is generally not a good idea to run the client and the server on the
same machine.
.TP 
.BI \-\-server\-name= S
Specifies the (default) server name that appears in the "Host:" header
of every request sent by
.BR httperf .
Without this option, the host name (or IP address) specified by option
.B \-\-server
is used instead.
.TP 
.B \-\-session\-cookie
When this option is turned on, cookie managment is enabled on a
per\-session basis.  What this means is that if a reply to a request
that was generated by session
.I R X 
contains a cookie, then all future requests sent by session
.I X
will include this cookie as well.  At present, the cookie manager in
.B httperf
supports only one cookie per session.  If a second cookie is received,
the new cookie overwrites the existing one and a warning message is
printed if ``\-\-debug 1'' is on.
.TP 
.B \-\-ssl
Specifies that all communication between
.B httperf
and the server should utilize the Secure Sockets Layer (SSL) protocol.
This option is available only if
.B httperf
was compiled with SSL support enabled.
.TP 
.BI \-\-ssl\-ciphers= L
This option is only meaningful if SSL is in use (see
.B \-\-ssl
option).  This option specifies the list
.I L
of cipher suites that
.B httperf
may use in negotiating a secure connection with the server.  If the
list contains more than one cipher suite, the ciphers must be
separated by a colon.  If the server does not accept any of the listed
cipher suites, the connection establishment will fail and
.B httperf
will exit immediately.  If this option is not specified when the
.B \-\-ssl
option is present then
.B httperf
will use all of the SSLv3 cipher suites provided by the underlying SSL
library.
.TP 
.B \-\-ssl\-no\-reuse
This option is only meaningful if SSL and sessions are in use (see
.BR \-\-ssl ,
.BR \-\-wsess ,
.BR \-\-wsesslog ).
When an SSL connection is established the client receives a session
identifier (session id) from the server.  On subsequent SSL
connections, the client normally reuses this session id in order to
avoid the expense of repeating the (slow) SSL handshake to establish a
new SSL session and obtain another session id (even if the client
attempts to re\-use a session id, the server may force the client to
renegotiate a session).  By default
.B httperf
reuses the session id across all connections in a session.  If the
.B \-\-ssl\-no\-reuse
option is in effect, then
.B httperf
will not reuse the session id, and the entire SSL handshake will be
performed for each new connection in a session.
.TP 
.BI \-\-think\-timeout= X
Specifies the maximum time that the server may need to initiate
sending the reply for a given request.  Note that this timeout value
is added to the normal timeout value (see option
.BR \-\-timeout ).
When accessing static web content, it is usually not necessary to
specify this option.  However, when performing tests with long\-running
CGI scripts, it may be necessary to use this option to allow for
larger response\-times.  The default value for this option is zero
seconds, meaning that the server has to be able to respond within the
normal timeout value.
.TP 
.BI \-\-timeout= X
Specifies the amount of time
.I X
that
.B httperf
is willing to wait for a server reaction.  The timeout is specified in
seconds and can be a fractional number (e.g.,
.BR "\-\-timeout 3.5" ).
This timeout value is used when establishing a TCP connection, when
sending a request, when waiting for a reply, and when receiving a
reply.  If during any of those activities a request fails to make
forward progress within the alloted time,
.B httperf
considers the request to have died, closes the associated connection
or session and increases the
.B client\-timo
error count.  The actual timeout value used when waiting for a reply
is the sum of this timeout and the think\-timeout (see option
.BR \-\-think\-timeout ).
By default, the timeout value is infinity.
.TP 
.BI \-\-uri= S
Specifies that URI
.I S
should be accessed on the server.  For some of the workload generators
(e.g.,
.BR \-\-wset ),
this option specifies the prefix for the URIs being accessed.
.TP 
.BI \-\-use\-timer\-cache
This feature allows the user to specify whether they want to
cache timestamps or not.  Timestamps are not cached by default, but
the user can enable caching if higher performance is more important
than timing accuracy. For small response sizes, disabling timer 
caching reduced the performance of httperf by about
10%; for larger response sizes there was little or no effect.
.TP 
.B \-v
.TP 
.B \-\-verbose
Puts
.B httperf
into verbose mode.  In this mode, additional output such as the
individual reply rate samples and connection lifetime histogram are
printed.
.TP 
.B \-V
.TP 
.B \-\-version
Prints the version of
.BR httperf .
.TP 
.BI \-\-wlog= B , F
This option can be used to generate a specific sequence of URI
accesses.  This is useful to replay the accesses recorded in a server
log file, for example.  Parameter
.I F
is the name of a file containing the ASCII NUL separated list of URIs
that should be accessed.  If parameter
.I B
is set to
.RB `` y '',
.B httperf
will wrap around to the beginning of the file when reaching the end of
the list (so the list of URIs is accessed repeatedly).  With
.I B
set to
.RB `` n '',
the test will stop no later than when reaching the end of the URI
list.
.TP 
.BI \-\-wsess= N1 , N2 , X
Requests the generation and measurement of sessions instead of
individual requests.  A session consists of a sequence of bursts which
are spaced out by the user think\-time.  Each burst consists of a fixed
number
.I L
of calls to the server
.RI ( L
is specified by option
.BR \-\-burst\-length ).
The calls in a burst are issued as follows: at first, a single call is
issued.  Once the reply to this first call has been fully received,
all remaining calls in the burst are issued concurrently.  The
concurrent calls are issued either as pipelined calls on an existing
persistent connection or as individual calls on separate connections.
Whether a persistent connection is used depends on whether the server
responds to the first call with a reply that includes a ``Connection:
close'' header line.  If such a line is present, separate connections
are used.

The option specifies the following parameters:
.I N1
is the total number of sessions to generate,
.I N2
is the number of calls per session, and
.I X
is the user think\-time (in seconds) that separates consecutive call
bursts.  For example, the options
.RB `` "\-\-wsess=100,50,10 \-\-burst\-len 5" ''
would result in 100 sessions with a total of 50 calls each.  Since
each burst has a length of 5 calls, a total of 10 call bursts would be
generated per session.  The user think\-time between call bursts would
be 10 seconds.  Note that user think\-time
.I X
denotes the time between receiving the last reply of the previous
call burst and the sending of the first request of the next burst.

A test involving sessions finishes as soon as the requested number
.I N1
of sessions have either failed or completed.  A session is considered
to have failed if any operation in a session takes longer than
the timeouts specified by options
.B \-\-timeout
and
.BR \-\-think\-timeout .
In addition, a session also fails if the server returns a reply with a
status code matching the one specified by option
.BR \-\-failure\-status .
.TP 
.BI \-\-wsesslog= N , X , F
This specifies a session workload generator similar to
.B \-\-wsess
(please read that description first).  With
.B \-\-wsesslog
though, many aspects of user sessions, including the number and
sequence of URI's, request method, think\-time and burst\-length parameters, 
can be specified in an input file
.I F.
Two other parameters are retained from
.B \-\-wsess,
namely
.I N,
the number of sessions to initiate, and
.I X,
the burst\-to\-burst user think time (note that this becomes a default
time since the input file
.I F
can also specify user think time on a per\-burst basis.
A small example input file can most\-easily show the settable parameters:
.br 

.br 
# Comment lines start with a ``#'' as the first
.br 
# character.  Lines with only whitespace delimit
.br 
# sessions (multiple blank lines do not generate
.br 
# ``null'' sessions).  All other lines specify a
.br 
# uri\-sequence (1 uri per line).  If the first
.br 
# character of the line is whitespace (e.g. space
.br 
# or tab), the uri is considered to be part of a
.br 
# burst that is sent out after the previous
.br 
# non\-burst uri.
.br 

.br 
# session 1 definition (this is a comment)
.br 
/foo.html think=2.0
.br 
	/pict1.gif
.br 
	/pict2.gif
.br 
/foo2.html method=POST contents='Post data'
.br 
	/pict3.gif
.br 
	/pict4.gif
.br 
	
.br 
# session 2 definition
.br 
/foo3.html method=POST contents="Multiline\\ndata"
.br 
/foo4.html method=HEAD
.br 

.br 
The above description specifies 2 sessions.  The first session will
start with a request for /foo.html.  When the /foo.html response comes
back, a burst of 2 requests will follow (/pict1.gif and /pict2.gif).
When the last of those responses is received, a two second user think
time is inserted before the next request of /foo2.html is issued.
This request is sent as a POST.  The posted data can be contained
between single\- or double\-quotes.  Newlines can appear within posted
data as ``\\n'' or as a ``\\<CR>''.  The /foo2.html response is
followed by a burst request of /pict3.gif and /pict4.gif, which
concludes this session.  The second session is started some time after
the first, as specified by the
.B \-\-rate
or
.B \-\-period
options.
.br 

.br 
The second session consists of 2 requests separated by the default user think
time as specified by the
.I X
parameter of the
.B \-\-wsesslog
option.  If the
.I N
parameter of
.B \-\-wsesslog
is greater than the number of sessions defined in input file
.I R F ,
then the defined sessions are used repeatedly until
.I N
sessions have been created (i.e., the defined sessions are used in a
round\-robin fashion).
.br 
	
.br 
One should avoid using
.B \-\-wsesslog
in conjunction with other
.B httperf
options that also control session behavior and workload URI's, namely
.B \-\-burst\-length,
.B \-\-wsess,
.B \-\-wlog,
and
.B \-\-wset.
.TP 
.BI \-\-wset= N , X
This option can be used to walk through a list of URIs at a given
rate.  Parameter
.I N
specifies the number of distinct URIs that should be generated and
.I X
specifies the rate at which new URIs are accessed.  A rate of
.B 0.25
would mean that the same URI would be accessed four times in a row
before moving on to the next URI.  This type of access pattern is
useful in generating a workload that induces a relatively predictable
amount of traffic in the disk I/O subsystem of the server (assuming
.I N
and the accessed files are big enough to exceed the server's buffer
cache).  The URIs generated are of the form
.I R	prefix / path .html,
where
.I prefix
is the URI prefix specified by option
.B \-\-uri
and
.I path
is generated as follows: for the
.I R i \-th
file in the working set, write down
.I i
in decimal, prefixing the number with as many zeroes as necessary
to get a string that has as many digits as
.I R N \-1.
Then insert a slash character between each digit.  For example,
the 103rd file in a working set consisting of 1024 files would
result in a path of
.RB `` 0/1/0/3 ''.
Thus, if the URI\-prefix is
.BR /wset1024 ,
then the URI being accessed would be
.BR /wset1024/0/1/0/3.html .
In other words, the files on the server need to be organized as a
10ary tree.
.SH "OUTPUT"
This section describes the statistics output at the end of each test
run.  The basic information shown below is printed independent of the
selected workload generator.
.PP 
.RS
.br 
.B Total:
connections 30000 requests 29997 replies 29997 test\-duration 299.992 s
.PP 
.B Connection rate:
100.0 conn/s (10.0 ms/conn, <=14 concurrent connections)
.br 
.B Connection time [ms]:
min 1.4 avg 3.0 max 163.4 median 1.5 stddev 7.3
.br 
.B Connection time [ms]:
connect 0.6
.br 
.B Connection length [replies/conn]:
1.000
.PP 
.B Request rate:
100.0 req/s (10.0 ms/req)
.br 
.B Request size [B]:
75.0
.PP 
.B Reply rate [replies/s]:
min 98.8 avg 100.0 max 101.2 stddev 0.3 (60 samples)
.br 
.B Reply time [ms]:
response 2.4 transfer 0.0
.br 
.B Reply size [B]:
header 242.0 content 1010.0 footer 0.0 (total 1252.0)
.br 
.B Reply status:
1xx=0 2xx=29997 3xx=0 4xx=0 5xx=0
.PP 
.B CPU time [s]:
user 94.31 system 205.26 (user 31.4% system 68.4% total 99.9%)
.br 
.B Net I/O:
129.6 KB/s (1.1*10^6 bps)
.PP 
.B Errors:
total 3 client\-timo 0 socket\-timo 0 connrefused 3 connreset 0
.br 
.B Errors:
fd\-unavail 0 addrunavail 0 ftab\-full 0 other 0
.br 
.RE
.PP 
There are six groups of statistics: overall results (``Total''),
connection related results (``Connection''), results relating to the
issuing of HTTP requests (``Request''), results relating to the replies
received from the server (``Reply''), miscellaneous results relating to
the CPU (``CPU'') and network (``Net I/O'') utilization and, last but not
least, a summary of errors encountered (``Errors'').
.TP 
Total Section
.br 
This section summarizes how many TCP connections were initiated by
.BR httperf ,
how many requests it sent out, how many replies it received, and
what the total test duration was.  In the example output
shown above, 30,000 connections were created, 29,997 requests were
sent out and 29,997 replies were received.  The duration of the
test was almost exactly 5 minutes (300 seconds).
.TP 
Connection Section
.br 
This section conveys information related to TCP connections generated
by the tool.  Specifically, the ``Connection rate'' line shows that new
connections were initiated at a rate of 100.0 connections per second.
This rate corresponds to a period of 10.0 milliseconds per
connection.  The last number in this line shows that at most 14
connections were open at any given time.

The first line labeled ``Connection time'' gives lifetime statistics
for successful connections.  The lifetime of a connection is the time
between a TCP connection is initiated and the time the connection is
closed.  A connection is considered successful if it had at least one
call that completed successfully.  In the example output, the line
indicates that the minimum (``min'') connection lifetime was 1.4
milliseconds, the average (``avg'') lifetime was 3.0 milliseconds, the
maximum (``max'') was 163.4 milliseconds, the median (``median'')
lifetime was 1.5 milliseconds, and that the standard deviation of the
lifetimes was 7.3 milliseconds.  The median lifetime is computed based
on a histogram with one millisecond resolution and a maximum lifetime
of 100 seconds.  Thus, the median is accurate to within half a
millisecond if at least half of the successful connections have a
lifetime of no more than 100 seconds.

The next statistic in this section is the average time it took to
establish a TCP connection.  Only successful TCP connection
establishments are counted.  In the example, the second line labeled
``Connection time'' shows that, on average, it took 0.6 milliseconds
to establish a connection.

The final line in this section is labeled ``Connection length.''  It
gives the average number of replies received on each connection that
received at least one reply (i.e., connections that failed before
yielding the first reply are not counted).  This number can be bigger
than 1.0 due to persistent connections.
.TP 
Request Section
.br 
The line labeled ``Request rate'' gives the rate at which HTTP requests
were issued and the period that this rate corresponds to.  In the
example above, the request rate was 100.0 requests per second, which
corresponds to 10.0 milliseconds per request.  As long as no
persistent connections are employed, the results in this section are
very similar or identical to results in the connection section.
However, when persistent connections are used, several calls can be
performed on a single connection in which case the results would be
different.

The line labeled ``Request size'' gives the average size of the HTTP
requests in bytes.  In the example above, the average request size was
75 bytes.
.TP 
Reply Section
.br 
For simple measurements, this section is often the most interesting
one as the line labeled ``Reply rate'' gives various statistics for
the reply rate.  In the example above, the minimum (``min'') reply
rate was 98.8 replies per second, the average (``avg'') was 100
replies per second, and the maximum (``max'') rate was 101.2 replies
per second.  The standard deviation was 0.3 replies per second.  The
number enclosed in parentheses shows that 60 reply rate samples were
acquired.  At present,
.B httperf
collects a rate sample once every five seconds.  To obtain a
meaningful standard deviation, it is recommended to run tests long
enough so at least thirty samples are obtained.  This corresponds to a
test duration of at least 150 seconds.

The line labeled ``Reply Time'' gives information on how long it took
for the server to respond and how long it took to receive the reply.
In the example, it took on average 2.4 milliseconds between sending
the first byte of the request and receiving the first byte of the
reply.  The time to ``transfer'', or read, the reply was too short to
be measured, so it shows up as zero.  The is typical when the entire
reply fits into a single TCP segment.

The next line, labeled ``Reply size'' contains statistics on the
average size of the replies\-\-\-all numbers are in reported bytes.
Specifically, the line lists the average length of reply headers, the
content, and footers (HTTP/1.1 uses footers to realize the ``chunked''
transfer encoding).  For convenience, the average total number of
bytes in the replies is also given in parentheses.  In the example,
the average header length (``header'') was 242 bytes, the average
content length (``content'') was 1010 bytes, and there were no footers
(``footer'' length is zero).  The total reply length of 1252 bytes on
average.

The final line in this section is a histogram of the major status
codes received in the replies from the server.  The major status code
is the ``hundreds''\-digit of the full HTTP status code.  In the
example, all 29,997 replies had a major status code of 2.  It's a good
guess that all status codes were ``200 OK'' but the information in the
histogram is not detailed enough to allow distinguishing status codes
with the same major code.
.TP 
Miscellaneous Section
.br 
This section starts with a summary of the CPU utilization on the
client machine.  In the example, the line labeled ``CPU time'' shows
that 94.31 seconds were spent executing in user mode (``user''),
205.26 seconds were spent executing in system mode (``system'') and
that this corresponds to 31.4% user mode execution and 68.4% system
execution.  The total utilization was 99.9%, which is expected given
that
.B httperf
is a CPU hog.  A total CPU utilization of significantly less than 100%
is a sign that there were competing processes that interfered with the
test.

The line labeled ``Net I/O'' gives the average network throughput in
kilobytes per second (where a kilobyte is 1024 bytes) and in megabits
per second (where a megabit is 10^6 bits).  In the example, an average
network usage of about 129.6 kilobytes per second was sustained.  The
number in parentheses shows that this corresponds to about 1.1
megabits per second.  This network bandwidth is computed based on the
number of bytes sent and received on the TCP connections.  In other
words, it does not account for the network headers or TCP
retransmissions that may have occurred.
.TP 
Errors Section
.br 
The last section contains statistics on the errors that were
encountered during a test.  In the example, the two lines labeled
``Errors'' show that there were a total of three errors and that all
three errors were due to the server refusing to accept a connection
(``connrefused'').  A description of each error counter follows:

.B client\-timo:
The number of times a session, connection, or call failed due
to a client timeout (as specified by the
.B \-\-timeout
and
.BR \-\-think\-timeout )
options.

.B socket\-timo:
The number of times a TCP connection failed with a socket\-level
timeout (ETIMEDOUT).

.B connrefused:
The number of times a TCP connection attempt failed with a
``connection refused by server'' error (ECONNREFUSED).

.B connreset:
The number of times a TCP connection failed due to a RESET from the
server.  Typically, a RESET is received when the client attempts to
send data to the server at a time the server has already closed its
end of the connection.  NT servers also send RESETs when attempting to
establish a new connection when the listen queue is full.

.B fd\-unavail:
The number of times the
.B httperf
process was out of file descriptors.  Whenever this count is non\-zero,
the test results are meaningless because the client was overloaded
(see section "CHOOSING TIMEOUT VALUES").

.B addrunavail:
The number of times the client was out of TCP port numbers
(EADDRNOTAVAIL).  This error should never occur.  If it does, the
results should be discarded.

.B ftab\-full:
The number of times the system's file descriptor table is full.
Again, this error should never occur.  If it does, the results should
be discarded.

.B other:
The number of times some other type of error occurred.  Whenever this
counter is non\-zero, it is necessary to track down the real cause of
the error.  To assist in doing this,
.B httperf
prints the error code (errno) of the first unknown errors that occurs
during a test run.
.RE
.PP 
When
.B \-\-wsess
or
.B \-\-wsesslog
is specified,
.B httperf
generates and measures sessions instead of individual calls and
additional statistics are printed at the end of a test.  An example
output is shown below.
.PP 
.RS
.B Session rate [sess/s]:
min 0.00 avg 0.59 max 2.40 stddev 0.37 (240/450)
.br 
.B Session:
avg 6.45 connections/session
.br 
.B Session lifetime [s]:
123.9
.br 
.B Session failtime [s]:
58.5
.br 
.B Session length histogram:
4 7 4 ... 3 3 240
.RE
.PP 
The line labeled ``Session rate'' shows the minimum, average, and
maximum rate at which sessions completed (based on a 5 second sampling
interval).  It also shows the standard deviation of the session
completion rate.  The numbers in parentheses show how many sessions
succeeded and how many sessions were initiated.  In the example above,
the minimum, average, and maximum session completion rates were 0.00,
0.59, and 2.40 sessions per second, respectively.  The standard
deviation was 0.37 sessions per second and 240 out of 450 sessions
completed successfully (210 failed due to errors such as timeouts).

The next line, labeled ``Session:'' shows the average length of a
session measured in connections.  In the example above, an average of
6.45 connections were required to complete a session.

The line labeled ``Session lifetime'' gives the average time it took
to complete a successful session.  In the example above, it took an
average of 123.9 seconds.

The line labeled ``Session failtime'' gives the average time it took
before an unsuccessful session failed.  In the example above, it took
on average 58.5 seconds for a session to fail.

Finally, the line labeled ``Session length histogram'' gives a
histogram of the number of replies received by each session.  In the
example above, 4 sessions ended after receiving no reply at all, 7
ended after receiving one reply, and so on (the ellipsis indicates
additional histogram counts that were omitted from this manual for
space reasons).  Note that this histogram does not distinguish between
successful and failed sessions.

.SH "CHOOSING TIMEOUT VALUES"
Since the machine that
.B httperf
runs on has only a finite set of resource available, it can not
sustain arbitrarily high HTTP loads.  For example, one limiting factor
is that there are only roughly 60,000 TCP port numbers that can be in
use at any given time.  Since on most UNIX systems it takes one minute
for a TCP connection to be fully closed (leave the TIME_WAIT state),
the maximum rate a client can sustain is at most 1,000 requests per
second.

The actual sustainable rate is often much lower than that because
before running out of TCP ports, the machine is likely to run out of
file descriptors (one file descriptor is used up for each open TCP
connection).  By default, HP\-UX 10.20 allows 1,024 open file
descriptors per process.  This means that without extra precautions,
.B httperf
could potentially very quickly use up all available file descriptors,
at which point it could not induce any additional load on the server.
To avoid this problem,
.B httperf
provides option
.B \-\-timeout
to set a timeout for all communication with the server.  If the server
does not respond before the timeout expires, the client considers the
corresponding session, connection, or call to be ``dead,'' closes the
associated TCP connection, and increases the ``client\-timo'' error
count.  The only exception to this rule is that after sending an
entire request to the server,
.B httperf
allows the server to take some additional time before it starts
sending the reply.  This is to accommodate HTTP requests that take a
long time to complete on the server.  This additional time is called
the ``server think time'' and can be specified by option
.BR \-\-think\-timeout .
By default, this additional think time is zero seconds, so the server
would always have to respond within the time alloted by option
.BR \-\-timeout .

Timeouts allow
.B httperf 
to sustain high offered loads even when the server is overloaded.  For
example, with a timeout of 2 seconds and assuming that 1,000
file\-descriptors are available, the offered load could be up to 500
requests per second (in practice, the sustainable load is often
somewhat smaller than the theoretical value).  On the downside,
timeouts artificially truncate the connection lifetime distribution.
Thus, it is recommended to pick a timeout value that is as large as
possible yet small enough to allow sustaining the desired offered
rate.  A timeout as short as one second may be acceptable, but larger
timeouts (5\-10 seconds) are preferable.

It is important to keep in mind that timeouts do not guarantee that a
client can sustain a particular offered load\-\-\-there are many other
potential resource bottlenecks.  For example, in some cases the client
machine may simply run out of CPU time.  To ensure that a given test
really measured the server's capabilities and not the client's, it is
a good idea to vary the number of machines participating in a test.
If observed performance remains the same as the number of client
machines is varied, the test results are likely to be valid.
.SH "AUTHOR"
.BR httperf
was developed by David Mosberger and was heavily influenced by an
earlier tool written by Tai Jin.  Stephane Eranian contributed the
log\-file based URI generator.  Dick Carter contributed the
.B \-\-wsesslog
workload generator, the support behind the
.B \-\-period
option, and bug fixes.  Ted Bullock has taken over maintenance and 
development activities since September 2006.
.SH "BUGS"
Probably many.  Always be sure to double\-check results and don't fall
prey to measuring client\-performance instead of server performance!
.PP 
The user\-interface definitely could be improved.  A simple workload
description language might be more suitable than the dozens of little
command\-line options the tool has right now.
07070100000011000081A40000000000000000000000015FCD000200000604000000000000000000000000000000000000002A00000000httperf-0.9.0+git.20201206/man/idleconn.1.TH idleconn "1" "Mar 2016" "IDLECONN 0.9.0" "tool for opening any number of idle connections"
.\" Text automatically generated by txt2man
.SH NAME
\fBidleconn \fP- tool for opening any number of idle connections
\fB
.SH SYNOPSIS
.nf
.fam C
 \fBidleconn\fP <server> <port> <numidle>

.fam T
.fi
.fam T
.fi
.SH DESCRIPTION
\fBidleconn\fP is part of httperf suite and is useful to simulate a large number
of concurrent and idle connections. It can establish a set of persistent
connections, each of which generated periodic requests to the server. The
effect is that at all times, some of the connections were active while the
rest were idle, and these active and idle connection sets kept changing with
time. (This paragraph was extracted and adapted from the article "Scalability
of Linux Event-Dispatch Mechanisms" (HPL-2000-174), written by Abhishek
Chandra and David Mosberger).
.SH OPTIONS
.TP
.B
server
IP of the server to connect.
.TP
.B
port
Port used by server.
.TP
.B
numidle
Number of idle process to be generated.
.SH EXAMPLE
This is a simple example how to use \fBidleconn\fP:
.PP
.nf
.fam C
    $ ./idleconn 192.168.1.1 80 100

.fam T
.fi
It would open and maintain 100 idle connections to a web server, listening on
port 80, using the IP address 192.168.1.1.
.SH SEE ALSO
\fBhttperf\fP(1)
.SH AUTHOR
The httperf was written by David Mosberger-Tang, Hewlett-Packard Company and Contributors.
.PP
This manual page was written by Joao Eriberto Mota Filho <eriberto@debian.org>
for the Debian project (but may be used by others).
07070100000012000041ED0000000000000000000000025FCD000200000000000000000000000000000000000000000000001F00000000httperf-0.9.0+git.20201206/src07070100000013000081ED0000000000000000000000015FCD000200000207000000000000000000000000000000000000002B00000000httperf-0.9.0+git.20201206/src/Makefile.amSUBDIRS = gen lib stat

# what flags you want to pass to the C compiler & linker
AM_CFLAGS = -I$(srcdir) -I$(srcdir)/gen -I$(srcdir)/lib -I$(srcdir)/stat

bin_PROGRAMS = httperf

if IDLECONN
bin_PROGRAMS += idleconn
idleconn_SOURCES = idleconn.c
idleconn_LDADD = lib/libutil.a
endif

httperf_SOURCES = httperf.c httperf.h object.c object.h call.c call.h conn.c \
  conn.h sess.c sess.h core.c core.h localevent.c localevent.h http.c http.h \
  timer.c timer.h

httperf_LDADD = gen/libgen.a lib/libutil.a stat/libstat.a
07070100000014000081ED0000000000000000000000015FCD000200000A93000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/src/call.c/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

#include "config.h"

#include <stdio.h>

#include <generic_types.h>
#include <object.h>
#include <call.h>
#include <httperf.h>

static u_long next_id = 0;

void
call_init (Call *c)
{
# define DEFAULT_METHOD	"GET"

  c->id = next_id++;
  call_set_method (c, DEFAULT_METHOD, sizeof (DEFAULT_METHOD) - 1);
  c->req.version = param.http_version;
  c->req.iov[IE_BLANK].iov_base = (caddr_t) " ";
  c->req.iov[IE_BLANK].iov_len = 1;
  c->req.iov[IE_NEWLINE1].iov_base = (caddr_t) "\r\n";
  c->req.iov[IE_NEWLINE1].iov_len = 2;
  c->req.iov[IE_NEWLINE2].iov_base = (caddr_t) "\r\n";
  c->req.iov[IE_NEWLINE2].iov_len = 2;
}

void
call_deinit (Call *call)
{
}

int
call_append_request_header (Call *c, const char *hdr, size_t len)
{
  u_int num_hdrs = c->req.num_extra_hdrs;

  if (num_hdrs >= MAX_EXTRA_HEADERS)
    {
      fprintf (stderr, "%s.call_append_request_header: max headers "
	       "(%d) exceeded.\n", prog_name, MAX_EXTRA_HEADERS);
      return -1;
    }
  c->req.iov[IE_FIRST_HEADER + num_hdrs].iov_base = (caddr_t) hdr;
  c->req.iov[IE_FIRST_HEADER + num_hdrs].iov_len = len;
  c->req.num_extra_hdrs = num_hdrs + 1;
  return 0;
}
07070100000015000081ED0000000000000000000000015FCD000200001339000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/src/call.h/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/
#ifndef call_h
#define call_h

#include <sys/uio.h>

/* Max. # of additional request header lines we allow: */
#define MAX_EXTRA_HEADERS	4

typedef enum IOV_Element
  {
    IE_METHOD,
    IE_BLANK,		/* space separating method from location */
    IE_LOC,		/* for proxy requests only */
    IE_URI,
    IE_PROTL,
    IE_HOST,		/* for the "Host:" header */
    IE_NEWLINE1,
    IE_FIRST_HEADER,
    IE_LAST_HEADER = IE_FIRST_HEADER + MAX_EXTRA_HEADERS - 1,
    IE_NEWLINE2,
    IE_CONTENT,
    IE_LEN		/* must be last */
  }
IOV_Element;

/* I call this a "call" because "transaction" is too long and because
   it's basically a remote procedure call consisting of a request that
   is answered by a reply.  */
typedef struct Call
  {
    Object obj;

    u_long id;			/* unique id */

    /* Connection this call is being sent on.  This pointer is NOT
       reference counted as otherwise we would get a recursive
       dependency between connections and calls....  */
    struct Conn *conn;
    struct Call *sendq_next;
    struct Call *recvq_next;
    Time timeout;		/* used for watchdog management */

    struct
      {
	Time time_send_start;
	Time time_recv_start;
      }
    basic;

    /* the request: */
    struct
      {
	int version;		/* 0x10000*major + minor */
	u_int num_extra_hdrs;	/* number of additional headers in use */
	int iov_index;		/* first iov element that has data */
	size_t size;		/* # of bytes sent */
	struct iovec iov_saved;	/* saved copy of iov[iov_index] */
	struct iovec iov[IE_LEN];
      }
    req;

    /* the reply: */
    struct
      {
	int status;
	int version;		/* 0x10000*major + minor */
	size_t header_bytes;	/* # of header bytes received so far */
	size_t content_bytes;	/* # of reply data bytes received so far */
	size_t footer_bytes;	/* # of footer bytes received so far */
      }
    reply;
  }
Call;

/* Initialize the new call object C.  */
extern void call_init (Call *c);

/* Destroy the call-specific state in call object C.  */
extern void call_deinit (Call *c);

#define call_new()	((Call *) object_new (OBJ_CALL))
#define call_inc_ref(c)	object_inc_ref ((Object *) (c))
#define call_dec_ref(c)	object_dec_ref ((Object *) (c))

/* Append the additional request header line(s) HDR to the request
   headers.  The total length of the additional headers is LEN bytes.
   The headers must be end with a carriage-return, line-feed sequence
   ("\r\n").  */
extern int call_append_request_header (Call *c, const char *hdr, size_t len);

#define call_set_method(c, method, method_len)			\
  do								\
    {								\
      c->req.iov[IE_METHOD].iov_base = (caddr_t) method;	\
      c->req.iov[IE_METHOD].iov_len = method_len;		\
    }								\
  while (0)

#define call_set_location(c, loc, loc_len)		\
  do							\
    {							\
      c->req.iov[IE_LOC].iov_base = (caddr_t) loc;	\
      c->req.iov[IE_LOC].iov_len = loc_len;		\
    }							\
  while (0)

#define call_set_uri(c, uri, uri_len)			\
  do							\
    {							\
      c->req.iov[IE_URI].iov_base = (caddr_t) uri;	\
      c->req.iov[IE_URI].iov_len = uri_len;		\
    }							\
  while (0)

#define call_set_contents(c, content, content_len)		\
  do								\
    {								\
      c->req.iov[IE_CONTENT].iov_base = (caddr_t) content;	\
      c->req.iov[IE_CONTENT].iov_len = content_len;		\
    }								\
  while (0)

#endif /* call_h */
07070100000016000081ED0000000000000000000000015FCD000200001076000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/src/conn.c/*
 * httperf -- a tool for measuring web server performance
 * Copyright 2000-2007 Hewlett-Packard Company
 *
 * This file is part of httperf, a web server performance measurment
 * tool.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of this work with the OpenSSL project's
 * "OpenSSL" library (or with modified versions of it that use the same
 * license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
 * file, but you are not obligated to do so.  If you do not wish to do
 * so, delete this exception statement from your version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA
 */

#include "config.h"

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/mman.h>
#include <sys/stat.h>

#include <generic_types.h>

#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <core.h>

static char *srvbase, *srvend, *srvcurrent;

void
conn_add_servers(void)
{
	struct stat st;
	int fd, len;

	fd = open(param.servers, O_RDONLY, 0);
	if (fd == -1)
		panic("%s: can't open %s\n", prog_name, param.servers);

	fstat(fd, &st);
	if (st.st_size == 0)
		panic("%s: file %s is empty\n", prog_name, param.servers);

	srvbase = (char *)mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
	if (srvbase == (char *)-1)
		panic("%s: can't mmap the file: %s\n", prog_name, strerror(errno));

	close(fd);

	srvend = srvbase + st.st_size;
	for (srvcurrent = srvbase; srvcurrent < srvend; srvcurrent += len + 1) {
		len = strlen(srvcurrent);
		core_addr_intern(srvcurrent, len, param.port);
	}
	srvcurrent = srvbase;
}

void
conn_init(Conn *conn)
{
	if (param.servers) {
		int len = strlen(srvcurrent);
		conn->hostname = srvcurrent;
		conn->hostname_len = len;
		conn->fqdname = conn->hostname;
		conn->fqdname_len = conn->hostname_len;

		srvcurrent += len + 1;
		if (srvcurrent >= srvend)
			srvcurrent = srvbase;
	} else if (param.server_name) {
		conn->hostname = param.server;
		conn->hostname_len = strlen(param.server);
		conn->fqdname = param.server_name;
		conn->fqdname_len = strlen(param.server_name);
	} else {
		conn->hostname = param.server;
		conn->hostname_len = strlen(param.server);
		conn->fqdname = conn->hostname;
		conn->fqdname_len = conn->hostname_len;
	}

	conn->port = param.port;
	conn->sd = -1;
	conn->myport = -1;
	conn->line.iov_base = conn->line_buf;
	conn->fqdname_len = strcspn(conn->fqdname,"\r\n"); // Chomp since used in Host header record

#ifdef HAVE_SSL
	if (param.use_ssl) {
		conn->ssl = SSL_new(ssl_ctx);
		if (!conn->ssl) {
			ERR_print_errors_fp(stderr);
			exit(-1);
		}

		if (param.tls_server_name)
		{
			SSL_set_tlsext_host_name(conn->ssl, param.tls_server_name);
		}
		
		if (param.ssl_cipher_list) {
			/* set order of ciphers */
			int ssl_err = SSL_set_cipher_list(conn->ssl, param.ssl_cipher_list);

			if (DBG > 2)
				fprintf(stderr,
					"core_ssl_connect: set_cipher_list returned %d\n",
					ssl_err);
		}
	}
#endif
}

void
conn_deinit(Conn *conn)
{
	assert(conn->sd < 0 && conn->state != S_FREE);
	assert(!conn->sendq);
	assert(!conn->recvq);
	assert(!conn->watchdog);
	conn->state = S_FREE;

#ifdef HAVE_SSL
	if (param.use_ssl)
		SSL_free(conn->ssl);
#endif
}
07070100000017000081ED0000000000000000000000015FCD00020000108C000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/src/conn.h/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/
#ifndef conn_h
#define conn_h

#include "config.h"

#include <sys/uio.h>

#ifdef HAVE_SSL
# include <openssl/ssl.h>
# include <openssl/err.h>
#endif

/* Maximum header line length that we can process properly.  Longer
   lines will be treated as if they were only this long (i.e., they
   will be truncated).  */
#define MAX_HDR_LINE_LEN	1024

struct Call;

typedef enum Conn_State
  {
    S_INITIAL,
    S_CONNECTING,
    S_CONNECTED,
    S_REPLY_STATUS,
    S_REPLY_HEADER,
    S_REPLY_CONTINUE,
    S_REPLY_DATA,
    S_REPLY_CHUNKED,
    S_REPLY_FOOTER,
    S_REPLY_DONE,
    S_CLOSING,
    S_FREE
  }
Conn_State;

struct local_addr;

typedef struct Conn
  {
    Object obj;

    Conn_State state;
    struct Conn *next;
    struct Call *sendq;		/* calls whose request needs to be sent */
    struct Call *sendq_tail;
    struct Call *recvq;		/* calls waiting for a reply */
    struct Call *recvq_tail;
    struct Timer *watchdog;

    struct
      {
	Time time_connect_start;	/* time connect() got called */
	u_int num_calls_completed;	/* # of calls that completed */
      }
    basic;			/* maintained by stat/stats_basic.c */

    size_t hostname_len;
    const char *hostname;	/* server's hostname (or 0 for default) */
    size_t fqdname_len;
    const char *fqdname;	/* fully qualified server name (or 0) */
    int port;			/* server's port (or -1 for default) */
    int	sd;			/* socket descriptor */
    int myport;			/* local port number or -1 */
    struct local_addr *myaddr;
    /* Since replies are read off the socket sequentially, much of the
       reply-processing related state can be kept here instead of in
       the reply structure: */
    struct iovec line;		/* buffer used to parse reply headers */
    size_t content_length;	/* content length (or INF if unknown) */
    u_int has_body : 1;		/* does reply have a body? */
    u_int is_chunked : 1;	/* is the reply chunked? */
    u_int reading : 1;
    u_int writing : 1;
    char line_buf[MAX_HDR_LINE_LEN];	/* default line buffer */

#ifdef HAVE_SSL
    SSL *ssl;			/* SSL connection info */
#endif
#ifdef HAVE_EPOLL
    int epoll_added;	/* is fd added into epoll? */
#endif
  }
Conn;

extern int max_num_conn;
extern Conn *conn;

/* Store the servers to connect to in memory. */
extern void conn_add_servers (void);

/* Initialize the new connection object C.  */
extern void conn_init (Conn *c);

/* Destroy the connection-specific state in connection object C.  */
extern void conn_deinit (Conn *c);

#define conn_new()	((Conn *) object_new (OBJ_CONN))
#define conn_inc_ref(c)	object_inc_ref ((Object *) (c))
#define conn_dec_ref(c)	object_dec_ref ((Object *) (c))

#endif /* conn_h */
07070100000018000081ED0000000000000000000000015FCD000200009968000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/src/core.c/*
 * Copyright (C) 2000-2007 Hewlett-Packard Company
 * 
 * This file is part of httperf, a web server performance measurment tool.
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 * 
 * In addition, as a special exception, the copyright holders give permission
 * to link the code of this work with the OpenSSL project's "OpenSSL" library
 * (or with modified versions of it that use the same license as the "OpenSSL" 
 * library), 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 this file, you may extend this
 * exception to your version of the file, but you are not obligated to do so.
 * If you do not wish to do so, delete this exception statement from your
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 
 */

#include "config.h"

#ifdef __FreeBSD__
#define	HAVE_KEVENT
#endif

#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifdef HAVE_EPOLL
#include <sys/epoll.h>
#endif

#ifdef HAVE_KEVENT
#include <sys/event.h>

/*
 * Older systems using kevent() always specify the time in
 * milliseconds and do not have a flag to select a different scale.
 */
#ifndef NOTE_MSECONDS
#define	NOTE_MSECONDS		0
#endif
#endif

#ifdef __FreeBSD__
#include <ifaddrs.h>
#endif
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>

#include <generic_types.h>
#include <sys/resource.h>	/* after sys/types.h for BSD (in generic_types.h) */

#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <core.h>
#include <localevent.h>
#include <http.h>

#define HASH_TABLE_SIZE	1024	/* can't have more than this many servers */
#define MIN_IP_PORT	IPPORT_RESERVED
#define MAX_IP_PORT	65535
#define BITSPERLONG	(8*sizeof (u_long))

struct local_addr {
	struct in_addr ip;
	u_long port_free_map[((MAX_IP_PORT - MIN_IP_PORT + BITSPERLONG)
		    / BITSPERLONG)];
	u_long mask;
	int previous;
};

struct address_pool {
	struct local_addr *addresses;
	int count;
	int last;
};

static volatile int      running = 1;
static int      iteration;
static u_long   max_burst_len;
#ifdef HAVE_KEVENT
static int	kq, max_sd = 0;
#else
#ifdef HAVE_EPOLL
#define	EPOLL_N_MAX		8192
static int epoll_fd, max_sd = 0;
static struct epoll_event *epoll_events;
static int epoll_timeout;
#else
static fd_set   rdfds, wrfds;
static int      min_sd = 0x7fffffff, max_sd = 0, alloced_sd_to_conn = 0;
static struct timeval select_timeout;
#endif
#endif
static struct sockaddr_in myaddr;
static struct address_pool myaddrs;
#ifndef HAVE_KEVENT
Conn          **sd_to_conn;
#endif
static char     http10req[] =
    " HTTP/1.0\r\nUser-Agent: httperf/" VERSION
    "\r\nConnection: keep-alive\r\nHost: ";
static char     http11req[] =
    " HTTP/1.1\r\nUser-Agent: httperf/" VERSION "\r\nHost: ";

static char     http10req_nohost[] =
    " HTTP/1.0\r\nUser-Agent: httperf/" VERSION
    "\r\nConnection: keep-alive";
static char     http11req_nohost[] =
    " HTTP/1.1\r\nUser-Agent: httperf/" VERSION;

#ifndef SOL_TCP
# define SOL_TCP 6		/* probably ought to do getprotlbyname () */
#endif

#ifdef TIME_SYSCALLS
# define SYSCALL(n,s)							\
  {									\
    Time start, stop;							\
    do									\
      {									\
	errno = 0;							\
	start = timer_now_forced ();					\
	s;				 /* execute the syscall */	\
	stop = timer_now_forced ();					\
	syscall_time[SC_##n] += stop - start;				\
	++syscall_count[SC_##n];					\
      }									\
    while (errno == EINTR);						\
  }

enum Syscalls {
	SC_BIND, SC_CONNECT, SC_READ, SC_SELECT, SC_SOCKET, SC_WRITEV,
	SC_SSL_READ, SC_SSL_WRITEV, SC_KEVENT,
	SC_EPOLL_CREATE, SC_EPOLL_CTL, SC_EPOLL_WAIT,
	SC_NUM_SYSCALLS
};

static const char *const syscall_name[SC_NUM_SYSCALLS] = {
	"bind", "connct", "read", "select", "socket", "writev",
	"ssl_read", "ssl_writev", "kevent",
	"epoll_create", "epoll_ctl", "epoll_wait"
};
static Time     syscall_time[SC_NUM_SYSCALLS];
static u_int    syscall_count[SC_NUM_SYSCALLS];
#else
# define SYSCALL(n,s)				\
  {						\
    do						\
      {						\
	errno = 0;				\
	s;					\
      }						\
    while (errno == EINTR);			\
  }
#endif

struct hash_entry {
	const char     *hostname;
	int             port;
	struct sockaddr_in sin;
} hash_table[HASH_TABLE_SIZE];

static int
hash_code(const char *server, size_t server_len, int port)
{
	u_char         *cp = (u_char *) server;
	u_long          h = port;
	u_long          g;
	int             ch;

	/*
	 * Basically the ELF hash algorithm: 
	 */

	while ((ch = *cp++) != '\0') {
		h = (h << 4) + ch;
		if ((g = (h & 0xf0000000)) != 0) {
			h ^= g >> 24;
			h &= ~g;
		}
	}
	return h;
}

static struct hash_entry *
hash_enter(const char *server, size_t server_len, int port,
	   struct sockaddr_in *sin)
{
	struct hash_entry *he;

	int             index =
	    hash_code(server, server_len, port) % HASH_TABLE_SIZE;

	while (hash_table[index].hostname) {
		++index;
		if (index >= HASH_TABLE_SIZE)
			index = 0;
	}
	he = hash_table + index;
	he->hostname = server;
	he->port = port;
	he->sin = *sin;
	return he;
}

static struct sockaddr_in *
hash_lookup(const char *server, size_t server_len, int port)
{
	int             index, start_index;

	index = start_index =
	    hash_code(server, server_len, port) % HASH_TABLE_SIZE;
	while (hash_table[index].hostname) {
		if (hash_table[index].port == port
		    && strcmp(hash_table[index].hostname, server) == 0)
			return &hash_table[index].sin;

		++index;
		if (index >= HASH_TABLE_SIZE)
			index = 0;
		if (index == start_index)
			break;
	}
	return 0;
}

static int
lffs(long w)
{
#ifdef __FreeBSD__
	return ffsl(w);
#else
	int             r;

	if (sizeof(w) == sizeof(int))
		r = ffs(w);
	else {
		r = ffs(w);
#if SIZEOF_LONG > 4
		if (r == 0) {
			r = ffs(w >> (8 * sizeof(int)));
			if (r > 0)
				r += 8 * sizeof(int);
		}
#endif
	}
	return r;
#endif
}

static void
port_put(struct local_addr *addr, int port)
{
	int             i, bit;

	port -= MIN_IP_PORT;
	i = port / BITSPERLONG;
	bit = port % BITSPERLONG;
	addr->port_free_map[i] |= (1UL << bit);
}

static int
port_get(struct local_addr *addr)
{
	int             port, bit, i;

	i = addr->previous;
	if ((addr->port_free_map[i] & addr->mask) == 0) {
		do {
			++i;
			if (i >= NELEMS(addr->port_free_map))
				i = 0;
			if (i == addr->previous) {
				if (DBG > 0)
					fprintf(stderr,
						"%s.port_get: Yikes! I'm out of port numbers!\n",
						prog_name);
				return -1;
			}
		}
		while (addr->port_free_map[i] == 0);
		addr->mask = ~0UL;
	}
	addr->previous = i;

	bit = lffs(addr->port_free_map[i] & addr->mask) - 1;
	if (bit >= BITSPERLONG - 1)
		addr->mask = 0;
	else
		addr->mask = ~((1UL << (bit + 1)) - 1);
	addr->port_free_map[i] &= ~(1UL << bit);
	port = bit + i * BITSPERLONG + MIN_IP_PORT;
	return port;
}

static void
conn_failure(Conn * s, int err)
{
	Any_Type        arg;

	arg.l = err;
	event_signal(EV_CONN_FAILED, (Object *) s, arg);

	core_close(s);
}

static void
conn_timeout(struct Timer *t, Any_Type arg)
{
	Conn           *s = arg.vp;
	Time            now;
	Call           *c;

	assert(object_is_conn(s));
	s->watchdog = 0;

	if (DBG > 0) {
		c = 0;
		if (s->sd >= 0) {
			now = timer_now();
			if (s->reading
				&& s->recvq && now >= s->recvq->timeout)
				c = s->recvq;
			else if (s->writing
				&& s->sendq && now >= s->sendq->timeout)
				c = s->sendq;
		}
		if (DBG > 0) {
			fprintf(stderr, "connection_timeout");
			if (c)
				fprintf(stderr, ".%lu", c->id);
			fprintf(stderr, ": t=%p, connection=%p\n", t, s);
		}
	}

	arg.l = 0;
	event_signal(EV_CONN_TIMEOUT, (Object *) s, arg);

	core_close(s);
}

enum IO_DIR { READ, WRITE };

static void
clear_active(Conn * s, enum IO_DIR dir)
{
 	int             sd = s->sd;
#ifdef HAVE_KEVENT
	struct kevent	ev;

	EV_SET(&ev, sd, dir == WRITE ? EVFILT_WRITE : EVFILT_READ, EV_DELETE,
	    0, 0, s);
	if (kevent(kq, &ev, 1, NULL, 0, NULL) < 0) {
		fprintf(stderr, "failed to add %s filter\n", write ?
		    "write" : "read");
		exit(1);
	}
#else
#ifdef HAVE_EPOLL
	struct epoll_event ev;
	int error;

	if (dir == WRITE)
		ev.events = EPOLLIN;
	else
		ev.events = EPOLLOUT;
	ev.data.ptr = s;

	error = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, sd, &ev);
	if (error < 0) {
		error = errno;
		fprintf(stderr, "failed to EPOLL_CTL_DEL\n");
		exit(1);
	}
#else
	fd_set *	fdset;
	
	if (dir == WRITE)
		fdset = &wrfds;
	else
		fdset = &rdfds;
	FD_CLR(sd, fdset);
#endif
#endif
	if (dir == WRITE)
		s->writing = 0;
	else
		s->reading = 0;
}

static void
set_active(Conn * s, enum IO_DIR dir)
{
 	int             sd = s->sd;
	Any_Type        arg;
	Time            timeout;
#ifdef HAVE_KEVENT
	struct kevent	ev;

	EV_SET(&ev, sd, dir == WRITE ? EVFILT_WRITE : EVFILT_READ, EV_ADD,
	    0, 0, s);
	if (kevent(kq, &ev, 1, NULL, 0, NULL) < 0) {
		fprintf(stderr, "failed to add %s filter\n", write ?
		    "write" : "read");
		exit(1);
	}
#else
#ifdef HAVE_EPOLL
	struct epoll_event ev;
	int error;

	if (dir == WRITE)
		ev.events = EPOLLOUT;
	else
		ev.events = EPOLLIN;
	ev.data.ptr = s;

	if (s->epoll_added)
		error = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, sd, &ev);
	else {
		error = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sd, &ev);
		s->epoll_added = 1;
	}
	if (error < 0) {
		error = errno;
		fprintf(stderr, "failed to EPOLL_CTL_MOD\n");
		exit(1);
	}
#else
	fd_set *	fdset;
	
	if (dir == WRITE)
		fdset = &wrfds;
	else
		fdset = &rdfds;
	FD_SET(sd, fdset);
	if (sd < min_sd)
		min_sd = sd;
#endif
#endif
	if (sd >= max_sd)
		max_sd = sd;
	if (dir == WRITE)
		s->writing = 1;
	else
		s->reading = 1;

	if (s->watchdog)
		return;

	timeout = 0.0;
	if (s->sendq)
		timeout = s->sendq->timeout;
	if (s->recvq && (timeout == 0.0 || timeout > s->recvq->timeout))
		timeout = s->recvq->timeout;

	if (timeout > 0.0) {
		arg.vp = s;
		s->watchdog = timer_schedule(conn_timeout, arg,
					     timeout - timer_now());
	}
}

static void
do_send(Conn * conn)
{
	int             async_errno;
	socklen_t       len;
	struct iovec   *iovp;
	int             sd = conn->sd;
	ssize_t         nsent = 0;
	Any_Type        arg;
	Call           *call;

	while (1) {
		call = conn->sendq;
		assert(call);

		arg.l = 0;
		event_signal(EV_CALL_SEND_RAW_DATA, (Object *) call, arg);

#ifdef HAVE_SSL
		if (param.use_ssl) {
			extern ssize_t  SSL_writev(SSL *, const struct iovec *,
						   int);
			SYSCALL(SSL_WRITEV, nsent =
				SSL_writev(conn->ssl,
					   call->req.iov + call->req.iov_index,
					   (NELEMS(call->req.iov)
					    - call->req.iov_index)));
		} else
#endif
		{
			SYSCALL(WRITEV,
				nsent =
				writev(sd, call->req.iov + call->req.iov_index,
				       (NELEMS(call->req.iov)
					- call->req.iov_index)));
		}

		if (DBG > 0)
			fprintf(stderr, "do_send.%lu: wrote %ld bytes on %p\n",
				call->id, (long) nsent, conn);

		if (nsent < 0) {
			if (errno == EAGAIN)
				return;

			len = sizeof(async_errno);
			if (getsockopt
			    (sd, SOL_SOCKET, SO_ERROR, &async_errno, &len) == 0
			    && async_errno != 0)
				errno = async_errno;

			if (DBG > 0)
				fprintf(stderr,
					"%s.do_send: writev() failed: %s\n",
					prog_name, strerror(errno));

			conn_failure(conn, errno);
			return;
		}

		call->req.size += nsent;

		iovp = call->req.iov + call->req.iov_index;
		while (iovp < call->req.iov + NELEMS(call->req.iov)) {
			if (nsent < iovp->iov_len) {
				iovp->iov_len -= nsent;
				iovp->iov_base =
				    (caddr_t) ((char *) iovp->iov_base +
					       nsent);
				break;
			} else {
				/*
				 * we're done with this fragment: 
				 */
				nsent -= iovp->iov_len;
				*iovp = call->req.iov_saved;
				++iovp;
				call->req.iov_saved = *iovp;
			}
		}
		call->req.iov_index = iovp - call->req.iov;
		if (call->req.iov_index < NELEMS(call->req.iov)) {
			/*
			 * there are more header bytes to write 
			 */
			call->timeout =
			    param.timeout ? timer_now() + param.timeout : 0.0;
			set_active(conn, WRITE);
			return;
		}

		/*
		 * we're done with sending this request 
		 */
		conn->sendq = call->sendq_next;
		if (!conn->sendq) {
			conn->sendq_tail = 0;
			clear_active(conn, WRITE);
		}
		arg.l = 0;
		event_signal(EV_CALL_SEND_STOP, (Object *) call, arg);
		if (conn->state >= S_CLOSING) {
			call_dec_ref(call);
			return;
		}

		/*
		 * get ready to receive matching reply (note that we
		 * implicitly pass on the reference to the call from the sendq 
		 * to the recvq): 
		 */
		call->recvq_next = 0;
		if (!conn->recvq)
			conn->recvq = conn->recvq_tail = call;
		else {
			conn->recvq_tail->recvq_next = call;
			conn->recvq_tail = call;
		}
		call->timeout = param.timeout + param.think_timeout;
		if (call->timeout > 0.0)
			call->timeout += timer_now();
		set_active(conn, READ);
		if (conn->state < S_REPLY_STATUS)
			conn->state = S_REPLY_STATUS;	/* expecting reply
							 * status */

		if (!conn->sendq)
			return;

		arg.l = 0;
		event_signal(EV_CALL_SEND_START, (Object *) conn->sendq, arg);
		if (conn->state >= S_CLOSING)
			return;
	}
}

static void
recv_done(Call * call)
{
	Conn           *conn = call->conn;
	Any_Type        arg;

	conn->recvq = call->recvq_next;
	if (!conn->recvq) {
		clear_active(conn, READ);
		conn->recvq_tail = 0;
	}
	/*
	 * we're done with receiving this request 
	 */
	arg.l = 0;
	event_signal(EV_CALL_RECV_STOP, (Object *) call, arg);

	call_dec_ref(call);
}

static void
do_recv(Conn * s)
{
	char           *cp, buf[8193];
	Call           *c = s->recvq;
	int             i, saved_errno;
	ssize_t         nread = 0;
	size_t          buf_len;

	assert(c);

#ifdef HAVE_SSL
	if (param.use_ssl) {
		SYSCALL(SSL_READ,
			nread = SSL_read(s->ssl, buf, sizeof(buf) - 1));
	} else
#endif
	{
		SYSCALL(READ, nread = read(s->sd, buf, sizeof(buf) - 1));
	}
	saved_errno = errno;
	if (nread <= 0) {
		if (DBG > 0) {
			fprintf(stderr,
				"do_recv.%lu: received %lu reply bytes on %p\n",
				c->id,
				(u_long) (c->reply.header_bytes +
					  c->reply.content_bytes), s);
			if (nread < 0)
				fprintf(stderr,
					"%s.do_recv: read() failed: %s\n",
					prog_name, strerror(saved_errno));
		}
		if (nread < 0) {
			if (saved_errno != EAGAIN)
				conn_failure(s, saved_errno);
		} else if (s->state != S_REPLY_DATA)
			conn_failure(s, ECONNRESET);
		else {
			if (s->state < S_CLOSING)
				s->state = S_REPLY_DONE;
			recv_done(c);
		}
		return;
	}
	buf[nread] = '\0';	/* ensure buffer is '\0' terminated */

	if (DBG > 3) {
		/*
		 * dump received data in hex & ascii: 
		 */

		fprintf(stderr, "do_recv.%lu: received reply data:\n", c->id);
		for (cp = buf; cp < buf + nread;) {
			fprintf(stderr, "  %04x:",
				(int) (c->reply.header_bytes +
				       c->reply.content_bytes + (cp - buf)));
			for (i = 0; i < 16 && i < buf + nread - cp; ++i)
				fprintf(stderr, " %02x", cp[i] & 0xff);
			i *= 3;
			while (i++ < 50)
				fputc(' ', stderr);
			for (i = 0; i < 16 && cp < buf + nread; ++i, ++cp)
				fprintf(stderr, "%c",
					isprint(*cp) ? *cp : '.');
			fprintf(stderr, "\n");
		}
	}

	/*
	 * process the replies in this buffer: 
	 */

	buf_len = nread;
	cp = buf;
	do {
		c = s->recvq;
		assert(c);
		/*
		 * sets right start time, but doesn't update each packet 
		 */
		if (s->state == S_REPLY_STATUS) {
			c->timeout = param.timeout + param.think_timeout;
			if (c->timeout > 0.0)
				c->timeout += timer_now();
		}

		http_process_reply_bytes(c, &cp, &buf_len);
		if (s->state == S_REPLY_DONE) {
			recv_done(c);
			if (s->state >= S_CLOSING)
				return;

			s->state = S_REPLY_STATUS;
		}
	}
	while (buf_len > 0);

	if (s->recvq)
		set_active(c->conn, READ);
}

struct sockaddr_in *
core_addr_intern(const char *server, size_t server_len, int port)
{
	struct sockaddr_in sin;
	struct hash_entry *h;
	struct hostent *he;
	Any_Type        arg;

	memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);

	arg.cvp = server;
	event_signal(EV_HOSTNAME_LOOKUP_START, 0, arg);
	he = gethostbyname(server);
	event_signal(EV_HOSTNAME_LOOKUP_STOP, 0, arg);
	if (he) {
		if (he->h_addrtype != AF_INET
		    || he->h_length != sizeof(sin.sin_addr)) {
			fprintf(stderr,
				"%s: can't deal with addr family %d or size %d\n",
				prog_name, he->h_addrtype, he->h_length);
			exit(1);
		}
		memcpy(&sin.sin_addr, he->h_addr_list[0],
		       sizeof(sin.sin_addr));
	} else {
		if (!inet_aton(server, &sin.sin_addr)) {
			fprintf(stderr,
				"%s.core_addr_intern: invalid server address %s\n",
				prog_name, server);
			exit(1);
		}
	}
	h = hash_enter(server, server_len, port, &sin);
	if (!h)
		return 0;
	return &h->sin;
}

static void
core_add_address(struct in_addr ip)
{
	struct local_addr *addr;

	myaddrs.addresses = realloc(myaddrs.addresses,
	    sizeof(struct local_addr) * (myaddrs.count + 1));
	if (myaddrs.addresses == NULL) {
		fprintf(stderr,
			"%s: out of memory parsing address list\n",
			prog_name);
		exit(1);
	}
	addr = &myaddrs.addresses[myaddrs.count];
	addr->ip = ip;
	memset(&addr->port_free_map, 0xff, sizeof(addr->port_free_map));
	addr->mask = ~0UL;
	addr->previous = 0;
	myaddrs.count++;
}

/*
 * Parses the value provided to --myaddr.  A value can either be a
 * hostname or IP, or an IP range.  Multiple values can be specified
 * in which case all matches are added to a pool which new connections
 * use in a round-robin fashion.  An interface name may also be
 * specified in which case all IP addresses assigned to that interface
 * are used.
 */
void
core_add_addresses(const char *spec)
{
	struct hostent *he;
	struct in_addr ip;
#ifdef __FreeBSD__
	struct ifaddrs *iflist, *ifa;
#endif
	char *cp;

	/* First try to resolve the argument as a hostname. */
	he = gethostbyname(spec);
	if (he) {
		if (he->h_addrtype != AF_INET ||
		    he->h_length != sizeof(struct in_addr)) {
			fprintf(stderr,
				"%s: can't deal with addr family %d or size %d\n",
				prog_name, he->h_addrtype, he->h_length);
			exit(1);
		}
		core_add_address(*(struct in_addr *)he->h_addr_list[0]);
		return;
	}

	/* If there seems to be an IP range, try that next. */
	cp = strchr(spec, '-');
	if (cp != NULL) {
		char *start_s;
		struct in_addr end_ip;

		start_s = strndup(spec, cp - spec);
		if (!inet_aton(start_s, &ip)) {
			fprintf(stderr, "%s: invalid starting address %s\n",
			    prog_name, start_s);
			exit(1);
		}
		if (!inet_aton(cp + 1, &end_ip)) {
			fprintf(stderr, "%s: invalid ending address %s\n",
			    prog_name, cp + 1);
			exit(1);
		}

		while (ip.s_addr != end_ip.s_addr) {
			core_add_address(ip);
			ip.s_addr += htonl(1);
		}
		core_add_address(end_ip);
		return;
	}

	/* Check for a single IP. */
	if (inet_aton(spec, &ip)) {
		core_add_address(ip);
		return;
	}

#ifdef __FreeBSD__
	/* Check for an interface name. */
	if (getifaddrs(&iflist) == 0) {
		int found;

		found = 0;
		for (ifa = iflist; ifa != NULL; ifa = ifa->ifa_next) {
			if (strcmp(ifa->ifa_name, spec) != 0)
				continue;
			if (found == 0)
				found = 1;
			if (ifa->ifa_addr->sa_family != AF_INET)
				continue;
			found = 2;
			core_add_address(
			    ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr);
		}
		freeifaddrs(iflist);
		if (found == 2)
			return;
		if (found == 1) {
			fprintf(stderr,
			    "%s: no valid addresses found on interface %s\n",
			    prog_name, spec);
			exit(1);
		}
	}
#endif

	fprintf(stderr, "%s: invalid address list %s\n",
	    prog_name, spec);
	exit(1);
}

static struct local_addr *
core_get_next_myaddr(void)
{
	struct local_addr *addr;

	assert(myaddrs.last >= 0 && myaddrs.last < myaddrs.count);
	addr = &myaddrs.addresses[myaddrs.last];
	myaddrs.last++;
	if (myaddrs.last == myaddrs.count)
		myaddrs.last = 0;
	return (addr);
}

static void
core_runtime_timer(struct Timer *t, Any_Type arg)
{

	core_exit();
}

void
core_init(void)
{
	struct rlimit   rlimit;
	Any_Type        arg;

	memset(&hash_table, 0, sizeof(hash_table));
#if !defined(HAVE_KEVENT) && !defined(HAVE_EPOLL)
	memset(&rdfds, 0, sizeof(rdfds));
	memset(&wrfds, 0, sizeof(wrfds));
#endif
	memset(&myaddr, 0, sizeof(myaddr));
#ifdef __FreeBSD__
	myaddr.sin_len = sizeof(myaddr);
#endif
	myaddr.sin_family = AF_INET;
	myaddr.sin_addr.s_addr = htonl(INADDR_ANY);

	if (myaddrs.count == 0)
		core_add_address(myaddr.sin_addr);

	/*
	 * Don't disturb just because a TCP connection closed on us... 
	 */
	signal(SIGPIPE, SIG_IGN);

#ifdef HAVE_KEVENT
	kq = kqueue();
	if (kq < 0) {
		fprintf(stderr,
		    "%s: failed to create kqueue: %s", prog_name,
		    strerror(errno));
		exit(1);
	}

	/*
	 * TIMER_INTERVAL doesn't exist anymore, so just take a wild
	 * guess.
	 */
	struct kevent ev;
	EV_SET(&ev, 0, EVFILT_TIMER, EV_ADD, NOTE_MSECONDS, 1, NULL);
	if (kevent(kq, &ev, 1, NULL, 0, NULL) < 0) {
		fprintf(stderr,
		    "%s: failed to add timer event: %s", prog_name,
		    strerror(errno));
	}	
#else
#ifdef HAVE_EPOLL
	epoll_fd = epoll_create(EPOLL_N_MAX);
	if (epoll_fd < 0) {
		fprintf(stderr,
		    "%s: failed to create epoll: %s", prog_name,
		    strerror(errno));
		exit(1);
	}
	epoll_events = calloc(EPOLL_N_MAX, sizeof(struct epoll_event));
	if (epoll_events == NULL) {
		fprintf(stderr,
		    "%s: failed to create epoll_events: %s", prog_name,
		    strerror(errno));
		exit(1);
	}
	epoll_timeout = 0;
#else
#ifdef DONT_POLL
	/*
	 * This causes select() to take several milliseconds on both Linux/x86 
	 * and HP-UX 10.20.  
	 */
	select_timeout.tv_sec = (u_long) TIMER_INTERVAL;
	select_timeout.tv_usec = (u_long) (TIMER_INTERVAL * 1e6);
#else
	/*
	 * This causes httperf to become a CPU hog as it polls for
	 * filedescriptors to become readable/writable.  This is OK as long as 
	 * httperf is the only (interesting) user-level process that executes
	 * on a machine.  
	 */
	select_timeout.tv_sec = 0;
	select_timeout.tv_usec = 0;
#endif
#endif
#endif

	/*
	 * boost open file limit to the max: 
	 */
	if (getrlimit(RLIMIT_NOFILE, &rlimit) < 0) {
		fprintf(stderr,
			"%s: failed to get number of open file limit: %s",
			prog_name, strerror(errno));
		exit(1);
	}

	rlimit.rlim_cur = rlimit.rlim_max;
	if (setrlimit(RLIMIT_NOFILE, &rlimit) < 0) {
		fprintf(stderr,
			"%s: failed to increase number of open file limit: %s",
			prog_name, strerror(errno));
		exit(1);
	}

	if (verbose)
		printf("%s: maximum number of open descriptors = %ld\n",
		       prog_name, rlimit.rlim_max);

	if (param.servers)
		conn_add_servers();
	else if (param.server)
		core_addr_intern(param.server, strlen(param.server), param.port);

	if (param.runtime) {
		arg.l = 0;
		timer_schedule(core_runtime_timer, arg, param.runtime);
	}
}

#ifdef HAVE_SSL

void
core_ssl_connect(Conn * s)
{
	Any_Type        arg;
	int             ssl_err;

	if (DBG > 2)
		fprintf(stderr, "core_ssl_connect(conn=%p)\n", (void *) s);

	if (SSL_set_fd(s->ssl, s->sd) == 0) {
		ERR_print_errors_fp(stderr);
		exit(-1);
	}

	ssl_err = SSL_connect(s->ssl);
	if (ssl_err < 0) {
		int             reason = SSL_get_error(s->ssl, ssl_err);

		if (reason == SSL_ERROR_WANT_READ
		    || reason == SSL_ERROR_WANT_WRITE) {
			if (DBG > 2)
				fprintf(stderr,
					"core_ssl_connect: want to %s more...\n",
					(reason ==
					 SSL_ERROR_WANT_READ) ? "read" :
					"write");
			if (reason == SSL_ERROR_WANT_READ
			    && !s->reading) {
				clear_active(s, WRITE);
				set_active(s, READ);
			} else if (reason == SSL_ERROR_WANT_WRITE
				   && !s->writing) {
				clear_active(s, READ);
				set_active(s, WRITE);
			}
			return;
		}
		fprintf(stderr,
			"%s: failed to connect to SSL server (err=%d, reason=%d)\n",
			prog_name, ssl_err, reason);
		ERR_print_errors_fp(stderr);
		exit(-1);
	}

	s->state = S_CONNECTED;

	if (DBG > 0)
		fprintf(stderr, "core_ssl_connect: SSL is connected!\n");

	if (DBG > 1) {
		const SSL_CIPHER     *ssl_cipher;

		ssl_cipher = SSL_get_current_cipher(s->ssl);
		if (!ssl_cipher)
			fprintf(stderr,
				"core_ssl_connect: server refused all client cipher "
				"suites!\n");
		else
			fprintf(stderr,
				"core_ssl_connect: cipher=%s, id=%lu\n",
				SSL_CIPHER_get_name(ssl_cipher),
				SSL_CIPHER_get_id(ssl_cipher));
	}

	arg.l = 0;
	event_signal(EV_CONN_CONNECTED, (Object *) s, arg);
}

#endif /* HAVE_SSL */

int
core_connect(Conn * s)
{
	int             sd, result, async_errno;
	socklen_t       len;
	struct sockaddr_in *sin;
	struct linger   linger;
	int             myport, optval;
	Any_Type        arg;
	static int      prev_iteration = -1;
	static u_long   burst_len;

	if (iteration == prev_iteration)
		++burst_len;
	else {
		if (burst_len > max_burst_len)
			max_burst_len = burst_len;
		burst_len = 1;
		prev_iteration = iteration;
	}

	SYSCALL(SOCKET, sd = socket(AF_INET, SOCK_STREAM, 0));
	if (sd < 0) {
		if (DBG > 0)
			fprintf(stderr,
				"%s.core_connect.socket: %s (max_sd=%d)\n",
				prog_name, strerror(errno), max_sd);
		goto failure;
	}

	if (fcntl(sd, F_SETFL, O_NONBLOCK) < 0) {
		fprintf(stderr, "%s.core_connect.fcntl: %s\n",
			prog_name, strerror(errno));
		goto failure;
	}

	if (param.close_with_reset) {
		linger.l_onoff = 1;
		linger.l_linger = 0;
		if (setsockopt
		    (sd, SOL_SOCKET, SO_LINGER, &linger, sizeof(linger)) < 0) {
			fprintf(stderr,
				"%s.core_connect.setsockopt(SO_LINGER): %s\n",
				prog_name, strerror(errno));
			goto failure;
		}
	}

	/*
	 * Disable Nagle algorithm so we don't delay needlessly when
	 * pipelining requests.  
	 */
	optval = 1;
	if (setsockopt(sd, SOL_TCP, TCP_NODELAY, &optval, sizeof(optval)) < 0) {
		fprintf(stderr, "%s.core_connect.setsockopt(SO_SNDBUF): %s\n",
			prog_name, strerror(errno));
		goto failure;
	}

	optval = param.send_buffer_size;
	if (setsockopt(sd, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) < 0) {
		fprintf(stderr, "%s.core_connect.setsockopt(SO_SNDBUF): %s\n",
			prog_name, strerror(errno));
		goto failure;
	}

	optval = param.recv_buffer_size;
	if (setsockopt(sd, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval)) < 0) {
		fprintf(stderr, "%s.core_connect.setsockopt(SO_SNDBUF): %s\n",
			prog_name, strerror(errno));
		goto failure;
	}

	s->sd = sd;
#if !defined(HAVE_KEVENT) && !defined(HAVE_EPOLL)
	if (sd >= alloced_sd_to_conn) {
		size_t          size, old_size;

		old_size = alloced_sd_to_conn * sizeof(sd_to_conn[0]);
		alloced_sd_to_conn += 2048;
		size = alloced_sd_to_conn * sizeof(sd_to_conn[0]);
		if (sd_to_conn)
			sd_to_conn = realloc(sd_to_conn, size);
		else
			sd_to_conn = malloc(size);
		if (!sd_to_conn) {
			if (DBG > 0)
				fprintf(stderr,
					"%s.core_connect.realloc: %s\n",
					prog_name, strerror(errno));
			goto failure;
		}
		memset((char *) sd_to_conn + old_size, 0, size - old_size);
	}
	assert(!sd_to_conn[sd]);
	sd_to_conn[sd] = s;
#endif

	sin = hash_lookup(s->hostname, s->hostname_len, s->port);
	if (!sin) {
		if (DBG > 0)
			fprintf(stderr,
				"%s.core_connect: unknown server/port %s:%d\n",
				prog_name, s->hostname, s->port);
		goto failure;
	}

	arg.l = 0;
	event_signal(EV_CONN_CONNECTING, (Object *) s, arg);
	if (s->state >= S_CLOSING)
		goto failure;

	s->myaddr = core_get_next_myaddr();
	myaddr.sin_addr = s->myaddr->ip;
	if (param.hog) {
		while (1) {
			myport = port_get(s->myaddr);
			if (myport < 0)
				goto failure;

			myaddr.sin_port = htons(myport);
			SYSCALL(BIND,
				result = bind(sd, (struct sockaddr *) &myaddr,
					      sizeof(myaddr)));
			if (result == 0)
				break;

			if (errno != EADDRINUSE && errno == EADDRNOTAVAIL) {
				if (DBG > 0)
					fprintf(stderr,
						"%s.core_connect.bind: %s\n",
						prog_name, strerror(errno));
				goto failure;
			}
		}
		s->myport = myport;
	} else if (myaddr.sin_addr.s_addr != htonl(INADDR_ANY)) {
		SYSCALL(BIND,
		    result = bind(sd, (struct sockaddr *) &myaddr,
			sizeof(myaddr)));
		if (result != 0)
			goto failure;
	}

	SYSCALL(CONNECT,
		result = connect(sd, (struct sockaddr *) sin, sizeof(*sin)));
	if (result == 0) {
#ifdef HAVE_SSL
		if (param.use_ssl)
			core_ssl_connect(s);
		else
#endif
		{
			s->state = S_CONNECTED;
			arg.l = 0;
			event_signal(EV_CONN_CONNECTED, (Object *) s, arg);
		}
	} else if (errno == EINPROGRESS) {
		/*
		 * The socket becomes writable only after the connection has
		 * been established.  Hence we wait for writability to detect
		 * connection establishment.  
		 */
		s->state = S_CONNECTING;
		set_active(s, WRITE);
		if (param.timeout > 0.0) {
			arg.vp = s;
			assert(!s->watchdog);
			s->watchdog =
			    timer_schedule(conn_timeout, arg, param.timeout);
		}
	} else {
		len = sizeof(async_errno);
		if (getsockopt(sd, SOL_SOCKET, SO_ERROR, &async_errno, &len) ==
		    0 && async_errno != 0)
			errno = async_errno;

		if (DBG > 0)
			fprintf(stderr,
				"%s.core_connect.connect: %s (max_sd=%d)\n",
				prog_name, strerror(errno), max_sd);
		if (s->myport > 0)
			port_put(s->myaddr, s->myport);
		goto failure;
	}
	return 0;

      failure:
	conn_failure(s, errno);
	return -1;
}

int
core_send(Conn * conn, Call * call)
{
	Any_Type        arg;

	arg.l = 0;
	event_signal(EV_CALL_ISSUE, (Object *) call, arg);

	call->conn = conn;	/* NO refcounting here (see call.h).  */

	if (param.no_host_hdr) {
		call->req.iov[IE_HOST].iov_base = (caddr_t) "";
		call->req.iov[IE_HOST].iov_len = 0;
	} else if (!call->req.iov[IE_HOST].iov_base) {
		/*
		 * Default call's hostname to connection's hostname: 
		 */
		call->req.iov[IE_HOST].iov_base = (caddr_t) conn->fqdname;
		call->req.iov[IE_HOST].iov_len = conn->fqdname_len;
	}

	/*
	 * NOTE: the protocol version indicates what the _client_ can
	 * understand.  If we send HTTP/1.1, it doesn't mean that the server
	 * has to speak HTTP/1.1.  In other words, sending an HTTP/1.1 header
	 * leaves it up to the server whether it wants to reply with a 1.0 or
	 * 1.1 reply.  
	 */
	switch (call->req.version) {
	case 0x10000:
		if (param.no_host_hdr) {
			call->req.iov[IE_PROTL].iov_base =
			    (caddr_t) http10req_nohost;
			call->req.iov[IE_PROTL].iov_len =
			    sizeof(http10req_nohost) - 1;
		} else {
			call->req.iov[IE_PROTL].iov_base = (caddr_t) http10req;
			call->req.iov[IE_PROTL].iov_len =
			    sizeof(http10req) - 1;
		}
		break;

	case 0x10001:
		if (param.no_host_hdr) {
			call->req.iov[IE_PROTL].iov_base = http11req_nohost;
			call->req.iov[IE_PROTL].iov_len =
			    sizeof(http11req_nohost) - 1;
		} else {
			call->req.iov[IE_PROTL].iov_base = http11req;
			call->req.iov[IE_PROTL].iov_len =
			    sizeof(http11req) - 1;
		}
		break;

	default:
		fprintf(stderr, "%s: unexpected version code %x\n",
			prog_name, call->req.version);
		exit(1);
	}
	call->req.iov_index = 0;
	call->req.iov_saved = call->req.iov[0];

	/*
	 * insert call into connection's send queue: 
	 */
	call_inc_ref(call);
	call->sendq_next = 0;
	if (!conn->sendq) {
		conn->sendq = conn->sendq_tail = call;
		arg.l = 0;
		event_signal(EV_CALL_SEND_START, (Object *) call, arg);
		if (conn->state >= S_CLOSING)
			return -1;
		call->timeout =
		    param.timeout ? timer_now() + param.timeout : 0.0;
		set_active(conn, WRITE);
	} else {
		conn->sendq_tail->sendq_next = call;
		conn->sendq_tail = call;
	}
	return 0;
}

void
core_close(Conn * conn)
{
	Call           *call, *call_next;
	Any_Type        arg;
	int             sd;

	if (conn->state >= S_CLOSING)
		return;		/* guard against recursive calls */
	conn->state = S_CLOSING;

	if (DBG >= 10)
		fprintf(stderr, "%s.core_close(conn=%p)\n", prog_name, conn);

	if (conn->watchdog) {
		timer_cancel(conn->watchdog);
		conn->watchdog = 0;
	}

	/*
	 * first, get rid of all pending calls: 
	 */
	for (call = conn->sendq; call; call = call_next) {
		call_next = call->sendq_next;
		call_dec_ref(call);
	}
	conn->sendq = 0;

	for (call = conn->recvq; call; call = call_next) {
		call_next = call->recvq_next;
		call_dec_ref(call);
	}
	conn->recvq = 0;

	sd = conn->sd;
	conn->sd = -1;

	arg.l = 0;
	event_signal(EV_CONN_CLOSE, (Object *) conn, arg);
	assert(conn->state == S_CLOSING);

#ifdef HAVE_SSL
	if (param.use_ssl)
		SSL_shutdown(conn->ssl);
#endif

	if (sd >= 0) {
#ifdef HAVE_EPOLL
		struct epoll_event ev = { 0, { 0 } };
		int error;

		error = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, sd, &ev);
		if (error < 0) {
			if (conn->epoll_added == 1 && error != ENOENT) {
				error = errno;
				printf("EPOLL_CTL_DEL: %d %d %d\n", epoll_fd, sd, error);
				assert(error == 0);
			}
		}
#endif
		close(sd);
#if !defined(HAVE_KEVENT) && !defined(HAVE_EPOLL)
		sd_to_conn[sd] = 0;
		FD_CLR(sd, &wrfds);
		FD_CLR(sd, &rdfds);
#endif
		conn->reading = 0;
		conn->writing = 0;
	}
	if (conn->myport > 0)
		port_put(conn->myaddr, conn->myport);

	/*
	 * A connection that has been closed is not useful anymore, so we give 
	 * up the reference obtained when creating the session.  This normally 
	 * initiates destruction of the connection.  
	 */
	conn_dec_ref(conn);
}

#ifdef HAVE_KEVENT
void
core_loop(void)
{
	struct kevent ev;
	int n;
	Any_Type   arg;
	Conn      *conn;

	while (running) {
		++iteration;

		n = kevent(kq, NULL, 0, &ev, 1, NULL);
		if (n < 0 && errno != EINTR) {
			fprintf(stderr, "failed to fetch event: %s",
			    strerror(errno));
			exit(1);
		}

		switch (ev.filter) {
		case EVFILT_TIMER:
			timer_tick();
			break;
		case EVFILT_READ:
		case EVFILT_WRITE:
			conn = ev.udata;
	                conn_inc_ref(conn);

	                if (conn->watchdog) {
	                    timer_cancel(conn->watchdog);
	                    conn->watchdog = 0;
	                }
	                if (conn->state == S_CONNECTING) {
#ifdef HAVE_SSL
	                    if (param.use_ssl)
	                        core_ssl_connect(conn);
	                    else
#endif
	                    if (ev.filter == EVFILT_WRITE) {
				clear_active(conn, WRITE);
	                        conn->state = S_CONNECTED;
	                        arg.l = 0;
	                        event_signal(EV_CONN_CONNECTED, (Object*)conn, arg);
	                    }
	                } else {
			    if (ev.filter == EVFILT_WRITE && conn->sendq)
	                        do_send(conn);
	                    if (ev.filter == EVFILT_READ && conn->recvq)
	                        do_recv(conn);
	                }
	                    
	                conn_dec_ref(conn);
			break;
		}
	}
}
#else
#ifdef HAVE_EPOLL
void
core_loop(void)
{
	struct epoll_event *ep;
	int i, n;
	Any_Type   arg;
	Conn      *conn;

	while (running) {
		++iteration;

		timer_tick();
		n = epoll_wait(epoll_fd, epoll_events, EPOLL_N_MAX, epoll_timeout);
		if (n < 0 && errno == EINTR) {
			fprintf(stderr, "failed to fetch event: %s",
			    strerror(errno));
			continue;
		}
		ep = epoll_events;
		for (i = 0; i < n; i++, ep++) {
			conn = ep->data.ptr;
			conn_inc_ref(conn);

			if (conn->watchdog) {
				timer_cancel(conn->watchdog);
				conn->watchdog = 0;
			}
			if (conn->state == S_CONNECTING) {
#ifdef HAVE_SSL
				if (param.use_ssl)
					core_ssl_connect(conn);
				else
#endif
				if (ep->events & EPOLLOUT) {
					clear_active(conn, WRITE);
					conn->state = S_CONNECTED;
					arg.l = 0;
					event_signal(EV_CONN_CONNECTED, (Object*)conn, arg);
				}
			} else {
				if (ep->events & (EPOLLIN | EPOLLHUP) && conn->recvq)
					do_recv(conn);
				if (ep->events & EPOLLOUT && conn->sendq)
					do_send(conn);
			}
			conn_dec_ref(conn);
		}
	}
	close(epoll_fd);
}
#else
void
core_loop(void)
{
	int        is_readable, is_writable, n, sd, bit, min_i, max_i, i = 0;
	fd_set     readable, writable;
	fd_mask    mask;
	Any_Type   arg;
	Conn      *conn;
 
	while (running) {
	    struct timeval  tv = select_timeout;

	    timer_tick();

	    readable = rdfds;
	    writable = wrfds;
	    min_i = min_sd / NFDBITS;
	    max_i = max_sd / NFDBITS;

	    SYSCALL(SELECT,	n = select(max_sd + 1, &readable, &writable, 0, &tv));

	    ++iteration;

	    if (n <= 0) {
	        if (n < 0) {
	            fprintf(stderr, "%s.core_loop: select failed: %s\n", prog_name, strerror(errno));
	            exit(1);
	        }
	        continue;
	    }

	    while (n > 0) {
	        /*
	         * find the index of the fdmask that has something
	         * going on: 
	         */
	        do {
	            ++i;
	            if (i > max_i)
	                i = min_i;

	            assert(i <= max_i);
	            mask = readable.fds_bits[i] | writable.fds_bits[i];
	        } while (!mask);
	        bit = 0;
	        sd = i * NFDBITS + bit;
	        do {
	            if (mask & 1) {
	                --n;
	                is_readable = (FD_ISSET(sd, &readable) && FD_ISSET(sd, &rdfds));
	                is_writable = (FD_ISSET(sd, &writable) && FD_ISSET(sd, &wrfds));
	                
	                if (is_readable || is_writable) {
	                    /*
	                     * only handle sockets that
	                     * haven't timed out yet
	                     */
	                    conn = sd_to_conn[sd];
	                    conn_inc_ref(conn);

	                    if (conn->watchdog) {
	                        timer_cancel(conn->watchdog);
	                        conn->watchdog = 0;
	                    }
	                    if (conn->state == S_CONNECTING) {
#ifdef HAVE_SSL
	                        if (param.use_ssl)
	                             core_ssl_connect(conn);
	                        else
#endif
	                        if (is_writable) {
				    clear_active(conn, WRITE);
	                            conn->state = S_CONNECTED;
	                            arg.l = 0;
	                            event_signal(EV_CONN_CONNECTED, (Object*)conn, arg);
	                        }
	                    } else {
	                        if (is_writable && conn->sendq)
	                            do_send(conn);
	                        if (is_readable && conn->recvq)
	                            do_recv(conn);
	                    }
	                    
	                    conn_dec_ref(conn);
	                    
	                    if (n > 0)
	                         timer_tick();
	                }
	            }
	            mask = ((u_long) mask) >> 1;
	            ++sd;
	        } while (mask);
	    }
	}
}
#endif
#endif

void
core_exit(void)
{
	running = 0;
	param.num_conns = 0;

	printf("Maximum connect burst length: %lu\n", max_burst_len);

#ifdef TIME_SYSCALLS
	{
		u_int           count;
		Time            time;
		int             i;

		printf("Average syscall execution times:\n");
		for (i = 0; i < NELEMS(syscall_name); ++i) {
			count = syscall_count[i];
			time = syscall_time[i];
			printf("\t%s:\t%.3f ms/call (%.3fs total, %u calls)\n",
			       syscall_name[i],
			       count > 0 ? 1e3 * time / count : 0, time,
			       count);

		}
		putchar('\n');
	}
#endif
}
07070100000019000081ED0000000000000000000000015FCD0002000007E1000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/src/core.h/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/
#ifndef core_h
#define core_h

#include <netinet/in.h>

extern void core_init (void);
extern void core_add_addresses (const char *spec);
extern struct sockaddr_in *core_addr_intern (const char *hostname,
					     size_t hostname_len, int port);
extern int core_connect (Conn *conn);
extern int core_send (Conn *conn, Call *call);
extern void core_close (Conn *conn);

extern void core_loop (void);
extern void core_exit (void);

#endif /* core_h */
0707010000001A000041ED0000000000000000000000025FCD000200000000000000000000000000000000000000000000002300000000httperf-0.9.0+git.20201206/src/gen0707010000001B000081ED0000000000000000000000015FCD00020000013C000000000000000000000000000000000000002F00000000httperf-0.9.0+git.20201206/src/gen/Makefile.am# what flags you want to pass to the C compiler & linker
AM_CFLAGS = -I$(srcdir)/.. -I$(srcdir)/../lib
AM_LDFLAGS =

noinst_LIBRARIES = libgen.a
libgen_a_SOURCES = call_seq.c conn_rate.c misc.c rate.c rate.h session.c \
	session.h uri_fixed.c uri_wlog.c uri_wset.c \
	wsess.c wsesslog.c wsesspage.c \
	sess_cookie.c
0707010000001C000081ED0000000000000000000000015FCD000200000F3A000000000000000000000000000000000000002E00000000httperf-0.9.0+git.20201206/src/gen/call_seq.c/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

/* Issue a sequence of calls on a connection.  */

#include "config.h"

#include <assert.h>
#include <sys/types.h>

#include <generic_types.h>

#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <core.h>
#include <localevent.h>

#define CONN_PRIVATE_DATA(c) \
  ((Conn_Private_Data *) ((char *)(c) + conn_private_data_offset))

#define MIN(a,b)	((a) < (b) ? (a) : (b))

typedef struct Conn_Private_Data
  {
    int num_calls;
    int num_completed;
    int num_destroyed;
  }
Conn_Private_Data;

static size_t conn_private_data_offset;

static void
issue_calls (Conn *conn)
{
  Conn_Private_Data *priv;
  Call *call;
  int i;

  priv = CONN_PRIVATE_DATA (conn);
  priv->num_completed = 0;
  priv->num_destroyed = 0;

  for (i = 0; i < param.burst_len; ++i)
    if (priv->num_calls++ < param.num_calls)
      {
	call = call_new ();
	if (call)
	  {
	    core_send (conn, call);
	    call_dec_ref (call);
	  }
      }
}

static void
conn_connected (Event_Type et, Conn *conn)
{
  assert (et == EV_CONN_CONNECTED && object_is_conn (conn));

  issue_calls (conn);
}

static void
call_done (Event_Type et, Call *call)
{
  Conn *conn = call->conn;
  Conn_Private_Data *priv;

  assert (et == EV_CALL_RECV_STOP && conn && object_is_conn (conn));

  priv = CONN_PRIVATE_DATA (conn);
  ++priv->num_completed;
}

static void
call_destroyed (Event_Type et, Call *call)
{
  Conn_Private_Data *priv;
  Conn *conn;

  assert (et == EV_CALL_DESTROYED && object_is_call (call));

  conn = call->conn;
  priv = CONN_PRIVATE_DATA (conn);

  if (++priv->num_destroyed >= MIN (param.burst_len, param.num_calls))
    {
      if (priv->num_completed == priv->num_destroyed
	  && priv->num_calls < param.num_calls)
	issue_calls (conn);
      else
	core_close (conn);
    }
}

static void
init (void)
{
  Any_Type arg;

  conn_private_data_offset = object_expand (OBJ_CONN,
					    sizeof (Conn_Private_Data));

  arg.l = 0;
  event_register_handler (EV_CONN_CONNECTED, (Event_Handler) conn_connected,
			  arg);
  event_register_handler (EV_CALL_RECV_STOP, (Event_Handler) call_done, arg);
  event_register_handler (EV_CALL_DESTROYED, (Event_Handler) call_destroyed,
			  arg);
}

Load_Generator call_seq =
  {
    "performs a sequence of calls on a connection",
    init,
    no_op,
    no_op
  };
0707010000001D000081ED0000000000000000000000015FCD000200000BC1000000000000000000000000000000000000002F00000000httperf-0.9.0+git.20201206/src/gen/conn_rate.c/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

/* Creates connections at the fixed rate PARAM.RATE or sequentially if
   PARAM.RATE is zero.  */

#include "config.h"

#include <assert.h>
#include <stdio.h>

#include <generic_types.h>

#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <localevent.h>
#include <rate.h>
#include <conn.h>
#include <call.h>
#include <core.h>

static int num_conns_generated;
static int num_conns_destroyed;
static int num_conns_open;
static Rate_Generator rg;
static bool paused;

static int
make_conn (Any_Type arg)
{
  Conn *s;

  if (paused)
    return 0;

  if (num_conns_generated++ >= param.num_conns)
    return -1;

  if (param.max_conns != 0 && num_conns_open >= param.max_conns)
    return 0;

  s = conn_new ();
  if (!s)
    return -1;

  num_conns_open++;
  if (core_connect (s) == -1) {
    num_conns_generated--;
    num_conns_destroyed--;
    paused = true;
  }
  return 0;
}

static void
destroyed (void)
{
  Any_Type arg;

  if (++num_conns_destroyed >= param.num_conns)
    core_exit ();
  num_conns_open--;
  paused = false;
}

static void
init (void)
{
  Any_Type arg;

  rg.arg.l = 0;
  rg.tick = make_conn;

  arg.l = 0;
  event_register_handler (EV_CONN_DESTROYED, (Event_Handler) destroyed, arg);
}

static void
start (void)
{
  rg.rate = &param.rate;
  rate_generator_start (&rg, EV_CONN_DESTROYED);
}

Load_Generator conn_rate =
  {
    "creates connections at a fixed rate",
    init,
    start,
    no_op
  };
0707010000001E000081ED0000000000000000000000015FCD000200001299000000000000000000000000000000000000002A00000000httperf-0.9.0+git.20201206/src/gen/misc.c/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

/* Implements miscellaneous command-line specified operations.  So
   far, the following options are implemented here:
   
	--add-header	Adds one or more command-line specified header(s)
			to each call request.

	--method	Sets the method to be used when performing a
			call.  */

#include "config.h"

#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <generic_types.h>
#include <object.h>
#include <httperf.h>
#include <call.h>
#include <localevent.h>

static const char *extra, *extra_file;
static size_t extra_len;

static size_t method_len, file_len;

/* A simple module that collects cookies from the server responses and
   includes them in future calls to the server.  */

static const char *
unescape (const char *str, size_t *len)
{
  char *dp, *dst = strdup (str);
  const char *cp;
  int ch;

  if (!dst)
    panic ("%s: strdup() failed: %s\n", prog_name, strerror (errno));

  for (cp = str, dp = dst; (ch = *cp++); )
    {
      if (ch == '\\')
	{
	  ch = *cp++;
	  switch (ch)
	    {
	    case '\\':	/* \\ -> \ */
	      break;

	    case 'a':	/* \a -> LF */
	      ch = 10;
	      break;

	    case 'r':	/* \r -> CR */
	      ch = 13;
	      break;

	    case 'n':	/* \n -> CR/LF */
	      *dp++ = 13;
	      ch = 10;
	      break;

	    case '0': case '1': case '2': case '3': case '4':
	    case '5': case '6': case '7': case '8': case '9':
	      ch = strtol (cp - 1, (char **) &cp, 8);
	      break;

	    default:
	      fprintf (stderr, "%s: ignoring unknown escape sequence "
		       "`\\%c' in --add-header\n", prog_name, ch);
	      break;
	    }
	}
      *dp++ = ch;
    }
  *len = dp - dst;
  return dst;
}

static void
call_created (Event_Type et, Object *obj, Any_Type reg_arg, Any_Type arg)
{
  Call *c = (Call *) obj;

  assert (et == EV_CALL_NEW && object_is_call (obj));

  if (method_len > 0)
    call_set_method (c, param.method, method_len);

  if (extra_len > 0)
    call_append_request_header (c, extra, extra_len);

  if (file_len > 0)
    call_append_request_header (c, extra_file, file_len);
}


static void
init (void)
{
  Any_Type arg;
  struct stat st;
  int fd;

  if (param.additional_header)
    extra = unescape (param.additional_header, &extra_len);

  if (param.additional_header_file) {
    fd = open (param.additional_header_file, O_RDONLY);
    if (fd < 0)
      fprintf (stderr, "%s: failed to open header file\n", prog_name);
    else {
      if (fstat(fd, &st) < 0)
	fprintf (stderr, "%s: failed to stat header file\n", prog_name);
      else {
	extra_file = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
	if (extra_file == (char *)MAP_FAILED) {
	  fprintf (stderr, "%s: failed to map header file\n", prog_name);
	  extra_file = NULL;
	} else
	  file_len = st.st_size;
      }
      close(fd);
    }
  }

  if (param.method)
    method_len = strlen (param.method);

  arg.l = 0;
  event_register_handler (EV_CALL_NEW, call_created, arg);
}

Load_Generator misc =
  {
    "Miscellaneous command line options",
    init,
    no_op,
    no_op
  };
0707010000001F000081ED0000000000000000000000015FCD000200001594000000000000000000000000000000000000002A00000000httperf-0.9.0+git.20201206/src/gen/rate.c/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

#include "config.h"

#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#include <generic_types.h>
#include <object.h>
#include <httperf.h>
#include <localevent.h>
#include <rate.h>
#include <timer.h>

int current_rate = 0;
Time duration_in_current_rate = 0;

/* By pushing the random number generator state into the caller via
   the xsubi array below, we gain some test repeatability.  For
   example, let us say one generator was starting sessions, and a
   different generator was controlling requests within a session.  If
   both processes were sharing the same random number generator, then
   a premature session termination would change the subsequent session
   arrival spacing.  */

static Time
next_arrival_time_det (Rate_Generator *rg)
{
  return rg->rate->mean_iat;
}

static Time
next_arrival_time_uniform (Rate_Generator *rg)
{
  Time lower, upper;

  lower = rg->rate->min_iat;
  upper = rg->rate->max_iat;

  return lower + (upper - lower)*erand48 (rg->xsubi);
}

static Time
next_arrival_time_exp (Rate_Generator *rg)
{
  Time mean = rg->rate->mean_iat;

  return -mean*log (1.0 - erand48 (rg->xsubi));
}

static Time
next_arrival_time_variable (Rate_Generator *rg)
{
  Time next;

  next = rg->rate->iat[current_rate];
  duration_in_current_rate += next;

  if (duration_in_current_rate >= rg->rate->duration[current_rate])
    {
      current_rate++;
      if (current_rate >= rg->rate->numRates)
	current_rate = 0;
      duration_in_current_rate = 0;
    }
  return (next);
}

static void
tick (struct Timer *t, Any_Type arg)
{
  Time delay, now = timer_now ();
  Rate_Generator *rg = arg.vp;

  rg->timer = 0;
  if (rg->done)
    return;

  while (now > rg->next_time)
    {
      delay = (*rg->next_interarrival_time) (rg);
      if (verbose > 2)
	fprintf (stderr, "next arrival delay = %.4f\n", delay);
      rg->next_time += delay;
      rg->done = ((*rg->tick) (rg->arg) < 0);
      if (rg->done)
	return;
    }
  rg->timer = timer_schedule ((Timer_Callback) tick, arg, rg->next_time - now);
}

static void
done (Event_Type type, Object *obj, Any_Type reg_arg, Any_Type call_arg)
{
  Rate_Generator *rg = reg_arg.vp;

  if (rg->done)
    return;
  rg->done = ((*rg->tick) (rg->arg) < 0);
}

void
rate_generator_start (Rate_Generator *rg, Event_Type completion_event)
{
  Time (*func) (struct Rate_Generator *rg);
  Any_Type arg;
  Time delay;

  /* Initialize random number generator with the init values here, all
     rate generators (although independent) will follow the same
     sequence of random values.  We factor in the client's id to make
     sure no two machines running httperf generate identical random
     numbers.  May want to pass these values as args to
     rate_generator_start in the future.  */
  rg->xsubi[0] = 0x1234 ^ param.client.id;
  rg->xsubi[1] = 0x5678 ^ (param.client.id << 8);
  rg->xsubi[2] = 0x9abc ^ ~param.client.id;

  arg.vp = rg;
  if (rg->rate->rate_param > 0.0)
    {
      switch (rg->rate->dist)
	{
	case DETERMINISTIC: func = next_arrival_time_det; break;
	case UNIFORM:	    func = next_arrival_time_uniform; break;
	case EXPONENTIAL:   func = next_arrival_time_exp; break;
	case VARIABLE:      func = next_arrival_time_variable; break;
	default:
	  fprintf (stderr, "%s: unrecognized interarrival distribution %d\n",
		   prog_name, rg->rate->dist);
	  exit (-1);
	}
      rg->next_interarrival_time = func;
      delay = (*func) (rg);
      /* bias `next time' so that timeouts are rounded to the closest
         tick: */
      rg->next_time = timer_now () + delay;
      rg->timer = timer_schedule ((Timer_Callback) tick, arg, delay);
    }
  else
    /* generate callbacks sequentially: */
    event_register_handler (completion_event, done, arg);

  rg->start = timer_now ();
  rg->done = ((*rg->tick) (rg->arg) < 0);
}

void
rate_generator_stop (Rate_Generator *rg)
{
  if (rg->timer)
    {
      timer_cancel (rg->timer);
      rg->timer = 0;
    }
  rg->done = 1;
}
07070100000020000081ED0000000000000000000000015FCD000200000855000000000000000000000000000000000000002A00000000httperf-0.9.0+git.20201206/src/gen/rate.h/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

#ifndef rate_h
#define rate_h

#include <httperf.h>
#include <timer.h>

typedef struct Rate_Generator
  {
    u_short xsubi[3];		/* used for random number generation */
    Rate_Info *rate;
    Time start;
    Time next_time;
    Any_Type arg;
    struct Timer *timer;
    int (*tick) (Any_Type arg);
    int done;
    Time (*next_interarrival_time) (struct Rate_Generator *rg);
  }
Rate_Generator;

extern void rate_generator_start (Rate_Generator *rg,
				  Event_Type completion_event);
extern void rate_generator_stop (Rate_Generator *rg);

#endif /* rate_h */
07070100000021000081ED0000000000000000000000015FCD000200001868000000000000000000000000000000000000003100000000httperf-0.9.0+git.20201206/src/gen/sess_cookie.c/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

/* This module intercepts `Set-Cookie:' headers on a per-session basis
   and includes set cookies in future calls of the session.

   Missing features:
	- intercepted cookies are always sent, independent of any constraints
	   that may be present in the set-cookie header
*/

#include "config.h"

#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <generic_types.h>

#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <core.h>
#include <localevent.h>
#include <session.h>

#define MAX_COOKIE_LEN	256

#define SESS_PRIVATE_DATA(c) \
  ((Sess_Private_Data *) ((char *)(c) + sess_private_data_offset))

#define CALL_PRIVATE_DATA(c) \
  ((Call_Private_Data *) ((char *)(c) + call_private_data_offset))

typedef struct Sess_Private_Data
  {
    /* For now, we support just one cookie per session.  If we get
       more than one cookie, we'll print a warning message when
       --debug is turned on.  */
    size_t cookie_len;
    /* We can't malloc the cookie string because we may get a
       ``Set-Cookie:'' while there are calls pending that refer to an
       existing cookie.  So if we were to malloc & free cookies, we
       would have to use reference counting to avoid the risk of
       dangling pointers.  */
    char cookie[MAX_COOKIE_LEN];
  }
Sess_Private_Data;

/* We need the call private data to ensure that the cookie gets set
   only once.  EV_CALL_ISSUE gets signalled each time a call is sent
   on a connection.  Since a connection may fail, the same call may be
   issued multiple times, hence we need to make sure that the cookie
   gets set only once per call.  */
typedef struct Call_Private_Data
  {
    u_int cookie_present;	/* non-zero if cookie has been set already */
    char cookie[MAX_COOKIE_LEN];
  }
Call_Private_Data;

static size_t sess_private_data_offset = -1;
static size_t call_private_data_offset = -1;

/* A simple module that collects cookies from the server responses and
   includes them in future calls to the server.  */

static void
call_issue (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  Call_Private_Data *cpriv;
  Sess_Private_Data *priv;
  Sess *sess;
  Call *call;

  assert (et == EV_CALL_ISSUE && object_is_call (obj));
  call = (Call *) obj;
  cpriv = CALL_PRIVATE_DATA (call);

  if (cpriv->cookie_present)
    /* don't do anything if cookie has been set already */
    return;

  sess = session_get_sess_from_call (call);
  priv = SESS_PRIVATE_DATA (sess);

  if (priv->cookie_len > 0)
    {
      if (DBG > 1)
	fprintf (stderr, "call_issue.%ld: inserting `%s'\n",
		 call->id, priv->cookie);
      cpriv->cookie_present = 1;
      memcpy (cpriv->cookie, priv->cookie, priv->cookie_len + 1);
      call_append_request_header (call, cpriv->cookie, priv->cookie_len);
    }
}

static void
call_recv_hdr (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  char *hdr, *start, *end;
  Sess_Private_Data *priv;
  size_t len;
  struct iovec *line;
  Sess *sess;
  Call *call;

  assert (et == EV_CALL_RECV_HDR && object_is_call (obj));
  call = (Call *) obj;
  sess = session_get_sess_from_call (call);
  priv = SESS_PRIVATE_DATA (sess);

  line = callarg.vp;
  hdr = line->iov_base;
  if (tolower (hdr[0]) == 's' && line->iov_len > 12
      && strncasecmp (hdr + 1, "et-cookie: ", 11) == 0)
    {
      /* munch time! */
      start = hdr + 12;
      end = strchr (start, ';');
      if (!end)
	end = hdr + line->iov_len;
      len = end - start;
      if (DBG > 0 && priv->cookie_len > 0)
	fprintf (stderr, "%s: can't handle more than one "
		 "cookie at a time, replacing existing one\n", prog_name);
      if (len + 10 >= MAX_COOKIE_LEN)
	{
	  fprintf (stderr, "%s.sess_cookie: truncating cookie to %d bytes\n",
		   prog_name, MAX_COOKIE_LEN - 11);
	  len = MAX_COOKIE_LEN - 11;
	}
      memcpy (priv->cookie, "Cookie: ", 8);
      memcpy (priv->cookie + 8, start, len);
      memcpy (priv->cookie + 8 + len, "\r\n", 2);
      priv->cookie[10 + len] = '\0';
      priv->cookie_len = len + 10;

      if (DBG > 0)
	fprintf (stderr, "%s: got cookie `%s'\n", prog_name, start);
    }
}

static void
init (void)
{
  Any_Type arg;

  sess_private_data_offset = object_expand (OBJ_SESS,
					    sizeof (Sess_Private_Data));
  call_private_data_offset = object_expand (OBJ_CALL,
					    sizeof (Call_Private_Data));
  arg.l = 0;
  event_register_handler (EV_CALL_ISSUE, call_issue, arg);
  event_register_handler (EV_CALL_RECV_HDR, call_recv_hdr, arg);
}

Load_Generator sess_cookie =
  {
    "per-session cookie manager",
    init,
    no_op,
    no_op
  };
07070100000022000081ED0000000000000000000000015FCD000200002E10000000000000000000000000000000000000002D00000000httperf-0.9.0+git.20201206/src/gen/session.c/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

/* A session consists of a number of calls.  Workload generators such
   as wsess and wsesslog determine when and how to issue calls.  This
   module is responsible for the actual mechanics of issuing the
   calls.  This includes creating and managing connections as
   necessary.  Connection management can be controlled by command-line
   options --max-piped-calls=Np and --max-connections=Nc.  This module
   creates up to Nc concurrent connections to dispatch calls.  On each
   connection, up to Np pipelined requests can be issued.  When no
   more calls can be issued, this module waits until some of the
   pending calls complete.

   Note that HTTP/1.1 allows a server to close a connection pretty
   much any time it feels like.  This means that a session may fail
   (be closed) while there pipelined calls are pending.  In such a
   case, this module takes care of creating a new connection and
   re-issuing the calls that were pending on the failed connection.

   A session is considered to fail if:

   (a) any operation exceeds the timeout parameters, or

   (b) a connection closes on us before we received at least one
       reply, or

   (c) param.failure_status is non-zero and the reply status of a call
       matches this failure status.
   */

#include "config.h"

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <generic_types.h>
#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <core.h>
#include <localevent.h>
#include <sess.h>
#include <session.h>

#define MAX_CONN		 4	/* max # of connections per session */
#define MAX_PIPED		32	/* max # of calls that can be piped */

#define SESS_PRIVATE_DATA(c)						\
  ((Sess_Private_Data *) ((char *)(c) + sess_private_data_offset))

#define CONN_PRIVATE_DATA(c)						\
  ((Conn_Private_Data *) ((char *)(c) + conn_private_data_offset))

#define CALL_PRIVATE_DATA(c)						\
  ((Call_Private_Data *) ((char *)(c) + call_private_data_offset))

typedef struct Sess_Private_Data
  {
    struct Conn_Info
      {
	Conn *conn;		/* connection or NULL */
	u_int is_connected : 1;	/* is connection ready for use? */
	u_int is_successful : 1; /* got at least one reply on this conn? */

	/* Ring-buffer of pending calls: */
	u_int num_pending;	/* # of calls pending */
	u_int num_sent;		/* # of calls sent so far */
	u_int rd;		/* first pending call */
	u_int wr;		/* where to insert next call */
	Call *call[MAX_PIPED];
      }
    conn_info[MAX_CONN];
  }
Sess_Private_Data;

typedef struct Conn_Private_Data
  {
    Sess *sess;
    struct Conn_Info *ci;	/* pointer to relevant conn-info */
  }
Conn_Private_Data;

typedef struct Call_Private_Data
  {
    Sess *sess;
  }
Call_Private_Data;

static size_t sess_private_data_offset = -1;
static size_t conn_private_data_offset = -1;
static size_t call_private_data_offset = -1;
static size_t max_qlen;

static void
create_conn (Sess *sess, struct Conn_Info *ci)
{
  Conn_Private_Data *cpriv;

  /* No connection yet (or anymore).  Create a new connection.  Note
     that CI->CONN is NOT reference-counted.  This is again to avoid
     introducing recursive dependencies (see also comment regarding
     member CONN in call.h). */
  ci->conn = conn_new ();
  if (!ci->conn)
    {
      sess_failure (sess);
      return;
    }
  cpriv = CONN_PRIVATE_DATA (ci->conn);
  cpriv->sess = sess;
  cpriv->ci = ci;

  ci->is_connected = 0;
  ci->is_successful = 0;
  ci->num_sent = 0;		/* (re-)send all pending calls */

#ifdef HAVE_SSL
  if (param.ssl_reuse && ci->conn->ssl && sess->ssl)
    {
      if (DBG > 0)
	fprintf (stderr, "create_conn: reusing SSL session %p\n",
		 (void *) sess->ssl);
      SSL_copy_session_id (ci->conn->ssl, sess->ssl);
    }
#endif

  if (core_connect (ci->conn) < 0)
    sess_failure (sess);
}

static void
send_calls (Sess *sess, struct Conn_Info *ci)
{
  u_int rd;
  int i;

  if (!ci->conn)
    {
      create_conn (sess, ci);
      return;
    }

  if (!ci->is_connected)
    /* wait until connection is connected (or has failed)  */
    return;

  rd = (ci->rd + ci->num_sent) % MAX_PIPED;

  for (i = ci->num_sent; i < ci->num_pending; ++i)
    {
      core_send (ci->conn, ci->call[rd]);
      ++ci->num_sent;
      rd = (rd + 1) % MAX_PIPED;
    }
}

static void
sess_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  Sess_Private_Data *priv;
  struct Conn_Info *ci;
  Sess *sess;
  int i, j, rd;

  assert (et == EV_SESS_DESTROYED && object_is_sess (obj));
  sess = (Sess *) obj;
  priv = SESS_PRIVATE_DATA (sess);

  for (i = 0; i < param.max_conns; ++i)
    {
      ci = priv->conn_info + i;

      if (ci->conn)
	core_close (ci->conn);

      rd = ci->rd;
      for (j = 0; j < ci->num_pending; ++j)
	{
	  call_dec_ref (ci->call[rd]);
	  rd = (rd + 1) % MAX_PIPED;
	}
    }
}

static void
conn_connected (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  Conn_Private_Data *cpriv;
  struct Conn_Info *ci;
  Sess *sess;
  Conn *conn;

  assert (et == EV_CONN_CONNECTED && object_is_conn (obj));

  conn = (Conn *) obj;
  cpriv = CONN_PRIVATE_DATA (conn);
  sess = cpriv->sess;
  ci = cpriv->ci;

  ci->is_connected = 1;

#ifdef HAVE_SSL
  if (param.ssl_reuse && !sess->ssl && ci->conn->ssl)
    {
      sess->ssl = SSL_dup (ci->conn->ssl);
      if (DBG > 0)
	fprintf (stderr, "create_conn: cached SSL session %p as %p\n",
		 (void *) ci->conn->ssl, (void *) sess->ssl);
    }
#endif /* HAVE_SSL */

  send_calls (sess, ci);
}

static void
conn_failed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  Conn_Private_Data *cpriv;
  struct Conn_Info *ci;
  Conn *conn;
  Sess *sess;

  assert (et == EV_CONN_FAILED && object_is_conn (obj));
  conn = (Conn *) obj;
  cpriv = CONN_PRIVATE_DATA (conn);
  sess = cpriv->sess;
  ci = cpriv->ci;

  if (ci->is_successful || param.retry_on_failure)
    /* try to create a new connection so we can issue the remaining
       calls. */
    create_conn (sess, ci);
  else
    /* The connection failed before we got even one reply, so declare
       the session as dead... */
    sess_failure (cpriv->sess);
}

static void
conn_timeout (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  Conn_Private_Data *cpriv;
  Conn *conn;

  /* doh, this session is dead now... */

  assert (et == EV_CONN_TIMEOUT && object_is_conn (obj));
  conn = (Conn *) obj;
  cpriv = CONN_PRIVATE_DATA (conn);

  sess_failure (cpriv->sess);
}

static void
call_done (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  Conn_Private_Data *cpriv;
  struct Conn_Info *ci;
  Sess *sess;
  Conn *conn;
  Call *call;

  assert (et == EV_CALL_RECV_STOP && object_is_call (obj));
  call = (Call *) obj;
  conn = call->conn;
  cpriv = CONN_PRIVATE_DATA (conn);
  sess = cpriv->sess;
  ci = cpriv->ci;

  ci->is_successful = 1;	/* conn has received at least one reply */

  /* remove the call from the conn_info structure */
  assert (ci->call[ci->rd] == call && ci->num_pending > 0 && ci->num_sent > 0);
  ci->call[ci->rd] = 0;
  ci->rd = (ci->rd + 1) % MAX_PIPED;
  --ci->num_pending;
  --ci->num_sent;

  /* if the reply status matches the failure status, the session has
     failed */
  if (param.failure_status && call->reply.status == param.failure_status)
    {
      if (param.retry_on_failure)
	session_issue_call (sess, call);
      else
	sess_failure (sess);
    }

  call_dec_ref (call);

  if (param.http_version < 0x10001)
    {
      /* Rather than waiting for the connection to close on us, we
	 close it pro-actively (this is what a pre-1.1 browser would
	 do.  */
      core_close (ci->conn);
      ci->conn = 0;
    }
}

void
session_init (void)
{
  Any_Type arg;

  if (!param.max_conns)
    param.max_conns = MAX_CONN;

  if (!param.max_piped)
    {
      if (param.http_version >= 0x10001)
	param.max_piped = MAX_PIPED;
      else
	/* no pipelining before HTTP/1.1... */
	param.max_piped = 1;
    }

  if (param.max_conns > MAX_CONN)
    {
      fprintf (stderr, "%s.session_init: --max-conns must be <= %u\n",
	       prog_name, MAX_CONN);
      exit (1);
    }
  if (param.max_piped > MAX_PIPED)
    {
      fprintf (stderr, "%s.session_init: --max-piped-calls must be <= %u\n",
	       prog_name, MAX_PIPED);
      exit (1);
    }

  max_qlen = param.max_conns * param.max_piped;

  sess_private_data_offset = object_expand (OBJ_SESS,
					    sizeof (Sess_Private_Data));
  conn_private_data_offset = object_expand (OBJ_CONN,
					    sizeof (Conn_Private_Data));
  call_private_data_offset = object_expand (OBJ_CALL,
					    sizeof (Call_Private_Data));

  arg.l = 0;
  event_register_handler (EV_SESS_DESTROYED, sess_destroyed, arg);

  event_register_handler (EV_CONN_CONNECTED, conn_connected, arg);
  event_register_handler (EV_CONN_FAILED, conn_failed, arg);
  event_register_handler (EV_CONN_TIMEOUT, conn_timeout, arg);

  event_register_handler (EV_CALL_RECV_STOP, call_done, arg);
}

size_t
session_max_qlen (Sess *sess)
{
  return max_qlen;
}

size_t
session_current_qlen (Sess *sess)
{
  Sess_Private_Data *priv;
  size_t num_pending = 0;
  int i;

  priv = SESS_PRIVATE_DATA (sess);

  for (i = 0; i < param.max_conns; ++i)
    num_pending += priv->conn_info[i].num_pending;

  return num_pending;
}

int
session_issue_call (Sess *sess, Call *call)
{
  Call_Private_Data *cpriv;
  Sess_Private_Data *priv;
  struct Conn_Info *ci;
  int i;

  priv = SESS_PRIVATE_DATA (sess);

  cpriv = CALL_PRIVATE_DATA (call);
  cpriv->sess = sess;

  for (i = 0; i < param.max_conns; ++i)
    {
      ci = priv->conn_info + i;
      if (ci->num_pending < param.max_piped)
	{
	  ++ci->num_pending;
	  ci->call[ci->wr] = call;
	  call_inc_ref (call);
	  ci->wr = (ci->wr + 1) % MAX_PIPED;
	  send_calls (sess, ci);
	  return 0;
	}
    }
  fprintf (stderr, "%s.session_issue_call: too many calls pending!\n"
	   "\tIncrease --max-connections and/or --max-piped-calls.\n",
	   prog_name);
  exit (1);
}

Sess *
session_get_sess_from_conn (Conn *conn)
{
  assert (object_is_conn (conn));
  return CONN_PRIVATE_DATA (conn)->sess;
}

Sess *
session_get_sess_from_call (Call *call)
{
  assert (object_is_call (call));
  return CALL_PRIVATE_DATA (call)->sess;
}
07070100000023000081ED0000000000000000000000015FCD00020000093D000000000000000000000000000000000000002D00000000httperf-0.9.0+git.20201206/src/gen/session.h/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

#ifndef session_h
#define session_h

#include <sess.h>

extern void session_init (void);	/* initialize session module */

/* Maximum number of calls that can be queued on a session.  */
extern size_t session_max_qlen (Sess *sess);

/* Current number of calls that are queued on the session.  */
extern size_t session_current_qlen (Sess *sess);

/* Issue call CALL on session SESS.  Returns negative number in case
   of failure.  */
extern int session_issue_call (Sess *sess, Call *call);

/* Given a connection object, find the session object that the
   connection belongs to.  */
extern Sess *session_get_sess_from_conn (Conn *conn);

/* Given a call object, find the session object that the call
   belongs to.  */
extern Sess *session_get_sess_from_call (Call *call);

#endif /* session_h */
07070100000024000081ED0000000000000000000000015FCD0002000008D8000000000000000000000000000000000000002F00000000httperf-0.9.0+git.20201206/src/gen/uri_fixed.c/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

/* Causes calls to make a request to the fixed URI specified by
   PARAM.URI.  */

#include "config.h"

#include <assert.h>
#include <stdio.h>
#include <string.h>

#include <generic_types.h>
#include <object.h>
#include <httperf.h>
#include <call.h>
#include <localevent.h>

static size_t uri_len;

static void
set_uri (Event_Type et, Call *call)
{
  assert (et == EV_CALL_NEW && object_is_call (call));
  call_set_uri (call, param.uri, uri_len);
}

static void
init (void)
{
  Any_Type arg;

  uri_len = strlen (param.uri);

  arg.l = 0;
  event_register_handler (EV_CALL_NEW, (Event_Handler) set_uri, arg);
}

Load_Generator uri_fixed =
  {
    "fixed url",
    init,
    no_op,
    no_op
  };
07070100000025000081ED0000000000000000000000015FCD0002000012FD000000000000000000000000000000000000002E00000000httperf-0.9.0+git.20201206/src/gen/uri_wlog.c/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

/* This load generator can be used to recreate a workload based on a
   server log file.
 
   This module can be used in conjunction with two comands to help in
   extracting information from the httpd CLF file (contact
   eranian@hpl.hp.com).
 
   Please note that you don't necessary need any of those tools. You
   can recreate the list of URIs by hand or with any other programs as
   long as you respect the format expected by this module (and also
   provided than you have the corresponding document tree on the
   server side).
 
   The format of the file used by this module is very simple (maybe
   too simple):

	URI1\0URI2\0......URIn\0
  
   It is a simple concatenated list of URI separated by the \0 (end of
   string) marker.
 
   This way, we don't need any parsing of the string when generating
   the URI.

   You can choose to loop on te list of URIs by using the following
   command line option to httperf:

       % httperf .... --wlog y,my_uri_file

   Otherwise httperf will stop once it reaches the end of the list.
 
   Any comment on this module contact eranian@hpl.hp.com or
   davidm@hpl.hp.com.  */

#include "config.h"

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

#include <generic_types.h>

#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <core.h>
#include <localevent.h>

static char *fbase, *fend, *fcurrent;

static void
set_uri (Event_Type et, Call * c)
{
  int len, did_wrap = 0;
  const char *uri;

  assert (et == EV_CALL_NEW && object_is_call (c));

  do
    {
      if (fcurrent >= fend)
	{
	  if (did_wrap)
	    panic ("%s: %s does not contain any valid URIs\n",
		   prog_name, param.wlog.file);
	  did_wrap = 1;

	  /* We reached the end of the uri list so wrap around to the
	     beginning.  If not looping, also ask for the test to stop
	     as soon as possible (the current request will still go
	     out, but httperf won't wait for its reply to show up).  */
	  fcurrent = fbase;
	  if (!param.wlog.do_loop)
	    core_exit ();
	}
      uri = fcurrent;
      len = strlen (fcurrent);
      call_set_uri (c, uri, len);
      fcurrent += len + 1;
    }
  while (len == 0);

  if (verbose)
    printf ("%s: accessing URI `%s'\n", prog_name, uri);
}

void
init_wlog (void)
{
  struct stat st;
  Any_Type arg;
  int fd;

  fd = open (param.wlog.file, O_RDONLY, 0);
  if (fd == -1)
    panic ("%s: can't open %s\n", prog_name, param.wlog.file);

  fstat (fd, &st);
  if (st.st_size == 0)
    panic ("%s: file %s is empty\n", prog_name, param.wlog.file);

  /* mmap anywhere in address space: */
  fbase = (char *) mmap (0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
  if (fbase == (char *) -1)
    panic ("%s: can't mmap the file: %s\n", prog_name, strerror (errno));

  close (fd);

  /* set the upper boundary: */
  fend = fbase + st.st_size;
  /* set current entry: */
  fcurrent = fbase;

  arg.l = 0;
  event_register_handler (EV_CALL_NEW, (Event_Handler) set_uri, arg);
}

static void
stop_wlog (void)
{
  munmap (fbase, fend - fbase);
}

Load_Generator uri_wlog =
  {
    "Generates URIs based on a predetermined list",
    init_wlog,
    no_op,
    stop_wlog
  };
07070100000026000081ED0000000000000000000000015FCD000200000ECC000000000000000000000000000000000000002E00000000httperf-0.9.0+git.20201206/src/gen/uri_wset.c/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

/* Causes accesses to a fixed set of files (working set) in such a way
   that is likely to cause disk I/O with a certain probability.  */

#include "config.h"

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <generic_types.h>
#include <object.h>
#include <httperf.h>
#include <call.h>
#include <localevent.h>

#define MAX_URI_LEN		128
#define CALL_PRIVATE_DATA(c) \
 ((void *) ((char *)(c) + call_private_data_offset))

static double miss_prob;
static unsigned file_num;
static size_t call_private_data_offset;
static size_t uri_prefix_len;

static void
set_uri (Event_Type et, Call *c)
{
  char *cp, *buf_end;
  unsigned j, n;

  assert (et == EV_CALL_NEW && object_is_call (c));

  miss_prob += param.wset.target_miss_rate;
  if (miss_prob >= 1.0)
    {
      /* generate (what we hope to be) a miss */
      miss_prob -= 1.0;
      file_num += param.client.num_clients;
      if (file_num >= param.wset.num_files)
	file_num -= param.wset.num_files;
    }

  /* fill in extension: */
  buf_end = (char *) CALL_PRIVATE_DATA (c) + MAX_URI_LEN;
  cp = buf_end - 6;
  memcpy (cp, ".html", 6);

  /* fill in file & pathname: */
  n = file_num;
  for (j = 1; j < param.wset.num_files; j *= 10, n /= 10)
    {
      cp -= 2;
      cp[0] = '/'; cp[1] = '0' + (n % 10);
    }

  /* fill in the uri prefix specified by param.uri: */
  cp -= uri_prefix_len;
  if (cp < (char *) CALL_PRIVATE_DATA (c))
    {
      fprintf (stderr, "%s.uri_wset: URI buffer overflow!\n", prog_name);
      exit (1);
    }
  memcpy (cp, param.uri, uri_prefix_len);

  call_set_uri (c, cp, (buf_end - cp) - 1);

  if (verbose)
    printf ("%s: accessing URI `%s'\n", prog_name, cp);
}

static void
init (void)
{
  Any_Type arg;

  miss_prob = drand48 ();
  file_num = param.client.id;

  call_private_data_offset = object_expand (OBJ_CALL, MAX_URI_LEN);

  uri_prefix_len = strlen (param.uri);
  if (param.uri[uri_prefix_len - 1] == '/')
    {
      ++param.uri;
      --uri_prefix_len;
    }

  arg.l = 0;
  event_register_handler (EV_CALL_NEW, (Event_Handler) set_uri, arg);
}

Load_Generator uri_wset =
  {
    "Generates URIs accessing a working-set at a given rate",
    init,
    no_op,
    no_op
  };
07070100000027000081ED0000000000000000000000015FCD0002000016EF000000000000000000000000000000000000002B00000000httperf-0.9.0+git.20201206/src/gen/wsess.c/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

/* Creates sessions at the fixed rate PARAM.RATE.  */

#include "config.h"

#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <generic_types.h>

#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <core.h>
#include <localevent.h>
#include <rate.h>
#include <session.h>

#define SESS_PRIVATE_DATA(c) \
  ((Sess_Private_Data *) ((char *)(c) + sess_private_data_offset))

typedef struct Sess_Private_Data
  {
    u_int num_calls_in_this_burst; /* # of calls created for this burst */
    u_int num_calls_target;	/* total # of calls desired */
    u_int num_calls_destroyed;	/* # of calls destroyed so far */
    struct Timer *timer;		/* timer for session think time */
  }
Sess_Private_Data;

static size_t sess_private_data_offset;

static int num_sessions_generated;
static int num_sessions_destroyed;
static Rate_Generator rg_sess;


static void
issue_calls (Sess *sess, Sess_Private_Data *priv)
{
  int i, to_create, retval;
  Call *call;

  /* Mimic browser behavior of fetching html object, then a couple of
     embedded objects: */

  to_create = 1;
  if (priv->num_calls_in_this_burst > 0)
    to_create = param.burst_len - priv->num_calls_in_this_burst;

  priv->num_calls_in_this_burst += to_create;

  for (i = 0; i < to_create; ++i)
    {
      call = call_new ();
      if (!call)
	{
	  sess_failure (sess);
	  return;
	}

      retval = session_issue_call (sess, call);
      call_dec_ref (call);

      if (retval < 0)
	return;
    }
}

static void
user_think_time_expired (struct Timer *t, Any_Type arg)
{
  Sess *sess = arg.vp;
  Sess_Private_Data *priv;

  assert (object_is_sess (sess));

  priv = SESS_PRIVATE_DATA (sess);
  priv->timer = 0;

  issue_calls (sess, priv);
}

static void
call_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  Any_Type arg;
  Sess *sess;
  Call *call;
  Sess_Private_Data *priv;

  assert (et == EV_CALL_DESTROYED && object_is_call (obj));

  call = (Call *) obj;
  sess = session_get_sess_from_call (call);
  priv = SESS_PRIVATE_DATA (sess);

  ++priv->num_calls_destroyed;

  if (priv->num_calls_destroyed >= param.wsess.num_calls)
    {
      /* we're done with this session */
      if (!sess->failed)
	sess_dec_ref (sess);
    }
  else if (priv->num_calls_in_this_burst < param.burst_len)
    /* now that we received the reply to the first call in this burst,
       create the remaining calls */
    issue_calls (sess, priv);
  else if (priv->num_calls_destroyed >= priv->num_calls_target)
    {
      /* we're done with this burst---schedule the user-think-time timer */
      priv->num_calls_in_this_burst = 0;
      priv->num_calls_target += param.burst_len;
      assert (!priv->timer);
      arg.vp = sess;
      priv->timer = timer_schedule (user_think_time_expired, arg,
				    param.wsess.think_time);
    }
}

/* Create a new session.  */
static int
sess_create (Any_Type arg)
{
  Sess_Private_Data *priv;
  Sess *sess;

  if (num_sessions_generated++ >= param.wsess.num_sessions)
    return -1;

  sess = sess_new ();
  if (!sess)
    return 1;

  priv = SESS_PRIVATE_DATA (sess);

  priv->num_calls_target = param.burst_len;
  issue_calls (sess, SESS_PRIVATE_DATA (sess));
  return 0;
}

static void
sess_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  Sess_Private_Data *priv;
  Sess *sess;

  assert (et == EV_SESS_DESTROYED && object_is_sess (obj));
  sess = (Sess *) obj;

  priv = SESS_PRIVATE_DATA (sess);
  if (priv->timer)
    {
      timer_cancel (priv->timer);
      priv->timer = 0;
    }

  if (++num_sessions_destroyed >= param.wsess.num_sessions)
    core_exit ();
}

static void
init (void)
{
  Any_Type arg;

  session_init ();

  sess_private_data_offset = object_expand (OBJ_SESS,
					    sizeof (Sess_Private_Data));
  rg_sess.rate = &param.rate;
  rg_sess.tick = sess_create;
  rg_sess.arg.l = 0;

  arg.l = 0;
  event_register_handler (EV_SESS_DESTROYED, sess_destroyed, arg);
  event_register_handler (EV_CALL_DESTROYED, call_destroyed, arg);
}

static void
start (void)
{
  rate_generator_start (&rg_sess, EV_SESS_DESTROYED);
}

Load_Generator wsess =
  {
    "creates session workload",
    init,
    start,
    no_op
  };
07070100000028000081ED0000000000000000000000015FCD00020000437A000000000000000000000000000000000000002E00000000httperf-0.9.0+git.20201206/src/gen/wsesslog.c/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

/* Creates sessions at the fixed rate PARAM.RATE.  The session descriptions
   are read in from a configuration file.

   There is currently no tool that translates from standard log
   formats to the format accepted by this module.

   An example input file follows:

   #
   # This file specifies the potentially-bursty uri sequence for a number of
   # user sessions.  The format rules of this file are as follows:
   #
   # Comment lines start with a '#' as the first character.  # anywhere else
   # is considered part of the uri.
   #
   # Lines with only whitespace delimit session definitions (multiple blank
   # lines do not generate "null" sessions).
   #
   # Lines otherwise specify a uri-sequence (1 uri per line).  If the
   # first character of the line is whitespace (e.g. space or tab), the
   # uri is considered to be part of a burst that is sent out after the
   # previous non-burst uri.
   #

   # session 1 definition (this is a comment)

   /foo.html
	/pict1.gif
	/pict2.gif
   /foo2.html
	/pict3.gif
	/pict4.gif

   #session 2 definition

   /foo3.html
   /foo4.html
	/pict5.gif

   Any comment on this module contact carter@hpl.hp.com.  */

#include "config.h"

#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <generic_types.h>

#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <conn.h>
#include <call.h>
#include <core.h>
#include <localevent.h>
#include <rate.h>
#include <session.h>

/* Maximum number of sessions that can be defined in the configuration
   file.  */
#define MAX_SESSION_TEMPLATES	1000

#ifndef TRUE
#define TRUE  (1)
#endif
#ifndef FALSE
#define FALSE (0)
#endif

#define SESS_PRIVATE_DATA(c)						\
  ((Sess_Private_Data *) ((char *)(c) + sess_private_data_offset))

typedef struct req REQ;
struct req
  {
    REQ *next;
    int method;
    char *uri;
    int uri_len;
    char *contents;
    int contents_len;
    char extra_hdrs[50];	/* plenty for "Content-length: 1234567890" */
    int extra_hdrs_len;
  };

typedef struct burst BURST;
struct burst
  {
    BURST *next;
    int num_reqs;
    Time user_think_time;
    REQ *req_list;
  };

typedef struct Sess_Private_Data Sess_Private_Data;
struct Sess_Private_Data
  {
    u_int num_calls_in_this_burst; /* # of calls created for this burst */
    u_int num_calls_target;	/* total # of calls desired */
    u_int num_calls_destroyed;	/* # of calls destroyed so far */
    struct Timer *timer;		/* timer for session think time */

    int total_num_reqs;		/* total number of requests in this session */

    BURST *current_burst;	/* the current burst we're working on */
    REQ *current_req;		/* the current request we're working on */
  };

/* Methods allowed for a request: */
enum
  {
    HM_DELETE, HM_GET, HM_HEAD, HM_OPTIONS, HM_POST, HM_PUT, HM_PROPFIND, HM_REPORT, HM_TRACE,
    HM_LEN
  };

static const char *call_method_name[] =
  {
    "DELETE", "GET", "HEAD", "OPTIONS", "POST", "PUT", "PROPFIND", "REPORT", "TRACE"
  };

static size_t sess_private_data_offset;
static int num_sessions_generated;
static int num_sessions_destroyed;
static Rate_Generator rg_sess;

/* This is an array rather than a list because we may want different
   httperf clients to start at different places in the sequence of
   sessions. */
static int num_templates;
static int next_session_template;
static Sess_Private_Data session_templates[MAX_SESSION_TEMPLATES] =
  {
    { 0, }
  };

static void
sess_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  Sess_Private_Data *priv;
  Sess *sess;

  assert (et == EV_SESS_DESTROYED && object_is_sess (obj));
  sess = (Sess *) obj;

  priv = SESS_PRIVATE_DATA (sess);
  if (priv->timer)
    {
      timer_cancel (priv->timer);
      priv->timer = 0;
    }

  if (++num_sessions_destroyed >= param.wsesslog.num_sessions)
    core_exit ();
}

static void
issue_calls (Sess *sess, Sess_Private_Data *priv)
{
  int i, to_create, retval, n;
  const char *method_str;
  Call *call;
  REQ *req;

  /* Mimic browser behavior of fetching html object, then a couple of
     embedded objects: */

  to_create = 1;
  if (priv->num_calls_in_this_burst > 0)
    to_create = priv->current_burst->num_reqs - priv->num_calls_in_this_burst;

  n = session_max_qlen (sess) - session_current_qlen (sess);
  if (n < to_create)
    to_create = n;

  priv->num_calls_in_this_burst += to_create;

  for (i = 0; i < to_create; ++i)
    {
      call = call_new ();
      if (!call)
	{
	  sess_failure (sess);
	  return;
	}

      /* fill in the new call: */
      req = priv->current_req;
      if (req == NULL)
	panic ("%s: internal error, requests ran past end of burst\n",
	       prog_name);

      method_str = call_method_name[req->method];
      call_set_method (call, method_str, strlen (method_str));
      call_set_uri (call, req->uri, req->uri_len);
      if (req->contents_len > 0)
	{
	  /* add "Content-length:" header and contents, if necessary: */
	  call_append_request_header (call, req->extra_hdrs,
				      req->extra_hdrs_len);
	  call_set_contents (call, req->contents, req->contents_len);
	}
      priv->current_req = req->next;

      if (DBG > 0)
	fprintf (stderr, "%s: accessing URI `%s'\n", prog_name, req->uri);

      retval = session_issue_call (sess, call);
      call_dec_ref (call);

      if (retval < 0)
	return;
    }
}

static void
user_think_time_expired (struct Timer *t, Any_Type arg)
{
  Sess *sess = arg.vp;
  Sess_Private_Data *priv;

  assert (object_is_sess (sess));

  priv = SESS_PRIVATE_DATA (sess);
  priv->timer = 0;
  issue_calls (sess, priv);
}

/* Create a new session and fill in our private information.  */
static int
sess_create (Any_Type arg)
{
  Sess_Private_Data *priv, *template;
  Sess *sess;

  if (num_sessions_generated++ >= param.wsesslog.num_sessions)
    return -1;

  sess = sess_new ();

  template = &session_templates[next_session_template];
  if (++next_session_template >= num_templates)
    next_session_template = 0;

  priv = SESS_PRIVATE_DATA (sess);
  priv->current_burst = template->current_burst;
  priv->current_req = priv->current_burst->req_list;
  priv->total_num_reqs = template->total_num_reqs;
  priv->num_calls_target = priv->current_burst->num_reqs;

  if (DBG > 0)
    fprintf (stderr, "Starting session, first burst_len = %d\n",
	     priv->num_calls_target);

  issue_calls (sess, SESS_PRIVATE_DATA (sess));
  return 0;
}

static void
prepare_for_next_burst (Sess *sess, Sess_Private_Data *priv)
{
  Time think_time;
  Any_Type arg;

  if (priv->current_burst != NULL)
    {
      think_time = priv->current_burst->user_think_time;

      /* advance to next burst: */
      priv->current_burst = priv->current_burst->next;

      if (priv->current_burst != NULL)
	{
	  priv->current_req = priv->current_burst->req_list;
	  priv->num_calls_in_this_burst = 0;
	  priv->num_calls_target += priv->current_burst->num_reqs;

	  assert (!priv->timer);
	  arg.vp = sess;
	  priv->timer = timer_schedule (user_think_time_expired,
					arg, think_time);
	}
    }
}

static void
call_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  Sess_Private_Data *priv;
  Sess *sess;
  Call *call;

  assert (et == EV_CALL_DESTROYED && object_is_call (obj));
  call = (Call *) obj;
  sess = session_get_sess_from_call (call);
  priv = SESS_PRIVATE_DATA (sess);

  if (sess->failed)
    return;

  ++priv->num_calls_destroyed;

  if (priv->num_calls_destroyed >= priv->total_num_reqs)
    /* we're done with this session */
    sess_dec_ref (sess);
  else if (priv->num_calls_in_this_burst < priv->current_burst->num_reqs)
    issue_calls (sess, priv);
  else if (priv->num_calls_destroyed >= priv->num_calls_target)
    prepare_for_next_burst (sess, priv);
}

/* Allocates memory for a REQ and assigns values to data members.
   This is used during configuration file parsing only.  */
static REQ*
new_request (char *uristr)
{
  REQ *retptr;

  retptr = (REQ *) malloc (sizeof (*retptr));
  if (retptr == NULL || uristr == NULL)
    panic ("%s: ran out of memory while parsing %s\n",
	   prog_name, param.wsesslog.file);  

  memset (retptr, 0, sizeof (*retptr));
  retptr->uri = uristr;
  retptr->uri_len = strlen (uristr);
  retptr->method = HM_GET;
  return retptr;
}

/* Like new_request except this is for burst descriptors.  */
static BURST*
new_burst (REQ *r)
{
  BURST *retptr;
    
  retptr = (BURST *) malloc (sizeof (*retptr));
  if (retptr == NULL)
    panic ("%s: ran out of memory while parsing %s\n",
	   prog_name, param.wsesslog.file);  
  memset (retptr, 0, sizeof (*retptr));
  retptr->user_think_time = param.wsesslog.think_time;
  retptr->req_list = r;
  return retptr;
}

/* Read in session-defining configuration file and create in-memory
   data structures from which to assign uri_s to calls. */
static void
parse_config (void)
{
  FILE *fp;
  int lineno, i, reqnum;
  Sess_Private_Data *sptr;
  char line[500000];	/* some uri's get pretty long */
  char uri[500000];	/* some uri's get pretty long */
  char method_str[1000];
  char this_arg[500000];
  char contents[500000];
  double think_time;
  int bytes_read;
  REQ *reqptr;
  BURST *bptr, *current_burst = 0;
  char *from, *to, *parsed_so_far;
  int ch;
  int single_quoted, double_quoted, escaped, done;

  fp = fopen (param.wsesslog.file, "r");
  if (fp == NULL)
    panic ("%s: can't open %s\n", prog_name, param.wsesslog.file);  

  num_templates = 0;
  sptr = &session_templates[0];

  for (lineno = 1; fgets (line, sizeof (line), fp); lineno++)
    {
      if (line[0] == '#')
	continue;		/* skip over comment lines */

      if (sscanf (line,"%s%n", uri, &bytes_read) != 1)
	{
	  /* must be a session-delimiting blank line */
	  if (sptr->current_req != NULL)
	    sptr++;		/* advance to next session */
	  continue;
	}
      /* looks like a request-specifying line */
      reqptr = new_request (strdup (uri));

      if (sptr->current_req == NULL)
	{
	  num_templates++;
	  if (num_templates > MAX_SESSION_TEMPLATES)
	    panic ("%s: too many sessions (%d) specified in %s\n",
		   prog_name, num_templates, param.wsesslog.file);  
	  current_burst = sptr->current_burst = new_burst (reqptr);
	}
      else
	{
	  if (!isspace (line[0]))
	    /* this uri starts a new burst */
	    current_burst = (current_burst->next = new_burst (reqptr));
	  else
	    sptr->current_req->next = reqptr;
	}
      /* do some common steps for all new requests */
      current_burst->num_reqs++;
      sptr->total_num_reqs++;
      sptr->current_req = reqptr;

      /* parse rest of line to specify additional parameters of this
	 request and burst */
      parsed_so_far = line + bytes_read;
      while (sscanf (parsed_so_far, " %s%n", this_arg, &bytes_read) == 1)
	{
	  if (sscanf (this_arg, "method=%s", method_str) == 1)
	    {
	      for (i = 0; i < HM_LEN; i++)
		{
		  if (!strncmp (method_str,call_method_name[i],
				strlen (call_method_name[i])))
		    {
		      sptr->current_req->method = i;
		      break;
		    }
		}
	      if (i == HM_LEN)
		panic ("%s: did not recognize method '%s' in %s\n",
		       prog_name, method_str, param.wsesslog.file);  
	    }
	  else if (sscanf (this_arg, "think=%lf", &think_time) == 1)
	    current_burst->user_think_time = think_time;
	  else if (sscanf (this_arg, "contents=%s", contents) == 1)
	    {
	      /* this is tricky since contents might be a quoted
		 string with embedded spaces or escaped quotes.  We
		 should parse this carefully from parsed_so_far */
	      from = strchr (parsed_so_far, '=') + 1;
	      to = contents;
	      single_quoted = FALSE;
	      double_quoted = FALSE;
	      escaped = FALSE;
	      done = FALSE;
	      while ((ch = *from++) != '\0' && !done)
		{
		  if (escaped == TRUE)
		    {
		      switch (ch)
			{
			case 'n':
			  *to++ = '\n';
			  break;
			case 'r':
			  *to++ = '\r';
			  break;
			case 't':
			  *to++ = '\t';
			  break;
			case '\n':
			  *to++ = '\n';
			  /* this allows an escaped newline to
			     continue the parsing to the next line. */
			  if (fgets(line,sizeof(line),fp) == NULL)
			    {
			      lineno++;
			      panic ("%s: premature EOF seen in '%s'\n",
				     prog_name, param.wsesslog.file);  
			    }
			  parsed_so_far = from = line;
			  break;
			default:
			  *to++ = ch;
			  break;
			}
		      escaped = FALSE;
		    }
		  else if (ch == '"' && double_quoted)
		    {
		      double_quoted = FALSE;
		    }
		  else if (ch == '\'' && single_quoted)
		    {
		      single_quoted = FALSE;
		    }
		  else
		    {
		      switch (ch)
			{
			case '\t':
			case '\n':
			case ' ':
			  if (single_quoted == FALSE &&
			      double_quoted == FALSE)
			    done = TRUE;	/* we are done */
			  else
			    *to++ = ch;
			  break;
			case '\\':		/* backslash */
			  escaped = TRUE;
			  break;
			case '"':		/* double quote */
			  if (single_quoted)
			    *to++ = ch;
			  else
			    double_quoted = TRUE;
			  break;
			case '\'':		/* single quote */
			  if (double_quoted)
			    *to++ = ch;
			  else
			    single_quoted = TRUE;
			  break;
			default:
			  *to++ = ch;
			  break;
			}
		    }
		}
	      *to = '\0';
	      from--;		/* back up 'from' to '\0' or white-space */
	      bytes_read = from - parsed_so_far;
	      if ((sptr->current_req->contents_len = strlen (contents)) != 0)
		{
		  sptr->current_req->contents = strdup (contents);
		  snprintf (sptr->current_req->extra_hdrs,
			    sizeof(sptr->current_req->extra_hdrs),
			    "Content-length: %d\r\n",
			   sptr->current_req->contents_len);
		  sptr->current_req->extra_hdrs_len =
		    strlen (sptr->current_req->extra_hdrs);
		}
	    }
	  else
	    {
	      /* do not recognize this arg */
	      panic ("%s: did not recognize arg '%s' in %s\n",
		     prog_name, this_arg, param.wsesslog.file);  
	    }
	  parsed_so_far += bytes_read;
	}
    }
  fclose (fp);

  if (DBG > 3)
    {
      fprintf (stderr,"%s: session list follows:\n\n", prog_name);

      for (i = 0; i < num_templates; i++)
	{
	  sptr = &session_templates[i];
	  fprintf (stderr, "#session %d (total_reqs=%d):\n",
		   i, sptr->total_num_reqs);
	    
	  for (bptr = sptr->current_burst; bptr; bptr = bptr->next)
	    {
	      for (reqptr = bptr->req_list, reqnum = 0;
		   reqptr;
		   reqptr = reqptr->next, reqnum++)
		{
		  if (reqnum >= bptr->num_reqs)
		    panic ("%s: internal error detected in parsing %s\n",
			   prog_name, param.wsesslog.file);  
		  if (reqnum > 0)
		    fprintf (stderr, "\t");
		  fprintf (stderr, "%s", reqptr->uri);
		  if (reqnum == 0
		      && bptr->user_think_time != param.wsesslog.think_time)
		    fprintf (stderr, " think=%0.2f",
			     (double) bptr->user_think_time);
		  if (reqptr->method != HM_GET)
		    fprintf (stderr," method=%s",
			     call_method_name[reqptr->method]);
		  if (reqptr->contents != NULL)
		    fprintf (stderr, " contents='%s'", reqptr->contents);
		  fprintf (stderr, "\n");
		}
	    }
	  fprintf (stderr, "\n");
	}
    }
}

static void
init (void)
{
  Any_Type arg;

  parse_config ();

  sess_private_data_offset = object_expand (OBJ_SESS,
					    sizeof (Sess_Private_Data));
  rg_sess.rate = &param.rate;
  rg_sess.tick = sess_create;
  rg_sess.arg.l = 0;

  arg.l = 0;
  event_register_handler (EV_SESS_DESTROYED, sess_destroyed, arg);
  event_register_handler (EV_CALL_DESTROYED, call_destroyed, arg);

  /* This must come last so the session event handlers are executed
     before this module's handlers.  */
  session_init ();
}

static void
start (void)
{
  rate_generator_start (&rg_sess, EV_SESS_DESTROYED);
}

Load_Generator wsesslog =
  {
    "creates log-based session workload",
    init,
    start,
    no_op
  };
07070100000029000081ED0000000000000000000000015FCD00020000344B000000000000000000000000000000000000002F00000000httperf-0.9.0+git.20201206/src/gen/wsesspage.c/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

/* Similar to wsess but instead of generating fixed bursts, each
   fetched html page is parsed and the embedded objects are fetched in
   a burst.

   This is NOT a high performance workload generator!  Use it only for
   non-performance critical tests.  */

#include "config.h"

#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <generic_types.h>

#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <core.h>
#include <localevent.h>
#include <rate.h>
#include <session.h>

#define CALL_PRIVATE_DATA(c) \
  ((Call_Private_Data *) ((char *)(c) + call_private_data_offset))
#define SESS_PRIVATE_DATA(c) \
  ((Sess_Private_Data *) ((char *)(c) + sess_private_data_offset))

typedef struct Call_Private_Data
  {
    enum
      {
	P_INITIAL,
	P_HTML,
	P_CMD,		/* we saw a `<' and are scanning for the end of CMD */
	P_DASH_ONE,	/* looking for the first dash of a comment close */
	P_DASH_TWO,	/* looking for the second dash of a comment close */
	P_RANGLE,	/* looking for '>' */
	P_SRC,		/* we're looking for "src" */
	P_DATA,		/* we're looking for "data" */
	P_LQUOTE,	/* we're looking for the left quote of a URI */
	P_NAKED_URI,	/* we're looking for an unquoted URI */
	P_QUOTED_URI	/* we're looking for a quoted URI */
      }
    state;
    int buf_len;
    char buf[1024];
    void *to_free;	/* call queue element to free when done */
  }
Call_Private_Data;

typedef struct Sess_Private_Data
  {
    u_int num_created;		/* # of calls created in this burst */
    u_int num_destroyed;	/* # of calls destroyed in this burst */
    u_int num_reqs_completed;	/* # of user reqs completed */
    struct Timer *timer;		/* timer for session think time */
    struct uri_list
      {
	struct uri_list *next;
	size_t uri_len;
	char uri[1];		/* really URI_LEN+1 bytes... */
      }
    *uri_list;
  }
Sess_Private_Data;

static size_t sess_private_data_offset;
static size_t call_private_data_offset;

static int num_sessions_generated;
static int num_sessions_destroyed;
static Rate_Generator rg_sess;

static size_t prefix_len;
static char *prefix;

static void
issue_calls (Sess *sess, Sess_Private_Data *priv)
{
  int i, to_create, retval, embedded = 0;
  Call_Private_Data *cpriv;
  struct uri_list *el;
  Call *call;

  /* Mimic browser behavior of fetching html object, then a couple of
     embedded objects: */

  to_create = 1;
  if (priv->num_created > 0)
    {
      to_create = session_max_qlen (sess) - session_current_qlen (sess);
      embedded = 1;
    }

  for (i = 0; i < to_create && (!embedded || priv->uri_list); ++i)
    {
      ++priv->num_created;

      call = call_new ();
      if (!call)
	{
	  sess_failure (sess);
	  return;
	}
      if (embedded)
	{
	  el = priv->uri_list;
	  priv->uri_list = el->next;

	  cpriv = CALL_PRIVATE_DATA (call);
	  cpriv->to_free = el;
	  call_set_uri (call, el->uri, el->uri_len);
	}

      if (verbose > 1)
	printf ("%s: fetching `%s'\n",
		prog_name, (char *)call->req.iov[IE_URI].iov_base);

      retval = session_issue_call (sess, call);
      call_dec_ref (call);
      if (retval < 0)
	return;
    }
}

static void
fetch_uri (Sess *sess, Sess_Private_Data *priv, Call_Private_Data *cpriv,
	   const char *uri, size_t uri_len)
{
  struct uri_list *el;
  int is_relative;
  size_t len;
  char *dst;

  if (strchr (uri, ':'))
    {
      len = strlen (param.server);
      if (strncmp (uri, "http://", 7) == 0
	  && strncmp (uri + 7, param.server, len) == 0
	  && uri[7 + len] == '/')
	{
	  uri += 7 + len;
	  uri_len -= 7 + len;
	}
      else
	{
	  /* Eventually, we may want to create new sessions on the fly,
	     but for now, we simply punt on non-absolute URIs */
	  if (verbose > 1)
	    fprintf (stderr, "%s: ignoring absolute URI `%s'\n",
		     prog_name, uri);
	  return;
	}
    }

  is_relative = (uri[0] != '/');

  /* enqueue the new uri: */
  len = uri_len;
  if (is_relative)
    len += prefix_len;
  el = malloc (sizeof (*el) + len);
  if (!el)
    panic ("%s.fetch_uri: out of memory!\n", prog_name);

  el->uri_len = len;
  dst = el->uri;
  if (is_relative)
    {
      memcpy (el->uri, prefix, prefix_len);
      dst += prefix_len;
    }
  memcpy (dst, uri, uri_len + 1);

  el->next = priv->uri_list;
  priv->uri_list = el;

  issue_calls (sess, priv);
}

static void
user_think_time_expired (struct Timer *t, Any_Type arg)
{
  Sess *sess = arg.vp;
  Sess_Private_Data *priv;

  assert (object_is_sess (sess));

  priv = SESS_PRIVATE_DATA (sess);
  priv->timer = 0;

  issue_calls (sess, priv);
}

static void
call_recv_hdr (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  Call_Private_Data *cpriv;
  Sess_Private_Data *priv;
  struct iovec *line;
  Call *call;
  Sess *sess;
  char *hdr;

  assert (et == EV_CALL_RECV_HDR && object_is_call (obj));
  call = (Call *) obj;
  cpriv = CALL_PRIVATE_DATA (call);
  sess = session_get_sess_from_call (call);
  priv = SESS_PRIVATE_DATA (sess);

  line = callarg.vp;
  hdr = line->iov_base;

  switch (tolower (hdr[0]))
    {
    case 'c':
      if (line->iov_len >= 23
	  && strncasecmp (hdr + 1, "ontent-type: text/html", 22) == 0)
	cpriv->state = P_HTML;
      break;

    case 'l':
      if (line->iov_len > 10
	  && strncasecmp (hdr + 1, "ocation: ", 9) == 0)
	fetch_uri (sess, priv, cpriv, hdr + 10, line->iov_len - 10);
      break;
    }

}

static void
call_recv_data (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  Call_Private_Data *cpriv;
  Sess_Private_Data *priv;
  const char *cp, *end;
  struct iovec *line;
  Sess *sess;
  Call *call;
  int ch;

  assert (et == EV_CALL_RECV_DATA && object_is_call (obj));
  call = (Call *) obj;
  cpriv = CALL_PRIVATE_DATA (call);
  sess = session_get_sess_from_call (call);
  priv = SESS_PRIVATE_DATA (sess);

  if (cpriv->state == P_INITIAL)
    return;	/* not an html object */

  line = callarg.vp;
  cp = line->iov_base;
  end = cp + line->iov_len;
  while (cp < end)
    {
      ch = *cp++;

      switch (cpriv->state)
	{
	case P_INITIAL:
	  break;

	case P_HTML:
	  cpriv->buf_len = 0;
	  if (ch == '<')
	    cpriv->state = P_CMD;
	  break;

	case P_CMD:
	  if (isspace (ch) || ch == '=')
	    {
	      if (cpriv->buf_len > 0)
		{
		  if (DBG > 3)
		    fprintf (stderr, "found command `%.*s'\n",
			     cpriv->buf_len, cpriv->buf);

		  if (cpriv->buf_len == 3
		      && strcmp (cpriv->buf, "!--") == 0)
		    cpriv->state = P_DASH_ONE;
		  else if (cpriv->buf_len == 5
			   && strncasecmp (cpriv->buf, "frame", 5) == 0)
		    cpriv->state = P_SRC;
		  else if (cpriv->buf_len == 6
			   && strncasecmp (cpriv->buf, "iframe", 6) == 0)
		    cpriv->state = P_SRC;
		  else if (cpriv->buf_len == 6
			   && strncasecmp (cpriv->buf, "data", 6) == 0)
		    cpriv->state = P_DATA;
		  else if (cpriv->buf_len == 3
			   && strncasecmp (cpriv->buf, "img", 3) == 0)
		    cpriv->state = P_SRC;
		  cpriv->buf_len = 0;
		}
	      else
		cpriv->state = P_HTML;
	    }
	  else if (ch == '>')
	    cpriv->state = P_HTML;
	  else if (cpriv->buf_len < sizeof (cpriv->buf))
	    cpriv->buf[cpriv->buf_len++] = ch;
	  break;

	case P_DASH_ONE:
	  if (ch == '-')
	    cpriv->state = P_DASH_TWO;
	  break;

	case P_DASH_TWO:
	  cpriv->state = (ch == '-') ? P_RANGLE : P_DASH_ONE;
	  break;

	case P_RANGLE:
	  if (ch == '>')
	    cpriv->state = P_HTML;
	  break;

	case P_SRC:
	  if (ch == '>')
	    cpriv->state = P_HTML;
	  else
	    {
	      cpriv->buf[cpriv->buf_len++] = ch;
	      if (cpriv->buf_len == 4)
		{
		  if (strncasecmp (cpriv->buf, "src=", 4) == 0)
		    {
		      cpriv->state = P_LQUOTE;
		      cpriv->buf_len = 0;
		    }
		  else
		    {
		      memcpy (cpriv->buf, cpriv->buf + 1, 3);
		      cpriv->buf_len = 3;
		    }
		}
	    }
	  break;

	case P_DATA:
	  if (ch == '>')
	    cpriv->state = P_HTML;
	  else
	    {
	      cpriv->buf[cpriv->buf_len++] = ch;
	      if (cpriv->buf_len == 5)
		{
		  if (strncasecmp (cpriv->buf, "data=", 5) == 0)
		    {
		      cpriv->state = P_LQUOTE;
		      cpriv->buf_len = 0;
		    }
		  else
		    {
		      memcpy (cpriv->buf, cpriv->buf + 1, 4);
		      cpriv->buf_len = 4;
		    }
		}
	    }
	  break;

	case P_LQUOTE:
	  if (ch == '"')
	    cpriv->state = P_QUOTED_URI;
	  else if (!isspace (ch))
	    {
	      cpriv->state = P_NAKED_URI;
	      cpriv->buf[cpriv->buf_len++] = ch;
	    }
	  break;

	case P_NAKED_URI:
	case P_QUOTED_URI:
	  if ((cpriv->state == P_QUOTED_URI && ch == '"')
	      || (cpriv->state == P_NAKED_URI && isspace (ch)))
	    {
	      cpriv->buf[cpriv->buf_len] = '\0';
	      fetch_uri (sess, priv, cpriv, cpriv->buf, cpriv->buf_len);
	      cpriv->state = P_HTML;
	      cpriv->buf_len = 0;
	    }
	  else if (cpriv->buf_len < sizeof (cpriv->buf) - 1)
	    cpriv->buf[cpriv->buf_len++] = ch;
	  break;
	}
    }
}

static void
call_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  Call_Private_Data *cpriv;
  Sess_Private_Data *priv;
  Any_Type arg;
  Sess *sess;
  Call *call;

  assert (et == EV_CALL_DESTROYED && object_is_call (obj));

  call = (Call *) obj;
  cpriv = CALL_PRIVATE_DATA (call);
  sess = session_get_sess_from_call (call);
  priv = SESS_PRIVATE_DATA (sess);

  if (cpriv->to_free)
    {
      free (cpriv->to_free);
      cpriv->to_free = 0;
    }
  ++priv->num_destroyed;

  if (sess->failed)
    return;

  if (priv->uri_list)
    /* there are some queued URI's which we may be able to issue now */
    issue_calls (sess, priv);
  else if (priv->num_destroyed >= priv->num_created)
    {
      /* we're done with this burst */
      if (++priv->num_reqs_completed >= param.wsesspage.num_reqs)
	/* we're done with this session */
	sess_dec_ref (sess);
      else 
	{
	  /* schedule the user-think-time timer */
	  priv->num_created = 0;
	  assert (!priv->timer);
	  arg.vp = sess;
	  priv->timer = timer_schedule (user_think_time_expired, arg,
					param.wsesspage.think_time);
	}
    }
}

/* Create a new session.  */
static int
sess_create (Any_Type arg)
{
  Sess_Private_Data *priv;
  Sess *sess;

  if (num_sessions_generated++ >= param.wsesspage.num_sessions)
    return -1;

  sess = sess_new ();
  if (!sess)
    return 1;

  priv = SESS_PRIVATE_DATA (sess);

  issue_calls (sess, SESS_PRIVATE_DATA (sess));
  return 0;
}

static void
sess_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  Sess_Private_Data *priv;
  Sess *sess;

  assert (et == EV_SESS_DESTROYED && object_is_sess (obj));
  sess = (Sess *) obj;

  priv = SESS_PRIVATE_DATA (sess);
  if (priv->timer)
    {
      timer_cancel (priv->timer);
      priv->timer = 0;
    }

  if (++num_sessions_destroyed >= param.wsesspage.num_sessions)
    core_exit ();
}

static void
init (void)
{
  const char *slash;
  Any_Type arg;

  slash = strrchr (param.uri, '/');
  if (slash)
    prefix_len = (slash + 1) - param.uri;
  else
    panic ("%s: URI specified with --uri must be absolute", prog_name);

  prefix = strdup (param.uri);
  prefix[prefix_len] = '\0';

  session_init ();

  call_private_data_offset = object_expand (OBJ_CALL,
					    sizeof (Call_Private_Data));
  sess_private_data_offset = object_expand (OBJ_SESS,
					    sizeof (Sess_Private_Data));
  rg_sess.rate = &param.rate;
  rg_sess.tick = sess_create;
  rg_sess.arg.l = 0;

  arg.l = 0;
  event_register_handler (EV_CALL_RECV_HDR, call_recv_hdr, arg);
  event_register_handler (EV_CALL_RECV_DATA, call_recv_data, arg);
  event_register_handler (EV_SESS_DESTROYED, sess_destroyed, arg);
  event_register_handler (EV_CALL_DESTROYED, call_destroyed, arg);
}

static void
start (void)
{
  rate_generator_start (&rg_sess, EV_SESS_DESTROYED);
}

Load_Generator wsesspage =
  {
    "creates sessions that fetch html pages and embedded objects",
    init,
    start,
    no_op
  };
0707010000002A000081ED0000000000000000000000015FCD0002000027D2000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/src/http.c/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

#include "config.h"

#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <generic_types.h>

#include <object.h>
#include <timer.h>
#include <call.h>
#include <conn.h>
#include <localevent.h>
#include <httperf.h>
#include <http.h>

/* Read a CRLF terminated line of characters into c->reply.line.
   Returns 1 when the line is complete, 0 when the line is incomplete
   and more data is needed.  */
static int
get_line (Call *c, char **bufp, size_t *buf_lenp)
{
  size_t to_copy, buf_len = *buf_lenp;
  Conn *s = c->conn;
  char *buf = *bufp;
  const char *eol;
  int has_lf;

  if (buf_len <= 0)
    return 0;

  /* Note that core.c guarantees that BUF is '\0' terminated. */
  eol = strchr (buf, '\n');
  if (eol)
    ++eol;
  else
    eol = buf + buf_len;

  to_copy = eol - buf;
  buf_len -= to_copy;
  if (s->line.iov_len + to_copy >= sizeof (s->line_buf))
    {
      fprintf (stderr,
	       "%s.get_line: truncating header from %lu to %lu bytes\n",
	       prog_name, (u_long) (s->line.iov_len + to_copy),
	       (u_long) sizeof (s->line_buf));
      to_copy = sizeof (s->line_buf) - 1 - s->line.iov_len;
    }
  memcpy ((char *) s->line.iov_base + s->line.iov_len, buf, to_copy);
  s->line.iov_len += to_copy;

  has_lf = ((char *) s->line.iov_base)[s->line.iov_len - 1] == '\n';

  *bufp = (char *) eol;
  *buf_lenp = buf_len;

  if (has_lf || s->line.iov_len == sizeof (s->line_buf) - 1)
    {
      /* We got a full header line.  Chop off \r\n at the tail if
	 necessary.  */
      if (((char *) s->line.iov_base)[s->line.iov_len - 1] == '\n')
	{
	  --s->line.iov_len;
	  if (((char *) s->line.iov_base)[s->line.iov_len - 1] == '\r')
	    --s->line.iov_len;
	}
      ((char *) s->line.iov_base)[s->line.iov_len] = '\0';
      return 1;
    }
  return 0;
}

static void
parse_status_line (Call *c, char **bufp, size_t *buf_lenp)
{
  char *buf, *buf_start = *bufp;
  u_int major, minor, status;
  Conn *s = c->conn;
  Any_Type arg;

  s->is_chunked = 0;

  /* default to "infinite" content length: */
  s->content_length = ~(size_t) 0;

  if (!get_line (c, bufp, buf_lenp))
    return;

  buf = c->conn->line.iov_base;
  if (sscanf (buf, "HTTP/%u.%u %u ", &major, &minor, &status) == 3)
    {
      c->reply.version = 0x10000*major + minor;
      c->reply.status = status;
    }
  else
    {
      c->reply.version = 0x10000;		/* default to 1.0 */
      c->reply.status = 599;
      if (c->reply.status == 599)
	fprintf (stderr, "%s.parse_status_line: invalid status line `%s'!!\n",
		 prog_name, buf);
    }
  if (DBG > 0)
    fprintf (stderr,
	     "parse_status_line.%lu: reply is HTTP/%u.%u, status = %d\n",
	     c->id, c->reply.version / 0x10000, c->reply.version & 0xffff,
	     c->reply.status);

  /* Determine whether we should be expecting a message body.  HEAD
     never includes an entity.  For other methods, things depend on
     the status code.  */

  if (strcmp ((char *) c->req.iov[IE_METHOD].iov_base, "HEAD") == 0)
    s->has_body = 0;
  else
    {
      s->has_body = 1;
      switch (status / 100)
	{
	case 1: /* informational */
	  s->has_body = 0;
	  if (status == 100)
	    {
	      arg.l = c->reply.status;
	      event_signal (EV_CALL_RECV_START, (Object *) c, arg);
	      s->state = S_REPLY_CONTINUE;
	      goto done;
	    }
	  break;

	case 2: /* success */
	case 3: /* redirection */
	  switch (status)
	    {
	    case 204: /* No Content */
	    case 205: /* Reset Content */
	    case 304: /* Not Modified */
	      s->has_body = 0;
	      break;
	    }
	  break;

	case 4: /* client errors */
	case 5: /* server errors */
	  break;

	default:
	  fprintf (stderr, "%s.parse_status_line: bad status %u\n",
		   prog_name, status);
	  break;
	}
    }
  arg.l = c->reply.status;
  event_signal (EV_CALL_RECV_START, (Object *) c, arg);
  if (s->state >= S_CLOSING)
    return;
  s->state = S_REPLY_HEADER;

 done:
  c->reply.header_bytes += *bufp - buf_start;
  s->line.iov_len = 0;		
}

static void
parse_headers (Call *c, char **bufp, size_t *buf_lenp)
{
  char *hdr, *buf_start = *bufp;
  Conn *s = c->conn;
  size_t hdr_len;
  Any_Type arg;

  while (get_line (c, bufp, buf_lenp) > 0)
    {
      hdr = s->line.iov_base;
      hdr_len = s->line.iov_len;

      if (!hdr_len)
	{
	  /* empty header implies end of headers */
	  if (s->has_body)
	    if (s->is_chunked)
	      {
		s->content_length = 0;
		s->state = S_REPLY_CHUNKED;
	      }
	    else
	      s->state = S_REPLY_DATA;
	  else if (s->state == S_REPLY_CONTINUE)
	    s->state = S_REPLY_HEADER;
	  else
	    s->state = S_REPLY_DONE;
	  break;
	}

      /* process line as a regular header: */
      switch (tolower (*hdr))
	{
	case 'c':
	  if (strncasecmp (hdr, "content-length:", 15) == 0)
	    {
	      hdr += 15;
	      s->content_length = strtoul (hdr, 0, 10);
	      if (!s->content_length)
	    s->has_body = 0;
	    }
	  break;

	case 't':
	  if (strncasecmp (hdr, "transfer-encoding:", 18) == 0)
	    {
	      hdr += 18;
	      while (isspace (*hdr))
		++hdr;
	      if (strcasecmp (hdr, "chunked") == 0)
		s->is_chunked = 1;
	      else
		fprintf (stderr, "%s.parse_headers: unknown transfer "
			 "encoding `%s'\n", prog_name, hdr);
	    }
	  break;
	}
      arg.vp = &s->line;
      event_signal (EV_CALL_RECV_HDR, (Object *) c, arg);
      if (s->state >= S_CLOSING)
	return;
      s->line.iov_len = 0;		
    }
  c->reply.header_bytes += *bufp - buf_start;
}

static void
parse_footers (Call *c, char **bufp, size_t *buf_lenp)
{
  char *hdr, *buf_start = *bufp;
  Conn *s = c->conn;
  size_t hdr_len;
  Any_Type arg;

  while (get_line (c, bufp, buf_lenp) > 0)
    {
      hdr = s->line.iov_base;
      hdr_len = s->line.iov_len;

      if (!hdr_len)
	{
	  /* empty footer implies end of footers */
	  s->state = S_REPLY_DONE;
	  break;
	}
      /* process line as a regular footer: */
      arg.vp = &s->line;
      event_signal (EV_CALL_RECV_FOOTER, (Object *) c, arg);
      if (s->state >= S_CLOSING)
	return;
      s->line.iov_len = 0;		
    }
  c->reply.footer_bytes += *bufp - buf_start;
}

static int
parse_data (Call *c, char **bufp, size_t *buf_lenp)
{
  size_t bytes_needed, buf_len = *buf_lenp;
  Conn *s = c->conn;
  char *buf = *bufp;
  struct iovec iov;
  Any_Type arg;

  bytes_needed = (s->content_length - c->reply.content_bytes);

  if (buf_len > bytes_needed)
    buf_len = bytes_needed;

  iov.iov_base = (caddr_t) buf;
  iov.iov_len = buf_len;
  arg.vp = &iov;
  event_signal (EV_CALL_RECV_DATA, (Object *) c, arg);

  c->reply.content_bytes += buf_len;
  *bufp = buf + buf_len;
  *buf_lenp -= buf_len;

  return (buf_len == bytes_needed);
}

static void
xfer_chunked  (Call *c, char **bufp, size_t *buf_lenp)
{
  Conn *s = c->conn;
  size_t chunk_length;
  char *end;

  while (*buf_lenp > 0 && s->state < S_CLOSING)
    {
      if (c->reply.content_bytes >= s->content_length)
	{
	  /* need to parse next chunk length line: */
	  if (!get_line (c, bufp, buf_lenp))
	    return;				/* need more data */
	  if (s->line.iov_len == 0)
	    continue;				/* skip over empty line */

	  errno = 0;
	  chunk_length = strtoul (s->line.iov_base, &end, 16);
	  s->line.iov_len = 0;
	  if (errno == ERANGE || end == s->line.iov_base)
	    {
	      fprintf (stderr, "%s.xfer_chunked: bad chunk line `%s'\n",
		       prog_name, (char *) s->line.iov_base);
	      continue;
	    }

	  if (chunk_length == 0)
	    {
	      /* a final chunk of zero bytes indicates the end of the reply */
	      s->state = S_REPLY_FOOTER;
	      return;
	    }
	  s->content_length += chunk_length;
	}
      parse_data (c, bufp, buf_lenp);
    }
}

void
http_process_reply_bytes (Call *c, char **bufp, size_t *buf_lenp)
{
  Conn *s = c->conn;
  struct iovec iov;
  Any_Type arg;

  iov.iov_base = *bufp;
  iov.iov_len = *buf_lenp;
  arg.vp = &iov;
  event_signal (EV_CALL_RECV_RAW_DATA, (Object *) c, arg);

  do
    {
      switch (s->state)
	{
	case S_REPLY_STATUS:
	  parse_status_line (c, bufp, buf_lenp);
	  break;

	case S_REPLY_HEADER:
	  parse_headers (c, bufp, buf_lenp);
	  break;

	case S_REPLY_FOOTER:
	  parse_footers (c, bufp, buf_lenp);
	  break;

	case S_REPLY_DATA:
	  if (parse_data (c, bufp, buf_lenp) && s->state < S_CLOSING)
	    s->state = S_REPLY_DONE;
	  break;

	case S_REPLY_CONTINUE:
	  parse_headers (c, bufp, buf_lenp);
	  break;

	case S_REPLY_CHUNKED:
	  xfer_chunked (c, bufp, buf_lenp);
	  break;

	case S_REPLY_DONE:
	  return;

	default:
	  fprintf (stderr, "%s.http_process_reply_bytes: bad state %d\n",
		   prog_name, s->state);
	  exit (1);
	}
    }
  while (*buf_lenp > 0 && s->state < S_CLOSING);
}
0707010000002B000081ED0000000000000000000000015FCD0002000006A1000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/src/http.h/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

#ifndef http_h
#define http_h

extern void http_process_reply_bytes (Call *c, char **buf, size_t *buf_len);

#endif /* http_h */
0707010000002C000081ED0000000000000000000000015FCD00020000A6F5000000000000000000000000000000000000002900000000httperf-0.9.0+git.20201206/src/httperf.c/*
 * Copyright (C) 2000-2007 Hewlett-Packard Company
 * Copyright (C) 2007 Ted Bullock <tbullock@comlore.com>
 * 
 * This file is part of httperf, a web server performance measurment tool.
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 * 
 * In addition, as a special exception, the copyright holders give permission
 * to link the code of this work with the OpenSSL project's "OpenSSL" library
 * (or with modified versions of it that use the same license as the "OpenSSL" 
 * library), 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 this file, you may extend this
 * exception to your version of the file, but you are not obligated to do so.
 * If you do not wish to do so, delete this exception statement from your
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 
 */

/*
 * Fundamentals:
 * 
 * There are three subsystems to httperf:
 * 
 * 1) The load generator which determines what URI is fetched next.
 * 
 * 2) The core engine that handles the mechanics of issuing a request.
 * 
 * 3) The instrumentation infrastructure that measures various aspects of the
 * transaction(s).
 * 
 * Since there is considerable potential variation in all three, it seems like 
 * an event-based approach might be ideal in tying the three together.
 * Ideally, it should be possible to write a new load generator without
 * modifications to the other subsystems.  Similarly, it should be possible to 
 * add instrumentation without requiring changes to the load generator or http 
 * engine.
 * 
 * Axioms: - The only point at which the client will fall back is if the
 * client itself is overloaded.  There is no point trying to fix up this
 * case---simply declare defeat and abort the test. 
 */
#include "config.h"

#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#ifdef __FreeBSD__
/* Required for fpsetmask() under FreeBSD */
#include <ieeefp.h>
#endif

#include <sys/time.h>

#include <generic_types.h>
#include <sys/resource.h>	/* after sys/types.h for BSD (in generic_types.h) */

#include <object.h>
#include <timer.h>
#include <conn.h>
#include <call.h>
#include <core.h>
#include <localevent.h>
#include <httperf.h>


#ifdef HAVE_SSL
#  include <openssl/rand.h>
#endif

#define RATE_INTERVAL	5.0

const char     *prog_name;
int             verbose;
int             periodic_stats;
Cmdline_Params  param;
Time            test_time_start;
Time            test_time_stop;
struct rusage   test_rusage_start;
struct rusage   test_rusage_stop;
size_t          object_type_size[OBJ_NUM_TYPES];

#ifdef HAVE_SSL
SSL_CTX        *ssl_ctx;
#endif

#ifdef DEBUG
int             debug_level;
#endif

static Time     perf_sample_start;

static struct option longopts[] = {
	{"add-header", required_argument, (int *) &param.additional_header, 0},
	{"add-header-file", required_argument, (int *) &param.additional_header_file, 0 },
	{"burst-length", required_argument, (int *) &param.burst_len, 0},
	{"client", required_argument, (int *) &param.client, 0},
	{"close-with-reset", no_argument, &param.close_with_reset, 1},
	{"debug", required_argument, 0, 'd'},
	{"failure-status", required_argument, &param.failure_status, 0},
	{"help", no_argument, 0, 'h'},
	{"hog", no_argument, &param.hog, 1},
	{"http-version", required_argument, (int *) &param.http_version, 0},
	{"max-connections", required_argument, (int *) &param.max_conns, 0},
	{"max-piped-calls", required_argument, (int *) &param.max_piped, 0},
	{"method", required_argument, (int *) &param.method, 0},
	{"myaddr", required_argument, (int *) &param.myaddr, 0},
	{"no-host-hdr", no_argument, &param.no_host_hdr, 1},
	{"num-calls", required_argument, (int *) &param.num_calls, 0},
	{"num-conns", required_argument, (int *) &param.num_conns, 0},
	{"period", required_argument, (int *) &param.rate.mean_iat, 0},
	{"port", required_argument, (int *) &param.port, 0},
	{"print-reply", optional_argument, &param.print_reply, 0},
	{"print-request", optional_argument, &param.print_request, 0},
	{"rate", required_argument, (int *) &param.rate, 0},
	{"recv-buffer", required_argument, (int *) &param.recv_buffer_size, 0},
	{"retry-on-failure", no_argument, &param.retry_on_failure, 1},
	{"runtime", required_argument, (int *) &param.runtime, 0},
	{"send-buffer", required_argument, (int *) &param.send_buffer_size, 0},
	{"server", required_argument, (int *) &param.server, 0},
	{"server-name", required_argument, (int *) &param.server_name, 0},
	{"servers", required_argument, (int *) &param.servers, 0},
	{"uri", required_argument, (int *) &param.uri, 0},
	{"session-cookies", no_argument, (int *) &param.session_cookies, 1},
#ifdef HAVE_SSL
	{"ssl", no_argument, &param.use_ssl, 1},
	{"ssl-ciphers", required_argument, (int *) &param.ssl_cipher_list, 0},
	{"tls-server-name", required_argument, (int *) &param.tls_server_name, 0},
	{"ssl-no-reuse", no_argument, &param.ssl_reuse, 0},
        {"ssl-certificate", required_argument, (int *) &param.ssl_cert,     0},
        {"ssl-key",      required_argument, (int *) &param.ssl_key,         0},
        {"ssl-verify",   optional_argument, (int *) &param.ssl_verify,      0},
        {"ssl-ca-file",  required_argument, (int *) &param.ssl_ca_file,     0},
        {"ssl-ca-path",  required_argument, (int *) &param.ssl_ca_path,     0},
        {"ssl-protocol", required_argument, &param.ssl_protocol,            0},
#endif
	{"think-timeout", required_argument, (int *) &param.think_timeout, 0},
	{"timeout", required_argument, (int *) &param.timeout, 0},
	{"use-timer-cache", no_argument, &param.use_timer_cache, 1},
	{"verbose", no_argument, 0, 'v'},
	{"version", no_argument, 0, 'V'},
	{"periodic-stats", no_argument, 0, 'n'},
	{"wlog", required_argument, (int *) &param.wlog, 0},
	{"wsess", required_argument, (int *) &param.wsess, 0},
	{"wsesslog", required_argument, (int *) &param.wsesslog, 0},
	{"wsesspage", required_argument, (int *) &param.wsesspage, 0},
	{"wset", required_argument, (int *) &param.wset, 0},
	{0, 0, 0, 0}
};

static void
usage(void)
{
	printf("Usage: %s "
	       "[-hdvV] [--add-header S] [--burst-length N] [--client N/N]\n"
	       "\t[--close-with-reset] [--debug N] [--failure-status N]\n"
	       "\t[--help] [--hog] [--http-version S] [--max-connections N]\n"
	       "\t[--max-piped-calls N] [--method S] [--no-host-hdr]\n"
	       "\t[--num-calls N] [--num-conns N] [--session-cookies]\n"
	       "\t[--period [d|u|e]T1[,T2]|[v]T1,D1[,T2,D2]...[,Tn,Dn]\n"
	       "\t[--print-reply [header|body]] [--print-request [header|body]]\n"
	       "\t[--rate X] [--recv-buffer N] [--retry-on-failure] [--send-buffer N]\n"
	       "\t[--server S|--servers file] [--server-name S] [--port N] [--uri S] "
	       "[--myaddr S]\n"
#ifdef HAVE_SSL
	       "\t[--ssl] [--ssl-ciphers L] [--ssl-no-reuse]\n"
               "\t[--ssl-certificate file] [--ssl-key file]\n"
               "\t[--ssl-ca-file file] [--ssl-ca-path path]\n"
               "\t[--ssl-verify [yes|no]] [--ssl-protocol S]\n"
#endif
	       "\t[--think-timeout X] [--timeout X] [--verbose] [--version]\n"
	       "\t[--wlog y|n,file] [--wsess N,N,X] [--wsesslog N,X,file]\n"
	       "\t[--wset N,X]\n"
	       "\t[--runtime X]\n"
	       "\t[--use-timer-cache]\n"
	       "\t[--periodic-stats]\n", prog_name);
}

void
panic(const char *msg, ...)
{
	va_list         va;

	va_start(va, msg);
	vfprintf(stderr, msg, va);
	va_end(va);
	exit(1);
}

void
no_op(void)
{
}

static void
perf_sample(struct Timer *t, Any_Type regarg)
{
	Any_Type        callarg;

	callarg.d = 1.0 / (timer_now() - perf_sample_start);
	event_signal(EV_PERF_SAMPLE, 0, callarg);

	/*
	 * prepare for next sample interval: 
	 */
	perf_sample_start = timer_now();
	if (timer_schedule(perf_sample, regarg, RATE_INTERVAL) == NULL)
		panic("%s(%d): Received NULL from timer_schedule\n", __func__,
		      __LINE__);
}

int
main(int argc, char **argv)
{
	extern Load_Generator uri_fixed, uri_wlog, uri_wset, conn_rate,
	    call_seq;
	extern Load_Generator wsess, wsesslog, wsesspage, sess_cookie, misc;
	extern Stat_Collector stats_basic, session_stat;
	extern Stat_Collector stats_print_reply;
	extern char    *optarg;
	int             session_workload = 0;
	int             num_gen = 3;
	Load_Generator *gen[5] = {
		&call_seq,
		&uri_fixed,
		&conn_rate,
	};
	int             num_stats = 1;
	Stat_Collector *stat[3] = {
		&stats_basic
	};
	int             i, ch, longindex;
	u_int           minor, major;
	char           *end, *name;
	Any_Type        arg;
	void           *flag;
	Time            t;

	int             numRates = 0;

#ifdef __FreeBSD__
	/*
	 * This works around a bug in earlier versions of FreeBSD that cause
	 * non-finite IEEE arithmetic to cause SIGFPE instead of the
	 * non-finite arithmetic as defined by IEEE.  
	 */
	fpsetmask(0);
#endif

	object_type_size[OBJ_CONN] = sizeof(Conn);
	object_type_size[OBJ_CALL] = sizeof(Call);

	param.http_version = 0x10001;	/* default to HTTP/1.1 */
	param.client.id = 0;
	param.client.num_clients = 1;
	param.port = -1;
	param.uri = "/";
	param.num_calls = 1;
	param.burst_len = 1;
	param.num_conns = 1;
	/*
	 * These should be set to the minimum of 2*bandwidth*delay and the
	 * maximum request/reply size for single-call connections.  
	 */
	param.send_buffer_size = 4096;
	param.recv_buffer_size = 16384;
	param.rate.dist = DETERMINISTIC;
#ifdef HAVE_SSL
	param.ssl_reuse = 1;
        param.ssl_verify = 0;
        param.ssl_protocol = 0;
#endif

	/*
	 * get program name: 
	 */
	prog_name = strrchr(argv[0], '/');
	if (prog_name)
		++prog_name;
	else
		prog_name = argv[0];

	/*
	 * process command line options: 
	 */
	while ((ch =
		getopt_long(argc, argv, "d:hvVn", longopts, &longindex)) >= 0) {
		switch (ch) {
		case 0:
			flag = longopts[longindex].flag;

			if (flag == &param.method)
				param.method = optarg;
			else if (flag == &param.additional_header)
				param.additional_header = optarg;
			else if (flag == &param.additional_header_file)
				param.additional_header_file = optarg;
			else if (flag == &param.num_calls) {
				errno = 0;
				param.num_calls = strtoul(optarg, &end, 10);
				if (errno == ERANGE || end == optarg || *end) {
					fprintf(stderr,
						"%s: illegal number of calls %s\n",
						prog_name, optarg);
					exit(1);
				}
			} else if (flag == &param.http_version) {
				if (sscanf(optarg, "%u.%u", &major, &minor) !=
				    2) {
					fprintf(stderr,
						"%s: illegal version number %s\n",
						prog_name, optarg);
					exit(1);
				}
				param.http_version =
				    (major << 16) | (minor & 0xffff);
			} else if (flag == &param.myaddr) {
				core_add_addresses(optarg);
			} else if (flag == &param.burst_len) {
				errno = 0;
				param.burst_len = strtoul(optarg, &end, 10);
				if (errno == ERANGE || end == optarg || *end
				    || param.burst_len < 1) {
					fprintf(stderr,
						"%s: illegal burst-length %s\n",
						prog_name, optarg);
					exit(1);
				}
			} else if (flag == &param.failure_status) {
				errno = 0;
				param.failure_status =
				    strtoul(optarg, &end, 10);
				if (errno == ERANGE || end == optarg || *end
				    || param.failure_status <= 0) {
					fprintf(stderr,
						"%s: illegal failure status %s\n",
						prog_name, optarg);
					exit(1);
				}
			} else if (flag == &param.num_conns) {
				errno = 0;
				param.num_conns = strtoul(optarg, &end, 10);
				if (errno == ERANGE || end == optarg || *end) {
					fprintf(stderr,
						"%s: illegal number of connections %s\n",
						prog_name, optarg);
					exit(1);
				}
			} else if (flag == &param.max_conns) {
				errno = 0;
				param.max_conns = strtoul(optarg, &end, 10);
				if (errno == ERANGE || end == optarg || *end) {
					fprintf(stderr,
						"%s: illegal max. # of connection %s\n",
						prog_name, optarg);
					exit(1);
				}
			} else if (flag == &param.max_piped) {
				errno = 0;
				param.max_piped = strtoul(optarg, &end, 10);
				if (errno == ERANGE || end == optarg || *end) {
					fprintf(stderr,
						"%s: illegal max. # of piped calls %s\n",
						prog_name, optarg);
					exit(1);
				}
			} else if (flag == &param.port) {
				errno = 0;
				param.port = strtoul(optarg, &end, 10);
				if (errno == ERANGE || end == optarg || *end
				    || (unsigned) param.port > 0xffff) {
					fprintf(stderr,
						"%s: illegal port number %s\n",
						prog_name, optarg);
					exit(1);
				}
			} else if (flag == &param.print_request
				   || flag == &param.print_reply) {
				int             val;

				if (!optarg)
					val = PRINT_HEADER | PRINT_BODY;
				else
					switch (tolower(optarg[0])) {
					case 'h':
						val = PRINT_HEADER;
						break;
					case 'b':
						val = PRINT_BODY;
						break;
					default:
						val =
						    PRINT_HEADER | PRINT_BODY;
						break;
					}
				*(int *) flag = val;
			} else if (flag == &param.rate) {
				errno = 0;
				param.rate.rate_param = strtod(optarg, &end);
				if (errno == ERANGE || end == optarg || *end) {
					fprintf(stderr,
						"%s: illegal request rate %s\n",
						prog_name, optarg);
					exit(1);
				}
				if (param.rate.rate_param <= 0.0)
					param.rate.mean_iat = 0.0;
				else
					param.rate.mean_iat =
					    1 / param.rate.rate_param;
				param.rate.dist = DETERMINISTIC;
			} else if (flag == &param.rate.mean_iat) {	/* --period 
									 */
				param.rate.dist = DETERMINISTIC;
				if (!isdigit(*optarg))
					switch (tolower(*optarg++)) {
					case 'd':
						param.rate.dist =
						    DETERMINISTIC;
						break;
					case 'u':
						param.rate.dist = UNIFORM;
						break;
					case 'e':
						param.rate.dist = EXPONENTIAL;
						break;
					case 'v':
						param.rate.dist = VARIABLE;
						break;
					default:
						fprintf(stderr,
							"%s: illegal interarrival distribution "
							"'%c' in %s\n",
							prog_name, optarg[-1],
							optarg - 1);
						exit(1);
					}

				/*
				 * remaining params depend on selected
				 * distribution: 
				 */
				errno = 0;
				switch (param.rate.dist) {
				case DETERMINISTIC:
				case EXPONENTIAL:
					param.rate.mean_iat =
					    strtod(optarg, &end);
					if (errno == ERANGE || end == optarg
					    || *end
					    || param.rate.mean_iat < 0) {
						fprintf(stderr,
							"%s: illegal mean interarrival "
							"time %s\n", prog_name,
							optarg);
						exit(1);
					}
					break;

				case UNIFORM:
					param.rate.min_iat =
					    strtod(optarg, &end);
					if (errno == ERANGE || end == optarg
					    || param.rate.min_iat < 0) {
						fprintf(stderr,
							"%s: illegal minimum interarrival "
							"time %s\n", prog_name,
							optarg);
						exit(1);
					}
					if (*end != ',') {
						fprintf(stderr,
							"%s: minimum interarrival time not "
							"followed by `,MAX_IAT' (rest: `%s')\n",
							prog_name, end);
						exit(1);
					}
					optarg = end + 1;
					param.rate.max_iat =
					    strtod(optarg, &end);
					if (errno == ERANGE || end == optarg
					    || *end
					    || param.rate.max_iat < 0) {
						fprintf(stderr,
							"%s: illegal request period %s\n",
							prog_name, optarg);
						exit(1);
					}
					param.rate.mean_iat =
					    0.5 * (param.rate.min_iat +
						   param.rate.max_iat);
					break;

				case VARIABLE:
					while (1) {
						if (numRates >= NUM_RATES) {
							fprintf(stderr,
								"%s: too many rates\n",
								prog_name);
							exit(1);
						}

						param.rate.iat[numRates] =
						    strtod(optarg, &end);
						if (errno == ERANGE
						    || end == optarg
						    || param.rate.
						    iat[numRates] < 0) {
							fprintf(stderr,
								"%s: illegal minimum interarrival"
								" time %s\n",
								prog_name,
								optarg);
							exit(1);
						}

						if (*end != ',') {
							fprintf(stderr,
								"%s: interarrival time not "
								"followed by `,duration' (rest: `%s')\n",
								prog_name,
								end);
							exit(1);
						}

						optarg = end + 1;
						param.rate.duration[numRates] =
						    strtod(optarg, &end);
						if (errno == ERANGE
						    || end == optarg
						    || param.rate.
						    duration[numRates] < 0) {
							fprintf(stderr,
								"%s: illegal duration %s\n",
								prog_name,
								optarg);
							exit(1);
						}

						if (numRates == 0)
							param.rate.mean_iat =
							    param.rate.
							    iat[numRates];
						else
							param.rate.mean_iat +=
							    param.rate.
							    iat[numRates];

						numRates++;

						if (*end != ',') {
							param.rate.numRates =
							    numRates;
							break;
						} else
							optarg = end + 1;
					}

					param.rate.mean_iat /= numRates;
					break;

				default:
					fprintf(stderr,
						"%s: internal error parsing %s\n",
						prog_name, optarg);
					exit(1);
					break;
				}
				param.rate.rate_param =
				    ((param.rate.mean_iat <= 0.0)
				     ? 0.0 : (1.0 / param.rate.mean_iat));
			} else if (flag == &param.recv_buffer_size) {
				errno = 0;
				param.recv_buffer_size =
				    strtoul(optarg, &end, 10);
				if (errno == ERANGE || end == optarg || *end
				    || param.port > 0xffff) {
					fprintf(stderr,
						"%s: illegal receive buffer size %s\n",
						prog_name, optarg);
					exit(1);
				}
			} else if (flag == &param.send_buffer_size) {
				errno = 0;
				param.send_buffer_size =
				    strtoul(optarg, &end, 10);
				if (errno == ERANGE || end == optarg || *end
				    || param.port > 0xffff) {
					fprintf(stderr,
						"%s: illegal send buffer size %s\n",
						prog_name, optarg);
					exit(1);
				}
			} else if (flag == &param.client) {
				errno = 0;
				param.client.id = strtoul(optarg, &end, 0);
				if (end == optarg || errno == ERANGE) {
					fprintf(stderr,
						"%s: bad client id (rest: `%s')\n",
						prog_name, optarg);
					exit(1);
				}

				if (*end != '/') {
					fprintf(stderr,
						"%s: client id not followed by `/' (rest: `%s')\n",
						prog_name, end);
					exit(1);
				}
				optarg = end + 1;

				param.client.num_clients =
				    strtoul(optarg, &end, 0);
				if (end == optarg || errno == ERANGE
				    || param.client.id >=
				    param.client.num_clients) {
					fprintf(stderr,
						"%s: bad number of clients (rest: `%s')\n",
						prog_name, optarg);
					exit(1);
				}
			} else if (flag == &param.server)
				param.server = optarg;
			else if (flag == &param.server_name)
				param.server_name = optarg;
			else if (flag == &param.servers)
				param.servers = optarg;
#ifdef HAVE_SSL
			else if (flag == &param.ssl_cipher_list)
				param.ssl_cipher_list = optarg;
                        else if (flag == &param.ssl_cert)
                                param.ssl_cert = optarg;
                        else if (flag == &param.ssl_key)
                                param.ssl_key = optarg;
                        else if (flag == &param.ssl_verify)
                            {
                                if (!optarg)
                                    param.ssl_verify = 1;
                                else
                                    switch (tolower (optarg[0]))
                                    {
                                    case 'y': param.ssl_verify = 1; break;
                                    case 'n': param.ssl_verify = 0; break;
                                    default:  param.ssl_verify = 0; break;
                                    }
                            }
                        else if (flag == &param.ssl_ca_file)
                                param.ssl_ca_file = optarg;
                        else if (flag == &param.ssl_ca_path)
                                param.ssl_ca_path = optarg;
                        else if (flag == &param.ssl_protocol)
                        {
			    param.use_ssl = 1;

                            if (strcasecmp (optarg, "auto") == 0)
                                param.ssl_protocol = 0;
#ifndef OPENSSL_NO_SSL2
                            else if (strcasecmp (optarg, "SSLv2") == 0)
                                param.ssl_protocol = 2;
#endif
#ifndef OPENSSL_NO_SSL3
                            else if (strcasecmp (optarg, "SSLv3") == 0)
                                param.ssl_protocol = 3;
#endif
                            else if (strcasecmp (optarg, "TLSv1.0") == 0 || strcasecmp (optarg, "TLSv1_0") == 0 || strcasecmp (optarg, "TLSv1") == 0)
                                param.ssl_protocol = 4;
			    else if (strcasecmp (optarg, "TLSv1.1") == 0 || strcasecmp (optarg, "TLSv1_1") == 0)
                                param.ssl_protocol = 5;
			    else if (strcasecmp (optarg, "TLSv1.2") == 0 || strcasecmp (optarg, "TLSv1_2") == 0)
                                param.ssl_protocol = 6;
#ifdef TLS1_3_VERSION
                            else if (strcasecmp (optarg, "TLSv1.3") == 0 || strcasecmp (optarg, "TLSv1_3") == 0)
                                param.ssl_protocol = 7;
#endif
                            else
                            {
                                fprintf (stderr, "%s: illegal SSL protocol %s\n",
                                        prog_name, optarg);
                                exit (1);
                            }
                        }
						else if (flag == &param.tls_server_name)
						{
							if (param.ssl_protocol >= 4)
							{
								param.tls_server_name = optarg;
							}
							else
                            {
                                fprintf (stderr, "%s: Error setting the SNI (Server Name Indication) server name to %s. The --tls-server-name option can only be used if --ssl-protocol-version is set to TLSv1.0 and above.\n",
                                        prog_name, optarg);
                                exit (1);
                            }
						}
#endif
			else if (flag == &param.uri)
				param.uri = optarg;
			else if (flag == &param.think_timeout) {
				errno = 0;
				param.think_timeout = strtod(optarg, &end);
				if (errno == ERANGE || end == optarg || *end) {
					fprintf(stderr,
						"%s: illegal think timeout value %s\n",
						prog_name, optarg);
					exit(1);
				}
			} else if (flag == &param.timeout) {
				errno = 0;
				param.timeout = strtod(optarg, &end);
				if (errno == ERANGE || end == optarg || *end) {
					fprintf(stderr,
						"%s: illegal connect timeout %s\n",
						prog_name, optarg);
					exit(1);
				}
			} else if (flag == &param.runtime) {
				errno = 0;
				param.runtime = strtod(optarg, &end);
				if (errno == ERANGE || end == optarg || *end ||
				    param.runtime <= 0.0) {
					fprintf(stderr,
						"%s: illegal runtime value %s\n",
						prog_name, optarg);
					exit(1);
				}
			} else if (flag == &param.wlog) {
				gen[1] = &uri_wlog;	/* XXX fix
							 * me---somehow */

				param.wlog.do_loop = (*optarg == 'y')
				    || (*optarg == 'Y');
				param.wlog.file = optarg + 2;
			} else if (flag == &param.wsess) {
				num_gen = 2;	/* XXX fix me---somehow */
				gen[0] = &wsess;

				stat[num_stats++] = &session_stat;

				errno = 0;
				name = "bad number of sessions (1st param)";
				param.wsess.num_sessions =
				    strtoul(optarg, &end, 0);
				if (end == optarg || errno == ERANGE)
					goto bad_wsess_param;
				optarg = end + 1;

				name =
				    "bad number of calls per session (2nd param)";
				if (*end != ',')
					goto bad_wsess_param;
				optarg = end + 1;

				param.wsess.num_calls =
				    strtoul(optarg, &end, 0);
				if (end == optarg || errno == ERANGE)
					goto bad_wsess_param;

				name = "bad user think time (3rd param)";
				if (*end != ',')
					goto bad_wsess_param;
				optarg = end + 1;

				param.wsess.think_time = strtod(optarg, &end);
				if (end == optarg || errno == ERANGE
				    || param.wsess.think_time < 0.0)
					goto bad_wsess_param;

				name = "extraneous parameter";
				if (*end) {
				      bad_wsess_param:
					fprintf(stderr,
						"%s: %s in --wsess arg (rest: `%s')",
						prog_name, name, end);
					if (errno)
						fprintf(stderr, ": %s",
							strerror(errno));
					fputc('\n', stderr);
					exit(1);
				}
				session_workload = 1;
			} else if (flag == &param.wsesspage) {
				num_gen = 2;	/* XXX fix me---somehow */
				gen[0] = &wsesspage;

				stat[num_stats++] = &session_stat;

				errno = 0;
				name = "bad number of sessions (1st param)";
				param.wsesspage.num_sessions =
				    strtoul(optarg, &end, 0);
				if (end == optarg || errno == ERANGE)
					goto bad_wsesspage_param;
				optarg = end + 1;

				name =
				    "bad number of user requests per session (2nd param)";
				if (*end != ',')
					goto bad_wsesspage_param;
				optarg = end + 1;

				param.wsesspage.num_reqs =
				    strtoul(optarg, &end, 0);
				if (end == optarg || errno == ERANGE)
					goto bad_wsesspage_param;

				name = "bad user think time (3rd param)";
				if (*end != ',')
					goto bad_wsesspage_param;
				optarg = end + 1;

				param.wsesspage.think_time =
				    strtod(optarg, &end);
				if (end == optarg || errno == ERANGE
				    || param.wsesspage.think_time < 0.0)
					goto bad_wsesspage_param;

				name = "extraneous parameter";
				if (*end) {
				      bad_wsesspage_param:
					fprintf(stderr,
						"%s: %s in --wsesspage arg (rest: `%s')",
						prog_name, name, end);
					if (errno)
						fprintf(stderr, ": %s",
							strerror(errno));
					fputc('\n', stderr);
					exit(1);
				}
				session_workload = 1;
			} else if (flag == &param.wsesslog) {
				num_gen = 1;	/* XXX fix me---somehow */
				gen[0] = &wsesslog;

				stat[num_stats++] = &session_stat;

				errno = 0;
				name = "bad number of sessions (1st param)";
				param.wsesslog.num_sessions =
				    strtoul(optarg, &end, 0);
				if (end == optarg || errno == ERANGE)
					goto bad_wsesslog_param;
				optarg = end + 1;

				name = "bad user think time (2nd param)";
				if (*end != ',')
					goto bad_wsesslog_param;
				optarg = end + 1;

				param.wsesslog.think_time =
				    strtod(optarg, &end);
				if (end == optarg || errno == ERANGE
				    || param.wsesslog.think_time < 0.0)
					goto bad_wsesslog_param;

				name = "bad session filename (3rd param)";
				if (*end != ',')
					goto bad_wsesslog_param;
				optarg = end + 1;

				/*
				 * simulate parsing of string 
				 */
				param.wsesslog.file = optarg;
				if ((end = strchr(optarg, ',')) == NULL)
					/*
					 * must be last param, position end at 
					 * final \0 
					 */
					end = optarg + strlen(optarg);
				else
					/*
					 * terminate end of string 
					 */
					*end++ = '\0';
				optarg = end;

				name = "extraneous parameter";
				if (*end) {
				      bad_wsesslog_param:
					fprintf(stderr,
						"%s: %s in --wsesslog arg (rest: `%s')",
						prog_name, name, end);
					if (errno)
						fprintf(stderr, ": %s",
							strerror(errno));
					fputc('\n', stderr);
					exit(1);
				}
				session_workload = 1;
			} else if (flag == &param.wset) {
				gen[1] = &uri_wset;	/* XXX fix
							 * me---somehow */

				errno = 0;
				name = "bad working set size (1st parameter)";
				param.wset.num_files =
				    strtoul(optarg, &end, 0);
				if (end == optarg || errno == ERANGE)
					goto bad_wset_param;

				name = "bad target miss rate (2nd parameter)";
				if (*end != ',')
					goto bad_wset_param;
				optarg = end + 1;

				param.wset.target_miss_rate =
				    strtod(optarg, &end);
				if (end == optarg || errno == ERANGE
				    || param.wset.target_miss_rate < 0.0
				    || param.wset.target_miss_rate > 1.0)
					goto bad_wset_param;

				name = "extraneous parameter";
				if (*end) {
				      bad_wset_param:
					fprintf(stderr,
						"%s: %s in --wset arg (rest: `%s')",
						prog_name, name, optarg);
					if (errno)
						fprintf(stderr, ": %s",
							strerror(errno));
					fputc('\n', stderr);
					exit(1);
				}
			}
			break;

		case 'd':
#ifdef DEBUG
			errno = 0;
			debug_level = strtoul(optarg, &end, 10);
			if (errno == ERANGE || end == optarg || *end) {
				fprintf(stderr, "%s: illegal debug level %s\n",
					prog_name, optarg);
				exit(1);
			}
#else
			fprintf(stderr,
				"%s: sorry, need to recompile with -DDEBUG on...\n",
				prog_name);
#endif
			break;

		case 'v':
			++verbose;
			break;

		case 'V':
			printf("%s: httperf-" VERSION " compiled " __DATE__
			       " with"
#ifndef DEBUG
			       "out"
#endif
			       " DEBUG with"
#ifndef TIME_SYSCALLS
			       "out"
#endif
			       " TIME_SYSCALLS.\n", prog_name);
			exit(0);

		case 'n':
			++periodic_stats;
			break;

		case 'h':
			usage();
			exit(0);

		case ':':
			fprintf(stderr,
				"%s: parameter missing for option %s\n",
				prog_name, longopts[longindex].name);
			exit(1);

		case '?':
			/*
			 * Invalid or ambiguous option name or extraneous
			 * parameter. getopt_long () already issued an
			 * explanation to the user, so all we do is call it
			 * quites.  
			 */
			exit(1);

		default:
			fprintf(stderr,
				"%s: getopt_long: unexpected value (%d)\n",
				prog_name, ch);
			exit(1);
		}
	}

	if (param.server != NULL && param.servers != NULL) {
		fprintf(stderr,
			"%s: --server S or --servers file\n",
			prog_name);
		exit(-1);
	}

	if (param.server == NULL && param.servers == NULL)
		param.server = "localhost";

#ifdef HAVE_SSL
	if (param.use_ssl) {
		char            buf[1024];

		if (param.port < 0)
			param.port = 443;

                SSL_library_init ();
		SSL_load_error_strings ();
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
                SSLeay_add_all_algorithms ();
#endif

		switch (param.ssl_protocol)
                {
                    /* 0/auto for highest available */
                    case 0: 
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
                    ssl_ctx = SSL_CTX_new (TLS_client_method ()); break;
#else
                    ssl_ctx = SSL_CTX_new (SSLv23_client_method ()); break;
#endif

#ifndef OPENSSL_NO_SSL2
                    /* 2/SSLv2 */
                    case 2: 
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
                    ssl_ctx = SSL_CTX_new (TLS_client_method ());
#if (OPENSSL_VERSION_NUMBER >= 0x10101000L)
                    SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2 | SSL_OP_NO_TLSv1_3); break;
#else
		    SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2); break;
#endif
#else
                    ssl_ctx = SSL_CTX_new (SSLv2_client_method ()); break;
#endif
#endif

#ifndef OPENSSL_NO_SSL3
                    /* 3/SSLv3 */
                    case 3: 
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
                    ssl_ctx = SSL_CTX_new (TLS_client_method ());
		    SSL_CTX_set_min_proto_version(ssl_ctx, SSL3_VERSION);
		    SSL_CTX_set_max_proto_version(ssl_ctx, SSL3_VERSION);
		    break;
#else
                    ssl_ctx = SSL_CTX_new (SSLv3_client_method ()); break;
#endif
#endif
                    /* 4/TLSv1.0 */
                    case 4: 
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
                    ssl_ctx = SSL_CTX_new (TLS_client_method ()); 
                    SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_VERSION);
		    SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_VERSION);
		    break;
#else
                    ssl_ctx = SSL_CTX_new (TLSv1_client_method ());
		    SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2); break;
#endif

                    /* 5/TLSv1.1 */
                    case 5:
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
                    ssl_ctx = SSL_CTX_new (TLS_client_method ());
                    SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_1_VERSION);
		    SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_1_VERSION);
		    break;
#else
                    ssl_ctx = SSL_CTX_new (TLSv1_client_method ());
		    SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_2); break;
#endif

		    /* 6/TLSv1.2 */
                    case 6:
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
                    ssl_ctx = SSL_CTX_new (TLS_client_method ());
                    SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_2_VERSION);
		    SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_2_VERSION);
		    break;
#else
                    ssl_ctx = SSL_CTX_new (TLSv1_client_method ());
		    SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); break;
#endif

#ifdef TLS1_3_VERSION
                    /* 7/TLSv1.3 */
		    case 7:
                    ssl_ctx = SSL_CTX_new (TLS_client_method ());
                    SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
		    SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
		    break;
#endif

                }
      
		if (!ssl_ctx) {
			ERR_print_errors_fp(stderr);
			exit(-1);
		}

		memset(buf, 0, sizeof(buf));
		RAND_seed(buf, sizeof(buf));
                
                /* set server certificate verification */
                if (param.ssl_verify == 1)
                    SSL_CTX_set_verify (ssl_ctx, SSL_VERIFY_PEER, NULL);
                else
                    SSL_CTX_set_verify (ssl_ctx, SSL_VERIFY_NONE, NULL);

                /* set default certificate authority verification path */
                SSL_CTX_set_default_verify_paths (ssl_ctx);

                /* load extra certificate authority files and paths */
                if (param.ssl_ca_file || param.ssl_ca_path)
                {
                    int ssl_err = SSL_CTX_load_verify_locations
                    (ssl_ctx, param.ssl_ca_file, param.ssl_ca_path);
                    if (DBG > 2)
                        fprintf (stderr, "SSL_CTX_load_verify_locations returned %d\n",
                            ssl_err);
                }

                /* if using client SSL authentication, load the certificate file */
                if (param.ssl_cert)
                {
                    int ssl_err = SSL_CTX_use_certificate_file
                        (ssl_ctx, param.ssl_cert, SSL_FILETYPE_PEM);
                    if (DBG > 2)
                        fprintf (stderr, "SSL_CTX_use_certificate_file returned %d\n",
                            ssl_err);
                }

                /* also load the client key */
                if (param.ssl_key)
                {
                    int ssl_err = SSL_CTX_use_PrivateKey_file
                        (ssl_ctx, param.ssl_key, SSL_FILETYPE_PEM);
                    if (DBG > 2)
                        fprintf (stderr, "SSL_CTX_use_PrivateKey_file returned %d\n",
                            ssl_err);
                }

                /* check client certificate and key consistency */
                if (param.ssl_cert && param.ssl_key)
                {
                    int ssl_err = SSL_CTX_check_private_key (ssl_ctx);
                    if (DBG > 2)
                        fprintf (stderr, "SSL_CTX_check_private_key returned %d\n",
                            ssl_err);
                    if (!ssl_err)
                    {
                        fprintf (stderr,
                            "SSL certificate and key failed consistency check\n");
                        exit (1);
                    }
                }      
	}
#endif
	if (param.port < 0)
		param.port = 80;

	if (param.print_reply || param.print_request)
		stat[num_stats++] = &stats_print_reply;

	if (param.session_cookies) {
		if (!session_workload) {
			fprintf(stderr,
				"%s: --session-cookie requires session-oriented "
				"workload (e.g., --wsess)\n", prog_name);
			exit(-1);
		}
		gen[num_gen++] = &sess_cookie;
	}

	if (param.additional_header || param.additional_header_file ||
	    param.method)
		gen[num_gen++] = &misc;

	/*
	 * echo command invocation for logging purposes: 
	 */
	printf("%s", prog_name);
	if (verbose)
		printf(" --verbose");
	switch (param.print_reply) {
	case 0:
		break;
	case PRINT_HEADER:
		printf(" --print-reply=header");
		break;
	case PRINT_BODY:
		printf(" --print-reply=body");
		break;
	default:
		printf(" --print-reply");
		break;
	}
	switch (param.print_request) {
	case 0:
		break;
	case PRINT_HEADER:
		printf(" --print-request=header");
		break;
	case PRINT_BODY:
		printf(" --print-request=body");
		break;
	default:
		printf(" --print-request");
		break;
	}
	if (param.hog)
		printf(" --hog");
	if (param.close_with_reset)
		printf(" --close-with-reset");
	if (param.think_timeout > 0)
		printf(" --think-timeout=%g", param.think_timeout);
	if (param.timeout > 0)
		printf(" --timeout=%g", param.timeout);
	if (param.runtime > 0)
		printf(" --runtime=%g", param.runtime);
	printf(" --client=%u/%u", param.client.id, param.client.num_clients);
	if (param.server)
		printf(" --server=%s", param.server);
	if (param.server_name)
		printf(" --server_name=%s", param.server_name);
	if (param.servers)
		printf(" --servers=%s", param.servers);
	if (param.port)
		printf(" --port=%d", param.port);
	if (param.uri)
		printf(" --uri=%s", param.uri);
	if (param.failure_status)
		printf(" --failure-status=%u", param.failure_status);
	if (param.http_version != 0x10001)
		printf(" --http-version=%u.%u", param.http_version >> 16,
		       param.http_version & 0xffff);
	if (param.max_conns)
		printf(" --max-connections=%lu", param.max_conns);
	if (param.max_piped)
		printf(" --max-piped-calls=%lu", param.max_piped);
	if (param.rate.rate_param > 0.0) {
		switch (param.rate.dist) {
		case DETERMINISTIC:
			/*
			 * for backwards compatibility, continue to use
			 * --rate: 
			 */
			printf(" --rate=%g", param.rate.rate_param);
			break;

		case UNIFORM:
			printf(" --period=u%g,%g",
			       param.rate.min_iat, param.rate.max_iat);
			break;

		case EXPONENTIAL:
			printf(" --period=e%g", param.rate.mean_iat);
			break;

		case VARIABLE:
			{
				int             m;
				printf(" --period=v");
				for (m = 0; m < param.rate.numRates; m++) {
					if (m != 0)
						printf(",");
					printf("%g,%g", param.rate.iat[m],
					       param.rate.duration[m]);
				}
			}
			break;

		default:
			printf("--period=??");
			break;
		}
	}
	printf(" --send-buffer=%lu", param.send_buffer_size);
	if (param.retry_on_failure)
		printf(" --retry-on-failure");
	printf(" --recv-buffer=%lu", param.recv_buffer_size);
	if (param.session_cookies)
		printf(" --session-cookies");
#ifdef HAVE_SSL
	if (param.use_ssl)
		printf(" --ssl");
	if (param.ssl_cipher_list)
		printf(" --ssl-ciphers=%s", param.ssl_cipher_list);
	if (param.tls_server_name)
		printf(" --tls-server-name=%s", param.tls_server_name);
	if (!param.ssl_reuse)
		printf(" --ssl-no-reuse");
        if (param.ssl_cert) printf (" --ssl-cert=%s", param.ssl_cert);
        if (param.ssl_key) printf (" --ssl-key=%s", param.ssl_key);
        if (param.ssl_ca_file) printf (" --ssl-ca-file=%s", param.ssl_ca_file);
        if (param.ssl_ca_path) printf (" --ssl-ca-path=%s", param.ssl_ca_path);
        if (param.ssl_verify) printf (" --ssl-verify");
        switch (param.ssl_protocol)
        {
            case 0: printf (" --ssl-protocol=auto");  break;
#ifndef OPENSSL_NO_SSL2
            case 2: printf (" --ssl-protocol=SSLv2"); break;
#endif
#ifndef OPENSSL_NO_SSL3
            case 3: printf (" --ssl-protocol=SSLv3"); break;
#endif
            case 4: printf (" --ssl-protocol=TLSv1.0"); break;
	    case 5: printf (" --ssl-protocol=TLSv1.1"); break;
	    case 6: printf (" --ssl-protocol=TLSv1.2"); break;
#ifdef TLS1_3_VERSION
            case 7: printf (" --ssl-protocol=TLSv1.3"); break;
#endif
        }
#endif
	if (param.additional_header)
		printf(" --add-header='%s'", param.additional_header);
	if (param.additional_header_file)
		printf(" --add-header-file='%s'", param.additional_header_file);
	if (param.method)
		printf(" --method=%s", param.method);
	if (param.use_timer_cache)
		printf(" --use-timer-cache");
	if (param.wsesslog.num_sessions) {
		/*
		 * This overrides any --wsess, --num-conns, --num-calls,
		 * --burst-length and any uri generator 
		 */
		printf(" --wsesslog=%u,%.3f,%s", param.wsesslog.num_sessions,
		       param.wsesslog.think_time, param.wsesslog.file);
	} else if (param.wsesspage.num_sessions) {
		printf(" --wsesspage=%u,%u,%.3f", param.wsesspage.num_sessions,
		       param.wsesspage.num_reqs, param.wsesspage.think_time);
	} else {
		if (param.wsess.num_sessions)
			printf(" --wsess=%u,%u,%.3f", param.wsess.num_sessions,
			       param.wsess.num_calls, param.wsess.think_time);
		else {
			if (param.num_conns)
				printf(" --num-conns=%lu", param.num_conns);
			if (param.num_calls)
				printf(" --num-calls=%lu", param.num_calls);
		}
		if (param.burst_len != 1)
			printf(" --burst-length=%lu", param.burst_len);
		if (param.wset.num_files)
			printf(" --wset=%u,%.3f",
			       param.wset.num_files,
			       param.wset.target_miss_rate);
	}
	if (periodic_stats)
		printf(" --periodic-stats");
	printf("\n");

	if (timer_init() == false) {
		fprintf(stderr,
			"%s: timer_init(): failed initialization (%d)\n",
			prog_name, __LINE__);
		exit(1);
	}
	core_init();

	signal(SIGINT, (void (*)()) core_exit);

	for (i = 0; i < num_stats; ++i)
		(*stat[i]->init) ();
	for (i = 0; i < num_gen; ++i)
		(*gen[i]->init) ();

	/*
	 * Update `now'.  This is to keep things accurate even when some of
	 * the initialization routines take a long time to execute.  
	 */
	timer_now_forced();

	/*
	 * ensure that clients sample rates at different times: 
	 */
	t = (param.client.id + 1.0) * RATE_INTERVAL / param.client.num_clients;
	arg.l = 0;
	timer_schedule(perf_sample, arg, t);
	perf_sample_start = timer_now();

	for (i = 0; i < num_gen; ++i)
		(*gen[i]->start) ();
	for (i = 0; i < num_stats; ++i)
		(*stat[i]->start) ();

	getrusage(RUSAGE_SELF, &test_rusage_start);
	test_time_start = timer_now();
	core_loop();
	test_time_stop = timer_now();
	getrusage(RUSAGE_SELF, &test_rusage_stop);

	for (i = 0; i < num_stats; ++i)
		(*stat[i]->stop) ();
	for (i = 0; i < num_gen; ++i)
		(*gen[i]->stop) ();
	for (i = 0; i < num_stats; ++i)
		(*stat[i]->dump) ();

	timer_free_all();

	return 0;
}
0707010000002D000081ED0000000000000000000000015FCD000200001A0B000000000000000000000000000000000000002900000000httperf-0.9.0+git.20201206/src/httperf.h/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

#ifndef httperf_h
#define httperf_h

#include "config.h"

#define NELEMS(a)	((sizeof (a)) / sizeof ((a)[0]))
#define TV_TO_SEC(tv)	((tv).tv_sec + 1e-6*(tv).tv_usec)

#define NUM_RATES 16

typedef enum Dist_Type
  {
    DETERMINISTIC,	/* also called fixed-rate */
    UNIFORM,		/* over interval [min_iat,max_iat) */
    VARIABLE,           /* allows varying input load */
    EXPONENTIAL		/* with mean mean_iat */
  }
Dist_Type;

typedef struct Load_Generator
  {
    const char *name;
    void (*init) (void);
    void (*start) (void);
    void (*stop) (void);
  }
Load_Generator;

typedef struct Stat_Collector
  {
    const char *name;
    /* START and STOP are timing sensitive, so they should be as short
       as possible.  More expensive stuff can be done during INIT and
       DUMP.  */
    void (*init) (void);
    void (*start) (void);
    void (*stop) (void);
    void (*dump) (void);
  }
Stat_Collector;

typedef struct Rate_Info
  {
    Dist_Type dist;		/* interarrival distribution */
    double rate_param;		/* 0 if mean_iat==0, else 1/mean_iat */
    Time mean_iat;		/* mean interarrival time */
    Time min_iat;		/* min interarrival time (for UNIFORM) */
    Time max_iat;	        /* max interarrival time (for UNIFORM) */
    int numRates;               /* number of rates we want to use */
    Time iat[NUM_RATES];
    Time duration[NUM_RATES];
  }
Rate_Info;

#define PRINT_HEADER	(1 << 0)
#define PRINT_BODY	(1 << 1)

typedef struct Cmdline_Params
  {
    int http_version;	/* (default) HTTP protocol version */
    const char *server;	/* (default) hostname */
    const char *server_name; /* fully qualified server name */
    const char *servers;
    int port;		/* (default) server port */
    const char *uri;	/* (default) uri */
    const char *myaddr;
    Rate_Info rate;
    Time timeout;	/* watchdog timeout */
    Time think_timeout;	/* timeout for server think time */
    Time runtime;	/* how long to run the test */
    u_long num_conns;	/* # of connections to generate */
    u_long num_calls;	/* # of calls to generate per connection */
    u_long burst_len;	/* # of calls to burst back-to-back */
    u_long max_piped;	/* max # of piped calls per connection */
    u_long max_conns;	/* max # of connections per session */
    int hog;		/* client may hog as much resources as possible */
    u_long send_buffer_size;
    u_long recv_buffer_size;
    int failure_status;	/* status code that should be considered failure */
    int retry_on_failure; /* when a call fails, should we retry? */
    int close_with_reset; /* close connections with TCP RESET? */
    int print_request;	/* bit 0: print req headers, bit 1: print req body */
    int print_reply;	/* bit 0: print repl headers, bit 1: print repl body */
    int session_cookies; /* handle set-cookies? (at the session level) */
    int no_host_hdr;	/* don't send Host: header in request */
#ifdef HAVE_SSL
    int use_ssl;	/* connect via SSL */
    int ssl_reuse;	/* reuse SSL Session ID */
    int ssl_verify;     /* whether to verify the server certificate */
    int ssl_protocol;   /* which SSL protocol to use */
    const char *tls_server_name; /* TLS SNI (server name indication) */
    const char *ssl_cipher_list; /* client's list of SSL cipher suites */
    const char *ssl_cert; /* client certificate file name */
    const char *ssl_key; /* client key file name */
    const char *ssl_ca_file; /* certificate authority file */
    const char *ssl_ca_path; /* certificate authority path */
#endif
    int use_timer_cache;
    const char *additional_header;	/* additional request header(s) */
    const char *additional_header_file;
    const char *method;	/* default call method */
    struct
      {
	u_int id;
	u_int num_clients;
      }
    client;
    struct
      {
	char *file;	/* name of the file where entries are */
	char do_loop;	/* boolean indicating if we want to loop on entries */
      }
    wlog;
    struct
      {
	u_int num_sessions;	/* # of sessions */
	u_int num_calls;	/* # of calls per session */
	Time think_time;	/* user think time between calls */
      }
    wsess;
    struct
      {
	u_int num_sessions;	/* # of sessions */
	u_int num_reqs;		/* # of user requests per session */
	Time think_time;	/* user think time between requests */
      }
    wsesspage;
    struct
      {
	u_int num_sessions;	/* # of user-sessions */
	Time think_time;	/* user think time between calls */
	char *file;		/* name of the file where session defs are */
      }
    wsesslog;
    struct
      {
	u_int num_files;
	double target_miss_rate;
      }
    wset;
  }
Cmdline_Params;

extern const char *prog_name;
extern int verbose;
extern int periodic_stats;
extern Cmdline_Params param;
extern Time test_time_start;
extern Time test_time_stop;
extern struct rusage test_rusage_start;
extern struct rusage test_rusage_stop;

#ifdef HAVE_SSL
# include <openssl/ssl.h>
  extern SSL_CTX *ssl_ctx;
#endif

#ifdef DEBUG
  extern int debug_level;
# define DBG debug_level
#else
# define DBG 0
#endif

extern void panic (const char *msg, ...);
extern void no_op (void);

#endif /* httperf_h */
0707010000002E000081ED0000000000000000000000015FCD000200001A2D000000000000000000000000000000000000002A00000000httperf-0.9.0+git.20201206/src/idleconn.c/*
 * Copyright (C) 2007, 2008 Ted Bullock <tbullock@comlore.com>
 * 
 * This file is part of httperf, a web server performance measurment tool.
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 * 
 * In addition, as a special exception, the copyright holders give permission
 * to link the code of this work with the OpenSSL project's "OpenSSL" library
 * (or with modified versions of it that use the same license as the "OpenSSL" 
 * library), 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 this file, you may extend this
 * exception to your version of the file, but you are not obligated to do so.
 * If you do not wish to do so, delete this exception statement from your
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>		/* For strrchr() */
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <inttypes.h>
#include <sys/time.h>

#include <generic_types.h>
#include <event.h>
#include <evdns.h>

#include <list.h>

/*
 * Events allocated onto the heap
 */
static struct List *active_events = NULL;

static const char *prog_name = NULL;
static unsigned long num_conn = 0, num_closed = 0;
static struct timeval start_time;
static struct sockaddr_in server_addr;

static char    *server = NULL;
static int      desired = 0;	/* Number of desired connections */
static int      port = 0;

/*
 * Frees the linked list of active event structures
 */
static void
cleanup()
{
	if (active_events != NULL) {
		while (!is_list_empty(active_events)) {
			Any_Type        a = list_pop(active_events);
			struct event   *evsock = (struct event *) a.vp;
			event_del(evsock);
			free(a.vp);
		}
		list_free(active_events);
	}
}

/*
 * Signal handler callback to be executed by event_dispatch upon receipt of
 * SIGINT (usually Control-C
 */
void
sigint_exit(int fd, short event, void *arg)
{
	struct event   *signal_int = arg;
	struct timeval  stop_time;
	double          delta_t = 0;

	gettimeofday(&stop_time, NULL);

	delta_t = ((stop_time.tv_sec - start_time.tv_sec)
		   + 1e-6 * (stop_time.tv_usec - start_time.tv_usec));

	printf("%s: Total conns created = %lu; close() rate = %g conn/sec\n",
	       prog_name, num_conn, num_closed / delta_t);

#ifdef DEBUG
	printf("%s: caught SIGINT... Exiting.\n", __func__);
#endif /* DEBUG */

	evdns_shutdown(0);
	event_del(signal_int);

	cleanup();
}

/*
 * Connection disconnect handler.  Once a connection is dropped by the remote
 * host, this function is executed and a new connection is established.
 *
 * Note, that this re-uses the event structure originally allocated in 
 * dns_lookup_callback
 */
void
reconnect(int sd, short event, void *arg)
{
	struct sockaddr_in sin = server_addr;
	struct event   *evsock = (struct event *) arg;

	close(sd);
	num_closed++;

	sd = socket(AF_INET, SOCK_STREAM, 0);

	if (sd == -1) {
		perror("socket");
		exit(EXIT_FAILURE);
	}

	if (connect(sd, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
		perror("connect");
		cleanup();
		exit(EXIT_FAILURE);
	}

	event_set(evsock, sd, EV_READ, reconnect, evsock);
	event_add(evsock, NULL);
	num_conn++;

}

/*
 * For the explanation of these parameters, please refer to the libevent evdns
 * callback API
 *
 * Upon receipt of a valid dns lookup result, attempts to open `desired`
 * connections and allocates memory for the associated event structures
 */
void
dns_lookup_callback(int result, char type, int count, int ttl, void *addresses,
		    void *arg)
{
	uint32_t        i;
	uint8_t         oct[4];
	struct in_addr *address_list = (struct in_addr *) addresses;

	if (result != DNS_ERR_NONE) {
		printf("DNS Lookup: result(%s)\n",
		       evdns_err_to_string(result));
		exit(EXIT_FAILURE);
	}

	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(port);
	server_addr.sin_addr = address_list[0];

	/*
	 * Echo the resolved address 
	 */
	printf("Resolved %s\n\t(%s)\n", server, inet_ntoa(address_list[0]));

	/*
	 * Open the number of `desired` connections 
	 */
	for (i = 0; i < desired; i++) {
		struct sockaddr_in sin;
		int             sd = socket(AF_INET, SOCK_STREAM, 0);

		struct event   *evsock = NULL;

		if (sd == -1) {
			perror("socket");
			continue;
		}

		sin = server_addr;

		if (connect(sd, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
			perror("connect");
			cleanup();
			exit(EXIT_FAILURE);
		}

		evsock = (struct event *) malloc(sizeof(struct event));

		if (evsock == NULL) {
			cleanup();
			exit(EXIT_FAILURE);
		}

		event_set(evsock, sd, EV_READ, reconnect, evsock);
		list_push(active_events, (Any_Type) (void *) evsock);
		num_conn++;

		event_add(evsock, NULL);

	}
}

int
main(int argc, char *argv[])
{
	struct event    signal_int;

	active_events = list_create();
	if (active_events == NULL)
		goto init_failure;

	event_init();
	evdns_init();

	/*
	 * Initalize one event 
	 */
	event_set(&signal_int, SIGINT, EV_SIGNAL, sigint_exit, &signal_int);

	event_add(&signal_int, NULL);

	prog_name = strrchr(argv[0], '/');
	if (prog_name)
		++prog_name;
	else
		prog_name = argv[0];

	if (argc != 4) {
		fprintf(stderr, "Usage: `%s server port numidle'\n",
			prog_name);
		goto init_failure;
	}

	server = argv[1];
	port = atoi(argv[2]);
	desired = atoi(argv[3]);

	printf("%s: Using libevent-%s for %s event notification system.\n"
	       "Control-c to exit\n\n", prog_name, event_get_version(),
	       event_get_method());

	gettimeofday(&start_time, NULL);

	evdns_resolve_ipv4(server, 0, dns_lookup_callback, NULL);

	event_dispatch();

	/*
	 * Event loop will only exit upon receiving SIGINT.  Make sure we pass
	 * this on to the parent process 
	 */
	if (signal(SIGINT, SIG_DFL) < 0)
		perror("signal");
	else
		kill(getpid(), SIGINT);

      init_failure:
	cleanup();
	exit(EXIT_FAILURE);

	/*
	 * Should never reach here 
	 */
	return 0;
}
0707010000002F000041ED0000000000000000000000025FCD000200000000000000000000000000000000000000000000002300000000httperf-0.9.0+git.20201206/src/lib07070100000030000081ED0000000000000000000000015FCD000200000100000000000000000000000000000000000000002F00000000httperf-0.9.0+git.20201206/src/lib/Makefile.am# what flags you want to pass to the C compiler & linker
# AM_CFLAGS = -I$(srcdir)/..
AM_CFLAGS = 
AM_LDFLAGS =

noinst_LIBRARIES = libutil.a
libutil_a_SOURCES = getopt.c getopt.h ssl_writev.c generic_types.h \
	queue.c queue.h heap.c heap.h list.c list.h
07070100000031000081ED0000000000000000000000015FCD000200000101000000000000000000000000000000000000002A00000000httperf-0.9.0+git.20201206/src/lib/READMEThis directory contains library-replacement functions that are needed
on some platforms.  This code is copyright by the Free Software
Foundation and a copy of the license under which it is distributed can
be found in the file COPYING.LIB in this directory.
07070100000032000081A40000000000000000000000015FCD00020000076E000000000000000000000000000000000000003300000000httperf-0.9.0+git.20201206/src/lib/generic_types.h/*
 * Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
 * 
 * This file is part of httperf, a web server performance measurment tool.
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 * 
 * In addition, as a special exception, the copyright holders give permission
 * to link the code of this work with the OpenSSL project's "OpenSSL" library
 * (or with modified versions of it that use the same license as the "OpenSSL" 
 * library), 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 this file, you may extend this
 * exception to your version of the file, but you are not obligated to do so.
 * If you do not wish to do so, delete this exception statement from your
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 
 */

#ifndef generic_types_h
#define generic_types_h

#include <sys/types.h>
#include <stdbool.h>

typedef union {
	char            c;
	int             i;
	long            l;
	u_char          uc;
	u_int           ui;
	u_long          ul;
	u_wide          uw;
	float           f;
	double          d;
	void           *vp;
	const void     *cvp;
} Any_Type;

typedef double  Time;

#endif /* generic_types_h */
07070100000033000081ED0000000000000000000000015FCD00020000387B000000000000000000000000000000000000002C00000000httperf-0.9.0+git.20201206/src/lib/getopt.c/*
 * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Sponsored in part by the Defense Advanced Research Projects
 * Agency (DARPA) and Air Force Research Laboratory, Air Force
 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
 */
/*-
 * Copyright (c) 2000 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Dieter Baron and Thomas Klausner.
 *
 * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <errno.h>
#include "getopt.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int	opterr = 1;		/* if error message should be printed */
int	optind = 1;		/* index into parent argv vector */
int	optopt = '?';		/* character checked for validity */
int	optreset;		/* reset getopt */
char    *optarg;		/* argument associated with option */

#define PRINT_ERROR	((opterr) && (*options != ':'))

#define FLAG_PERMUTE	0x01	/* permute non-options to the end of argv */
#define FLAG_ALLARGS	0x02	/* treat non-options as args to option "-1" */
#define FLAG_LONGONLY	0x04	/* operate as getopt_long_only */

/* return values */
#define	BADCH		(int)'?'
#define	BADARG		((*options == ':') ? (int)':' : (int)'?')
#define	INORDER 	(int)1

#define	EMSG		""

static int getopt_internal(int, char * const *, const char *,
			   const struct option *, int *, int);
static int parse_long_options(char * const *, const char *,
			      const struct option *, int *, int);
static int gcd(int, int);
static void permute_args(int, int, int, char * const *);

static char *place = EMSG; /* option letter processing */

/* XXX: set optreset to 1 rather than these two */
static int nonopt_start = -1; /* first non option argument (for permute) */
static int nonopt_end = -1;   /* first option after non options (for permute) */

/* Error messages */
static const char recargchar[] = "option requires an argument -- %c";
static const char recargstring[] = "option requires an argument -- %s";
static const char ambig[] = "ambiguous option -- %.*s";
static const char noarg[] = "option doesn't take an argument -- %.*s";
static const char illoptchar[] = "unknown option -- %c";
static const char illoptstring[] = "unknown option -- %s";

/*
 * Compute the greatest common divisor of a and b.
 */
static int
gcd(int a, int b)
{
	int c;

	c = a % b;
	while (c != 0) {
		a = b;
		b = c;
		c = a % b;
	}

	return (b);
}

/*
 * Exchange the block from nonopt_start to nonopt_end with the block
 * from nonopt_end to opt_end (keeping the same order of arguments
 * in each block).
 */
static void
permute_args(int panonopt_start, int panonopt_end, int opt_end,
	char * const *nargv)
{
	int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
	char *swap;

	/*
	 * compute lengths of blocks and number and size of cycles
	 */
	nnonopts = panonopt_end - panonopt_start;
	nopts = opt_end - panonopt_end;
	ncycle = gcd(nnonopts, nopts);
	cyclelen = (opt_end - panonopt_start) / ncycle;

	for (i = 0; i < ncycle; i++) {
		cstart = panonopt_end+i;
		pos = cstart;
		for (j = 0; j < cyclelen; j++) {
			if (pos >= panonopt_end)
				pos -= nnonopts;
			else
				pos += nopts;
			swap = nargv[pos];
			/* LINTED const cast */
			((char **) nargv)[pos] = nargv[cstart];
			/* LINTED const cast */
			((char **)nargv)[cstart] = swap;
		}
	}
}

/*
 * parse_long_options --
 *	Parse long options in argc/argv argument vector.
 * Returns -1 if short_too is set and the option does not match long_options.
 */
static int
parse_long_options(char * const *nargv, const char *options,
	const struct option *long_options, int *idx, int short_too)
{
	char *current_argv, *has_equal;
	size_t current_argv_len;
	int i, match;

	current_argv = place;
	match = -1;

	optind++;

	if ((has_equal = strchr(current_argv, '=')) != NULL) {
		/* argument found (--option=arg) */
		current_argv_len = has_equal - current_argv;
		has_equal++;
	} else
		current_argv_len = strlen(current_argv);

	for (i = 0; long_options[i].name; i++) {
		/* find matching long option */
		if (strncmp(current_argv, long_options[i].name,
		    current_argv_len))
			continue;

		if (strlen(long_options[i].name) == current_argv_len) {
			/* exact match */
			match = i;
			break;
		}
		/*
		 * If this is a known short option, don't allow
		 * a partial match of a single character.
		 */
		if (short_too && current_argv_len == 1)
			continue;

		if (match == -1)	/* partial match */
			match = i;
		else {
			/* ambiguous abbreviation */
			if (PRINT_ERROR)
				fprintf(stderr, "ambiguous option -- %.*s\n"
					, (int)current_argv_len,
					current_argv);
			optopt = 0;
			return (BADCH);
		}
	}
	if (match != -1) {		/* option found */
		if (long_options[match].has_arg == no_argument
		    && has_equal) {
			if (PRINT_ERROR)
				fprintf(stderr, "option doesn't take an "
					"argument -- %.*s\n",
					(int)current_argv_len,
					current_argv);
			/*
			 * XXX: GNU sets optopt to val regardless of flag
			 */
			if (long_options[match].flag == NULL)
				optopt = long_options[match].val;
			else
				optopt = 0;
			return (BADARG);
		}
		if (long_options[match].has_arg == required_argument ||
		    long_options[match].has_arg == optional_argument) {
			if (has_equal)
				optarg = has_equal;
			else if (long_options[match].has_arg ==
			    required_argument) {
				/*
				 * optional argument doesn't use next nargv
				 */
				optarg = nargv[optind++];
			}
		}
		if ((long_options[match].has_arg == required_argument)
		    && (optarg == NULL)) {
			/*
			 * Missing argument; leading ':' indicates no error
			 * should be generated.
			 */
			if (PRINT_ERROR)
				fprintf(stderr, "option requires an argument "
					"-- %s\n",
					current_argv);
			/*
			 * XXX: GNU sets optopt to val regardless of flag
			 */
			if (long_options[match].flag == NULL)
				optopt = long_options[match].val;
			else
				optopt = 0;
			--optind;
			return (BADARG);
		}
	} else {			/* unknown option */
		if (short_too) {
			--optind;
			return (-1);
		}
		if (PRINT_ERROR)
			fprintf(stderr, "unknown option -- %s\n", current_argv);
		optopt = 0;
		return (BADCH);
	}
	if (idx)
		*idx = match;
	if (long_options[match].flag) {
		*long_options[match].flag = long_options[match].val;
		return (0);
	} else
		return (long_options[match].val);
}

/*
 * getopt_internal --
 *	Parse argc/argv argument vector.  Called by user level routines.
 */
static int
getopt_internal(int nargc, char * const *nargv, const char *options,
	const struct option *long_options, int *idx, int flags)
{
	char *oli;				/* option letter list index */
	int optchar, short_too;
	static int posixly_correct = -1;

	if (options == NULL)
		return (-1);

	/*
	 * Disable GNU extensions if POSIXLY_CORRECT is set or options
	 * string begins with a '+'.
	 */
	if (posixly_correct == -1)
		posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
	if (posixly_correct || *options == '+')
		flags &= ~FLAG_PERMUTE;
	else if (*options == '-')
		flags |= FLAG_ALLARGS;
	if (*options == '+' || *options == '-')
		options++;

	/*
	 * XXX Some GNU programs (like cvs) set optind to 0 instead of
	 * XXX using optreset.  Work around this braindamage.
	 */
	if (optind == 0)
		optind = optreset = 1;

	optarg = NULL;
	if (optreset)
		nonopt_start = nonopt_end = -1;
start:
	if (optreset || !*place) {		/* update scanning pointer */
		optreset = 0;
		if (optind >= nargc) {          /* end of argument vector */
			place = EMSG;
			if (nonopt_end != -1) {
				/* do permutation, if we have to */
				permute_args(nonopt_start, nonopt_end,
				    optind, nargv);
				optind -= nonopt_end - nonopt_start;
			}
			else if (nonopt_start != -1) {
				/*
				 * If we skipped non-options, set optind
				 * to the first of them.
				 */
				optind = nonopt_start;
			}
			nonopt_start = nonopt_end = -1;
			return (-1);
		}
		if (*(place = nargv[optind]) != '-' ||
		    (place[1] == '\0' && strchr(options, '-') == NULL)) {
			place = EMSG;		/* found non-option */
			if (flags & FLAG_ALLARGS) {
				/*
				 * GNU extension:
				 * return non-option as argument to option 1
				 */
				optarg = nargv[optind++];
				return (INORDER);
			}
			if (!(flags & FLAG_PERMUTE)) {
				/*
				 * If no permutation wanted, stop parsing
				 * at first non-option.
				 */
				return (-1);
			}
			/* do permutation */
			if (nonopt_start == -1)
				nonopt_start = optind;
			else if (nonopt_end != -1) {
				permute_args(nonopt_start, nonopt_end,
				    optind, nargv);
				nonopt_start = optind -
				    (nonopt_end - nonopt_start);
				nonopt_end = -1;
			}
			optind++;
			/* process next argument */
			goto start;
		}
		if (nonopt_start != -1 && nonopt_end == -1)
			nonopt_end = optind;

		/*
		 * If we have "-" do nothing, if "--" we are done.
		 */
		if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
			optind++;
			place = EMSG;
			/*
			 * We found an option (--), so if we skipped
			 * non-options, we have to permute.
			 */
			if (nonopt_end != -1) {
				permute_args(nonopt_start, nonopt_end,
				    optind, nargv);
				optind -= nonopt_end - nonopt_start;
			}
			nonopt_start = nonopt_end = -1;
			return (-1);
		}
	}

	/*
	 * Check long options if:
	 *  1) we were passed some
	 *  2) the arg is not just "-"
	 *  3) either the arg starts with -- we are getopt_long_only()
	 */
	if (long_options != NULL && place != nargv[optind] &&
	    (*place == '-' || (flags & FLAG_LONGONLY))) {
		short_too = 0;
		if (*place == '-')
			place++;		/* --foo long option */
		else if (*place != ':' && strchr(options, *place) != NULL)
			short_too = 1;		/* could be short option too */

		optchar = parse_long_options(nargv, options, long_options,
		    idx, short_too);
		if (optchar != -1) {
			place = EMSG;
			return (optchar);
		}
	}

	if ((optchar = (int)*place++) == (int)':' ||
	    (optchar == (int)'-' && *place != '\0') ||
	    (oli = strchr(options, optchar)) == NULL) {
		/*
		 * If the user specified "-" and  '-' isn't listed in
		 * options, return -1 (non-option) as per POSIX.
		 * Otherwise, it is an unknown option character (or ':').
		 */
		if (optchar == (int)'-' && *place == '\0')
			return (-1);
		if (!*place)
			++optind;
		if (PRINT_ERROR)
			fprintf(stderr, "unknown option -- %c\n", optchar);
		optopt = optchar;
		return (BADCH);
	}
	if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
		/* -W long-option */
		if (*place)			/* no space */
			/* NOTHING */;
		else if (++optind >= nargc) {	/* no arg */
			place = EMSG;
			if (PRINT_ERROR)
				fprintf(stderr, "option requires an argument"
					" -- %c\n", optchar);
			optopt = optchar;
			return (BADARG);
		} else				/* white space */
			place = nargv[optind];
		optchar = parse_long_options(nargv, options, long_options,
		    idx, 0);
		place = EMSG;
		return (optchar);
	}
	if (*++oli != ':') {			/* doesn't take argument */
		if (!*place)
			++optind;
	} else {				/* takes (optional) argument */
		optarg = NULL;
		if (*place)			/* no white space */
			optarg = place;
		else if (oli[1] != ':') {	/* arg not optional */
			if (++optind >= nargc) {	/* no arg */
				place = EMSG;
				if (PRINT_ERROR)
					fprintf(stderr, "option requires an "
						"argument -- %c\n", optchar);
				optopt = optchar;
				return (BADARG);
			} else
				optarg = nargv[optind];
		}
		place = EMSG;
		++optind;
	}
	/* dump back option letter */
	return (optchar);
}

/*
 * getopt --
 *	Parse argc/argv argument vector.
 *
 * [eventually this will replace the BSD getopt]
 */
int
getopt(int nargc, char * const *nargv, const char *options)
{

	/*
	 * We don't pass FLAG_PERMUTE to getopt_internal() since
	 * the BSD getopt(3) (unlike GNU) has never done this.
	 *
	 * Furthermore, since many privileged programs call getopt()
	 * before dropping privileges it makes sense to keep things
	 * as simple (and bug-free) as possible.
	 */
	return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
}

/*
 * getopt_long --
 *	Parse argc/argv argument vector.
 */
int
getopt_long(int nargc, char * const *nargv, const char *options,
    const struct option *long_options, int *idx)
{

	return (getopt_internal(nargc, nargv, options, long_options, idx,
	    FLAG_PERMUTE));
}

/*
 * getopt_long_only --
 *	Parse argc/argv argument vector.
 */
int
getopt_long_only(int nargc, char * const *nargv, const char *options,
    const struct option *long_options, int *idx)
{

	return (getopt_internal(nargc, nargv, options, long_options, idx,
	    FLAG_PERMUTE|FLAG_LONGONLY));
}
07070100000034000081ED0000000000000000000000015FCD000200000A59000000000000000000000000000000000000002C00000000httperf-0.9.0+git.20201206/src/lib/getopt.h/*-
 * Copyright (c) 2000 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Dieter Baron and Thomas Klausner.
 *
 * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
 */

#ifndef _GETOPT_H_
#define _GETOPT_H_

/*
 * GNU-like getopt_long() and 4.4BSD getsubopt()/optreset extensions
 */
#define no_argument        0
#define required_argument  1
#define optional_argument  2

struct option {
	/* name of long option */
	const char *name;
	/*
	 * one of no_argument, required_argument, and optional_argument:
	 * whether option takes an argument
	 */
	int has_arg;
	/* if not NULL, set *flag to val when option found */
	int *flag;
	/* if flag not NULL, value to set *flag to; else return value */
	int val;
};

int	 getopt_long(int, char * const *, const char *,
	    const struct option *, int *);
int	 getopt_long_only(int, char * const *, const char *,
	    const struct option *, int *);
#ifndef _GETOPT_DEFINED_
#define _GETOPT_DEFINED_
int	 getopt(int, char * const *, const char *);
int	 getsubopt(char **, char * const *, char **);

extern   char *optarg;                  /* getopt(3) external variables */
extern   int opterr;
extern   int optind;
extern   int optopt;
extern   int optreset;
extern   char *suboptarg;               /* getsubopt(3) external variable */
#endif
 
#endif /* !_GETOPT_H_ */
07070100000035000081A40000000000000000000000015FCD000200000F0A000000000000000000000000000000000000002A00000000httperf-0.9.0+git.20201206/src/lib/heap.c/*
 * Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
 * 
 * This file is part of httperf, a web server performance measurment tool.
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 * 
 * In addition, as a special exception, the copyright holders give permission
 * to link the code of this work with the OpenSSL project's "OpenSSL" library
 * (or with modified versions of it that use the same license as the "OpenSSL" 
 * library), 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 this file, you may extend this
 * exception to your version of the file, but you are not obligated to do so.
 * If you do not wish to do so, delete this exception statement from your
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 
 */

#include "config.h"

#include <stdlib.h>

#include <generic_types.h>

#define Minimum_Heap_Size 10

struct Heap {
	u_long          array_size;
	u_long          num_elements;
	bool            (*compare) (Any_Type, Any_Type);
	Any_Type        storage[];	/* c99 Flexible Array Member */
};

struct Heap    *
create_heap(u_long size, bool(*compare_callback) (Any_Type, Any_Type))
{
	struct Heap    *h;

	if (size < Minimum_Heap_Size)
		size = Minimum_Heap_Size;

	/*
	 * We are using c99 Flexible Array Members so we can do this :) 
	 */
	h = malloc(sizeof(struct Heap) + sizeof(Any_Type) * size);
	if (!h)
		return NULL;

	h->array_size = size;
	h->num_elements = 0;
	h->compare = compare_callback;

	return h;
}

bool
is_heap_empty(struct Heap * h)
{
	return h->num_elements == 0;
}

bool
is_heap_full(struct Heap * h)
{
	return h->array_size == h->num_elements;
}

u_long
num_heap_elements(struct Heap * h)
{
	return h->num_elements;
}

void
free_heap(struct Heap *h)
{
	if (h != NULL)
		free(h);
}

#define PARENT(i)	(i/2)
bool
insert(Any_Type a, struct Heap *h)
{
	u_long          i;

	if (is_heap_full(h))
		return false;

	i = ++h->num_elements;

	/*
	 * find the correct place to insert
	 */
	while ((i > 1) && (*h->compare) (h->storage[PARENT(i)], a)) {
		h->storage[i] = h->storage[PARENT(i)];
		i = PARENT(i);
	}
	h->storage[i] = a;
	return true;
}

static void
percolate(struct Heap *h, u_long hole)
{
	int             child;
	Any_Type        dest_val = h->storage[hole];

	for (; hole * 2 <= h->num_elements; hole = child) {
		child = hole * 2;
		if (child != h->num_elements
		    && (*h->compare) (h->storage[child + 1],
				      h->storage[child + 1]))
			child++;
		if ((*h->compare) (h->storage[child + 1], dest_val))
			h->storage[hole] = h->storage[child];
		else
			break;
	}
	h->storage[hole] = dest_val;
}

Any_Type
remove_min(struct Heap *h)
{
	if (is_heap_empty(h)) {
		Any_Type temp = {0};
		return temp;
	}
	else {
		Any_Type        min = h->storage[1];
		h->storage[1] = h->storage[h->num_elements--];
		percolate(h, 1);

		return min;
	}
}

Any_Type
poll_min(struct Heap * h)
{
	if (is_heap_empty(h)) {
		Any_Type temp = {0};
		return temp;
	}
	else
		return h->storage[1];
}

void
heap_for_each(struct Heap *h, void (*action) (Any_Type))
{
	for (u_long i = 1; i <= h->num_elements; i++) {
		(*action) (h->storage[i]);
	}
}
07070100000036000081A40000000000000000000000015FCD000200000842000000000000000000000000000000000000002A00000000httperf-0.9.0+git.20201206/src/lib/heap.h/*
 * Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
 * 
 * This file is part of httperf, a web server performance measurment tool.
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 * 
 * In addition, as a special exception, the copyright holders give permission
 * to link the code of this work with the OpenSSL project's "OpenSSL" library
 * (or with modified versions of it that use the same license as the "OpenSSL" 
 * library), 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 this file, you may extend this
 * exception to your version of the file, but you are not obligated to do so.
 * If you do not wish to do so, delete this exception statement from your
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 
 */

#ifndef heap_h
#define heap_h

struct Heap;

typedef         bool(*heap_compare) (Any_Type, Any_Type);
typedef void    (*heap_for_each_action) (Any_Type);

struct Heap    *create_heap(u_long, heap_compare);
bool            is_heap_empty(struct Heap *);
bool            is_heap_full(struct Heap *);
u_long          num_heap_elements(struct Heap *);
void            free_heap(struct Heap *);

bool            insert(Any_Type, struct Heap *);
Any_Type        remove_min(struct Heap *);
Any_Type        poll_min(struct Heap *);

void            heap_for_each(struct Heap *, heap_for_each_action);

#endif /* Heap_h */
07070100000037000081A40000000000000000000000015FCD000200000D2F000000000000000000000000000000000000002A00000000httperf-0.9.0+git.20201206/src/lib/list.c/*
 * Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
 * 
 * This file is part of httperf, a web server performance measurment tool.
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 * 
 * In addition, as a special exception, the copyright holders give permission
 * to link the code of this work with the OpenSSL project's "OpenSSL" library
 * (or with modified versions of it that use the same license as the "OpenSSL" 
 * library), 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 this file, you may extend this
 * exception to your version of the file, but you are not obligated to do so.
 * If you do not wish to do so, delete this exception statement from your
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 
 */

#include "config.h"

#include <stdlib.h>

#include <generic_types.h>

struct Node {
	Any_Type        data;
	struct Node    *next;
};

struct List {
	struct Node    *dummy_head;
};

bool
is_list_empty(struct List *l)
{

	return l->dummy_head->next == NULL;
}

struct List    *
list_create()
{
	struct List    *l;

	if ((l = malloc(sizeof(struct List))) == NULL)
		goto create_error;

	if ((l->dummy_head = malloc(sizeof(struct Node))) == NULL)
		goto create_error;

	l->dummy_head->next = NULL;

	return l;

      create_error:
	if (l != NULL)
		free(l);

	return NULL;
}

void
list_free(struct List *l)
{
	free(l->dummy_head);
	l->dummy_head = NULL;
	free(l);
}

bool
list_push(struct List *l, Any_Type data)
{
	struct Node    *n;

	/*
	 * TODO: Implement caching so that we don't have to call
	 * malloc every time we push a new node onto the list
	 */
	if ((n = malloc(sizeof(struct Node))) == NULL) {
		return false;
	}

	n->data = data;
	n->next = l->dummy_head->next;
	l->dummy_head->next = n;

	return true;
}

Any_Type
list_top(struct List * l)
{
	return l->dummy_head->next->data;
}

Any_Type
list_pop(struct List * l)
{
	Any_Type        data;
	struct Node    *n;

	n = l->dummy_head->next;
	data = l->dummy_head->next->data;
	l->dummy_head->next = l->dummy_head->next->next;

	/*
	 * TODO: As per above, implement caching here so that this memory
	 * does not have to be freed
	 */
	free(n);

	return data;
}

void
list_remove_if_true(struct List *l, bool (*action) (Any_Type))
{
	struct Node    *n = l->dummy_head;

	while (n->next != NULL) {
		if ((*action) (n->next->data)) {
			struct Node    *oldnext = n->next;
			n->next = n->next->next;
			free(oldnext);
		} else
			n = n->next;
	}

}

void
list_for_each(struct List *l, int (*action) (Any_Type))
{
	struct Node    *n = l->dummy_head->next;

	while (n != NULL) {
		(*action) (n->data);
		n = n->next;
	}
}

07070100000038000081A40000000000000000000000015FCD0002000007C3000000000000000000000000000000000000002A00000000httperf-0.9.0+git.20201206/src/lib/list.h
/*
 * Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
 * 
 * This file is part of httperf, a web server performance measurment tool.
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 * 
 * In addition, as a special exception, the copyright holders give permission
 * to link the code of this work with the OpenSSL project's "OpenSSL" library
 * (or with modified versions of it that use the same license as the "OpenSSL" 
 * library), 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 this file, you may extend this
 * exception to your version of the file, but you are not obligated to do so.
 * If you do not wish to do so, delete this exception statement from your
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 
 */

#ifndef list_h
#define list_h

typedef bool    (*list_action) (Any_Type);
struct List;

struct List    *list_create();
void            list_free(struct List *);
bool            list_push(struct List *, Any_Type);
bool            is_list_empty(struct List *);
Any_Type        list_top(struct List *);
Any_Type        list_pop(struct List *);
void            list_remove_if_true(struct List *, list_action);
void            list_for_each(struct List *, list_action);

#endif /* list_h */
07070100000039000081A40000000000000000000000015FCD000200000C24000000000000000000000000000000000000002B00000000httperf-0.9.0+git.20201206/src/lib/queue.c/*
 * Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
 * 
 * This file is part of httperf, a web server performance measurment tool.
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 * 
 * In addition, as a special exception, the copyright holders give permission
 * to link the code of this work with the OpenSSL project's "OpenSSL" library
 * (or with modified versions of it that use the same license as the "OpenSSL" 
 * library), 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 this file, you may extend this
 * exception to your version of the file, but you are not obligated to do so.
 * If you do not wish to do so, delete this exception statement from your
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 
 */

#include "config.h"

#include <stdlib.h>

#include <generic_types.h>

#define Minimum_Wheel_Size 10

struct Queue {
	u_long          wheel_size;
	u_long          num_elements;
	u_long          first_element;
	u_long          last_element;
	Any_Type        wheel[];	/* c99 Flexible Array Member */
};

static void
empty_queue(struct Queue *q)
{
	q->num_elements = 0;
	q->first_element = 1;
	q->last_element = 0;

	for (u_long i = 0; i < q->wheel_size; i++)
		q->wheel[i].uw = 0;
}

struct Queue   *
create_queue(u_long size)
{
	struct Queue   *q;

	if (size < Minimum_Wheel_Size)
		size = Minimum_Wheel_Size;

	/*
	 * Again, we are using c99 Flexible Array Members so we can do this :) 
	 */
	q = malloc(sizeof(struct Queue) + sizeof(Any_Type) * size);
	if (q == NULL)
		return NULL;
	
	q->wheel_size = size;
	empty_queue(q);

	return q;
}

int
is_queue_empty(struct Queue *q)
{
	return q->num_elements == 0;
}

int
is_queue_full(struct Queue *q)
{
	return q->num_elements == q->wheel_size;
}

void
free_queue(struct Queue *q)
{
	if (q != NULL) {
		empty_queue(q);
		free(q);
	}
}

int
enqueue(Any_Type a, struct Queue *q)
{
	if (is_queue_full(q))
		return 0;
	
	q->num_elements++;

	if (++q->last_element == q->wheel_size)
		q->last_element = 0;

	q->wheel[q->last_element] = a;

	return 1;
}

void
dequeue(struct Queue *q)
{
	q->num_elements--;

	if (++q->first_element == q->wheel_size)
		q->first_element = 0;

}

Any_Type
get_front(struct Queue *q)
{
	return q->wheel[q->first_element];
}

Any_Type
get_front_and_dequeue(struct Queue * q)
{
	Any_Type        a = get_front(q);
	dequeue(q);

	return a;

}
0707010000003A000081A40000000000000000000000015FCD0002000007B2000000000000000000000000000000000000002B00000000httperf-0.9.0+git.20201206/src/lib/queue.h/*
 * Copyright (C) 2007 Ted Bullock <tbullock@canada.com>
 * 
 * This file is part of httperf, a web server performance measurment tool.
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 * 
 * In addition, as a special exception, the copyright holders give permission
 * to link the code of this work with the OpenSSL project's "OpenSSL" library
 * (or with modified versions of it that use the same license as the "OpenSSL" 
 * library), 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 this file, you may extend this
 * exception to your version of the file, but you are not obligated to do so.
 * If you do not wish to do so, delete this exception statement from your
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 
 */

#ifndef queue_h
#define queue_h

struct Queue;

struct Queue   *create_queue(u_long);
int             is_queue_empty(struct Queue *);
int             is_queue_full(struct Queue *);
void            free_queue(struct Queue *);
int             enqueue(Any_Type, struct Queue *);
Any_Type        get_front(struct Queue *);
void            dequeue(struct Queue *);
Any_Type        get_front_and_dequeue(struct Queue *);
void			print_vp(struct Queue *);

#endif /* queue_h */
0707010000003B000081ED0000000000000000000000015FCD000200000CF8000000000000000000000000000000000000003000000000httperf-0.9.0+git.20201206/src/lib/ssl_writev.c/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company and Contributors listed in
    AUTHORS file. Originally contributed by David Mosberger-Tang

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

/* This code is based on the generic writev() implementation of GNU
   libc.  It implements writev() simply in terms of write().  No
   atomicity guarantees and performance isn't great either, but its
   good enough for SSL purposes.  */

#include "config.h"

#ifdef HAVE_OPENSSL_SSL_H

/* AIX requires this to be the first thing in the file.  */
#ifndef __GNUC__
# if HAVE_ALLOCA_H
#  include <alloca.h>
# else
#  ifdef _AIX
#pragma alloca
#  else
#   ifndef alloca /* predefined by HP cc +Olibcalls */
char *alloca ();
#   endif
#  endif
# endif
#else
/* stdlib.h provides the alloca functionality. */
#include <stdlib.h>
#endif

#include <string.h>

#include <generic_types.h>
#include <sys/uio.h>

#include <openssl/rsa.h>
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

ssize_t
SSL_writev (SSL *ssl, const struct iovec *vector, int count)
{
  char *buffer;
  register char *bp;
  size_t bytes, to_copy;
  int i;

  /* Find the total number of bytes to be written.  */
  bytes = 0;
  for (i = 0; i < count; ++i)
    bytes += vector[i].iov_len;

  /* Allocate a temporary buffer to hold the data.  */
  buffer = (char *) alloca (bytes);

  /* Copy the data into BUFFER.  */
  to_copy = bytes;
  bp = buffer;
  for (i = 0; i < count; ++i)
    {
#     define min(a, b)		((a) > (b) ? (b) : (a))
      size_t copy = min (vector[i].iov_len, to_copy);

      memcpy ((void *) bp, (void *) vector[i].iov_base, copy);
      bp += copy;

      to_copy -= copy;
      if (to_copy == 0)
        break;
    }

  return SSL_write (ssl, buffer, bytes);
}

#endif /* HAVE_OPENSSL_SSL_H */
0707010000003C000081ED0000000000000000000000015FCD000200000DB0000000000000000000000000000000000000002C00000000httperf-0.9.0+git.20201206/src/localevent.c/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

#include "config.h"

#include <assert.h>
#include <stdio.h>

#include <generic_types.h>
#include <object.h>
#include <httperf.h>
#include <localevent.h>

#define MAX_NUM_OPS	4

static const char * const event_name[EV_NUM_EVENT_TYPES] =
  {
    "EV_PERF_SAMPLE",
    "EV_HOSTNAME_LOOKUP_START",
    "EV_HOSTNAME_LOOKUP_STOP",
    "EV_SESS_NEW",
    "EV_SESS_FAILED",
    "EV_SESS_DESTROYED",
    "EV_CONN_NEW",
    "EV_CONN_CONNECTING",
    "EV_CONN_CONNECTED",
    "EV_CONN_CLOSE",
    "EV_CONN_DESTROYED",
    "EV_CONN_FAILED",
    "EV_CONN_TIMEOUT",
    "EV_CALL_NEW",
    "EV_CALL_ISSUE",
    "EV_CALL_SEND_START",
    "EV_CALL_SEND_RAW_DATA",
    "EV_CALL_SEND_STOP",
    "EV_CALL_RECV_START",
    "EV_CALL_RECV_HDR",
    "EV_CALL_RECV_RAW_DATA",
    "EV_CALL_RECV_DATA",
    "EV_CALL_RECV_FOOTER",
    "EV_CALL_RECV_STOP",
    "EV_CALL_DESTROYED"
  };

typedef struct Event_Action
  {
    int num_ops;
    struct closure
      {
	Event_Handler op;
	Any_Type arg;
      }
    closure[MAX_NUM_OPS];
  }
Event_Action;

static Event_Action action[EV_NUM_EVENT_TYPES] = {{0, }};

void
event_register_handler (Event_Type et, Event_Handler handler, Any_Type arg)
{
  struct closure *c;
  int n;

  n = action[et].num_ops;
  if (n >= MAX_NUM_OPS)
    {
      fprintf (stderr, "event_register_handler: sorry, attempted to register "
	       "more than %d handlers\n", MAX_NUM_OPS);
      exit (1);
    }
  c = action[et].closure + n;
  c->op = handler;
  c->arg = arg;
  action[et].num_ops = n + 1;
}

void
event_signal (Event_Type type, Object *obj, Any_Type arg)
{
  Event_Action *act = action + type;
  struct closure *c, *end;

  if (DBG > 1)
    {
      assert (NELEMS (event_name) == EV_NUM_EVENT_TYPES);
      fprintf (stderr, "event_signal: %s (obj=%p,arg=%lx)\n",
	       event_name[type], obj, arg.l);
    }

  end = &act->closure[act->num_ops];
  for (c = &act->closure[0]; c < end; ++c)
    (*c->op) (type, obj, c->arg, arg);
}
0707010000003D000081ED0000000000000000000000015FCD000200000ACB000000000000000000000000000000000000002C00000000httperf-0.9.0+git.20201206/src/localevent.h/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

#ifndef localevent_h
#define localevent_h

typedef enum Event_Type
  {
    EV_NULL = 0,
    EV_PERF_SAMPLE,

    EV_HOSTNAME_LOOKUP_START,
    EV_HOSTNAME_LOOKUP_STOP,

    EV_SESS_NEW,
    EV_SESS_FAILED,
    EV_SESS_DESTROYED,

    EV_CONN_NEW,
    EV_CONN_CONNECTING,
    EV_CONN_CONNECTED,
    EV_CONN_CLOSE,		/* connection closed */
    EV_CONN_DESTROYED,
    EV_CONN_FAILED,		/* failed for reasons other than timeout */
    EV_CONN_TIMEOUT,

    EV_CALL_NEW,
    EV_CALL_ISSUE,
    EV_CALL_SEND_START,
    EV_CALL_SEND_RAW_DATA,
    EV_CALL_SEND_STOP,
    EV_CALL_RECV_START,
    EV_CALL_RECV_HDR,
    EV_CALL_RECV_RAW_DATA,
    EV_CALL_RECV_DATA,
    EV_CALL_RECV_FOOTER,
    EV_CALL_RECV_STOP,
    EV_CALL_DESTROYED,

    EV_NUM_EVENT_TYPES
  }
Event_Type;

typedef struct Event
  {
    Event_Type type;
    Object *obj;
    Any_Type arg;
  }
Event;

typedef void (*Event_Handler) (Event_Type type, Object *obj,
			       Any_Type registration_time_arg,
			       Any_Type signal_time_arg);

extern void event_register_handler (Event_Type et, Event_Handler handler,
				    Any_Type arg);
extern void event_signal (Event_Type type, Object *obj, Any_Type arg);

#endif /* localevent_h */
0707010000003E000081ED0000000000000000000000015FCD000200001095000000000000000000000000000000000000002800000000httperf-0.9.0+git.20201206/src/object.c/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

#include "config.h"

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <generic_types.h>

#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <localevent.h>
#include <sess.h>

#define ALIGN(s)	(((s) + sizeof (double) - 1) & ~(sizeof (double) - 1))

static size_t type_size[OBJ_NUM_TYPES] =
  {
    ALIGN (sizeof (Conn)),
    ALIGN (sizeof (Call)),
    ALIGN (sizeof (Sess))
  };

struct free_list_el
  {
    struct free_list_el *next;
  };

static struct free_list_el *free_list[OBJ_NUM_TYPES];

static void
object_destroy (Object *obj)
{
  Object_Type type = obj->type;
  struct free_list_el *el;
  Event_Type event = EV_NULL;
  Any_Type arg;

  switch (type)
    {
    case OBJ_CALL:
      call_deinit ((Call *) obj);
      event = EV_CALL_DESTROYED;
      break;

    case OBJ_CONN:
      conn_deinit ((Conn *) obj);
      event = EV_CONN_DESTROYED;
      break;

    case OBJ_SESS:
      sess_deinit ((Sess *) obj);
      event = EV_SESS_DESTROYED;
      break;

    default:
      assert (0);
      break;
    }
  arg.l = 0;
  event_signal (event, obj, arg);

  /* Each object must be at least the size and alignment of "struct
     free_list_el".  Malloc takes care of returning properly aligned
     objects.  */
  el = (struct free_list_el *) obj;
  el->next = free_list[type];
  free_list[type] = el;
}

size_t
object_expand (Object_Type type, size_t size)
{
  size_t offset = type_size[type];
  type_size[type] += ALIGN (size);
  return offset;
}

Object *
object_new (Object_Type type)
{
  struct free_list_el *el;
  Event_Type event = EV_NULL;
  size_t obj_size;
  Any_Type arg;
  Object *obj;

  obj_size = type_size[type];

  if (free_list[type])
    {
      el = free_list[type];
      free_list[type] = el->next;
      obj = (Object *) el;
    }
  else
    {
      obj = malloc (obj_size);
      if (!obj)
	{
	  fprintf (stderr, "%s.object_new: %s\n", prog_name, strerror (errno));
	  return 0;
	}
    }
  memset (obj, 0, obj_size);
  obj->ref_count = 1;
  obj->type = type;
  switch (type)
    {
    case OBJ_CALL:
      call_init ((Call *) obj);
      event = EV_CALL_NEW;
      break;

    case OBJ_CONN:
      conn_init ((Conn *) obj);
      event = EV_CONN_NEW;
      break;

    case OBJ_SESS:
      sess_init ((Sess *) obj);
      event = EV_SESS_NEW;
      break;

    default:
      panic ("object_new: bad object type %d\n", type);
      break;
    }
  arg.l = 0;
  event_signal (event, obj, arg);
  return obj;
}

void
object_dec_ref (Object *obj)
{
  assert (obj->ref_count > 0);

  if (--obj->ref_count == 0)
    object_destroy (obj);
}
0707010000003F000081ED0000000000000000000000015FCD000200000AFA000000000000000000000000000000000000002800000000httperf-0.9.0+git.20201206/src/object.h/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/
#ifndef object_h
#define object_h

#ifdef DEBUG
# define object_is_conn(o)	(((Object *) (o))->type == OBJ_CONN)
# define object_is_call(o)	(((Object *) (o))->type == OBJ_CALL)
# define object_is_sess(o)	(((Object *) (o))->type == OBJ_SESS)
#else
# define object_is_conn(o)	1
# define object_is_call(o)	1
# define object_is_sess(o)	1
#endif

typedef enum Object_Type
  {
    OBJ_CONN,			/* connection object */
    OBJ_CALL,			/* call object */
    OBJ_SESS,			/* session object */
    OBJ_NUM_TYPES
  }
Object_Type;

typedef struct Object
  {
    Object_Type type;
    u_int ref_count;			/* # of references to this object */
  }
Object;

/* This may be called during httperf initialize to reserve SIZE
   "private" bytes in objects of type TYPE.  The return value is the
   offset of the private area.  */

extern size_t object_expand (Object_Type type, size_t size);

/* Create a new object of type TYPE.  */
extern Object *object_new (Object_Type type);

/* Create a new reference for object OBJ.  */
#define object_inc_ref(o)	(++(o)->ref_count)

/* Decrement the reference for object OBJ.  If the reference count
   reaches zero, the object's destroy function is called.  */
extern void object_dec_ref (Object *obj);

#endif /* object_h */
07070100000040000081ED0000000000000000000000015FCD0002000007EF000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/src/sess.c/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

#include "config.h"

#include <generic_types.h>
#include <object.h>
#include <httperf.h>
#include <localevent.h>
#include <sess.h>

void
sess_init (Sess *sess)
{
}

void
sess_deinit (Sess *sess)
{
#ifdef HAVE_SSL
  if (sess->ssl)
    SSL_free (sess->ssl);
#endif
}

void
sess_failure (Sess *sess)
{
  Any_Type arg;

  if (sess->failed)
    return;
  sess->failed = 1;

  arg.l = 0;
  event_signal (EV_SESS_FAILED, (Object *) sess, arg);

  sess_dec_ref (sess);
}
07070100000041000081ED0000000000000000000000015FCD0002000009DB000000000000000000000000000000000000002600000000httperf-0.9.0+git.20201206/src/sess.h/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

#ifndef sess_h
#define sess_h

#ifdef HAVE_SSL
# include <openssl/ssl.h>
#endif

/* Sessions are not used by the httperf's core itself, but they are
   provided here for the benefit of workload generators that need such
   a notion (e.g., to represent users).  */
typedef struct Sess
  {
    Object obj;
#ifdef HAVE_SSL
    SSL *ssl;		/* SSL session (or NULL) */
#endif /* HAVE_SSL */
    u_int failed : 1;	/* did session fail? */
  }
Sess;

/* Initialize the new session object S.  */
extern void sess_init (Sess *s);

/* Destroy the session-specific state in session object S.  */
extern void sess_deinit (Sess *s);

/* Session S failed.  This causes EV_SESS_FAILED to be signalled and
   gives up the caller's reference to S.  */
extern void sess_failure (Sess *s);

#define sess_new()	((Sess *) object_new (OBJ_SESS))
#define sess_inc_ref(s)	object_inc_ref ((Object *) (s))
#define sess_dec_ref(s)	object_dec_ref ((Object *) (s))

#endif /* sess_h */
07070100000042000041ED0000000000000000000000025FCD000200000000000000000000000000000000000000000000002400000000httperf-0.9.0+git.20201206/src/stat07070100000043000081ED0000000000000000000000015FCD0002000000D6000000000000000000000000000000000000003000000000httperf-0.9.0+git.20201206/src/stat/Makefile.am# what flags you want to pass to the C compiler & linker
AM_CFLAGS = -I$(srcdir)/.. -I$(srcdir)/../gen -I$(srcdir)/../lib

noinst_LIBRARIES = libstat.a
libstat_a_SOURCES = basic.c sess_stat.c print_reply.c stats.h
07070100000044000081ED0000000000000000000000015FCD000200003DB3000000000000000000000000000000000000002C00000000httperf-0.9.0+git.20201206/src/stat/basic.c/*
 * httperf -- a tool for measuring web server performance
 * Copyright 2000-2007 Hewlett-Packard Company
 * 
 * This file is part of httperf, a web server performance measurment tool.
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 * 
 * In addition, as a special exception, the copyright holders give permission
 * to link the code of this work with the OpenSSL project's "OpenSSL" library
 * (or with modified versions of it that use the same license as the "OpenSSL" 
 * library), 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 this file, you may extend this
 * exception to your version of the file, but you are not obligated to do so.
 * If you do not wish to do so, delete this exception statement from your
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 
 */

/*
 * Basic statistics collector.  
 */

#include "config.h"

#include <assert.h>
#include <errno.h>
#include <float.h>
#include <stdio.h>

#include <generic_types.h>
#include <sys/resource.h>

#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <localevent.h>
#include <stats.h>

/*
 * Increase this if it does not cover at least 50% of all response times.  
 */
#define MAX_LIFETIME	100.0	/* max. conn. lifetime in seconds */
#define BIN_WIDTH	1e-3
#define NUM_BINS	((u_int) (MAX_LIFETIME / BIN_WIDTH))

static struct {
	u_long           num_conns_issued;	/* total # of connections * issued */
	u_long           num_replies[6];	/* completion count per status class */
	u_long           num_200;		/* total # of 200 responses */
	u_long           num_302;		/* total # of 302 responses */
	u_long           num_client_timeouts;	/* # of client timeouts */
	u_long           num_sock_fdunavail;	/* # of times out of *
										 * filedescriptors */
	u_long           num_sock_ftabfull;	/* # of times file table was full */
	u_long           num_sock_refused;	/* # of ECONNREFUSED */
	u_long           num_sock_reset;	/* # of ECONNRESET */
	u_long           num_sock_timeouts;	/* # of ETIMEDOUT */
	u_long           num_sock_addrunavail;	/* # of EADDRNOTAVAIL */
	u_long           num_other_errors;	/* # of other errors */
	u_long           max_conns;	/* max # of concurrent connections */

	u_long           num_lifetimes;
	Time            conn_lifetime_sum;	/* sum of connection lifetimes */
	Time            conn_lifetime_sum2;	/* sum of connection lifetimes
										 * squared */
	Time            conn_lifetime_min;	/* minimum connection lifetime */
	Time            conn_lifetime_max;	/* maximum connection lifetime */

	u_long           num_reply_rates;
	Time            reply_rate_sum;
	Time            reply_rate_sum2;
	Time            reply_rate_min;
	Time            reply_rate_max;

	u_long           num_connects;	/* # of completed connect()s */
	Time            conn_connect_sum;	/* sum of connect times */

	u_long           num_responses;
	Time            call_response_sum;	/* sum of response times */

	Time            call_xfer_sum;	/* sum of response times */

	u_long           num_sent;	/* # of requests sent */
	size_t          req_bytes_sent;

	u_long           num_received;	/* # of replies received */
	u_wide          hdr_bytes_received;	/* sum of all header bytes */
	u_wide          reply_bytes_received;	/* sum of all data bytes */
	u_wide          footer_bytes_received;	/* sum of all footer bytes */

	u_int           conn_lifetime_hist[NUM_BINS];	/* histogram of
													 * connection lifetimes */
} basic;

static u_long    num_active_conns;
static u_long    num_replies;	/* # of replies received in this interval */

static void
perf_sample(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type call_arg)
{
	Time            weight = call_arg.d;
	double          rate;

	assert(et == EV_PERF_SAMPLE);

	rate = weight * num_replies;

	if (verbose)
		printf("reply-rate = %-8.1f\n", rate);

	basic.reply_rate_sum += rate;
	basic.reply_rate_sum2 += SQUARE(rate);
	if (rate < basic.reply_rate_min)
		basic.reply_rate_min = rate;
	if (rate > basic.reply_rate_max)
		basic.reply_rate_max = rate;
	++basic.num_reply_rates;

	/*
	 * prepare for next sample interval: 
	 */
	num_replies = 0;
}

static void
conn_timeout(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type call_arg)
{
	assert(et == EV_CONN_TIMEOUT);

	++basic.num_client_timeouts;
}

static void
conn_fail(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type call_arg)
{
	static int      first_time = 1;
	int             err = call_arg.i;

	assert(et == EV_CONN_FAILED);

	switch (err) {
#ifdef __linux__
	case EINVAL:				/* Linux has a strange way of saying "out of
								 * * fds"... */
#endif
	case EMFILE:
		++basic.num_sock_fdunavail;
		break;
	case ENFILE:
		++basic.num_sock_ftabfull;
		break;
	case ECONNREFUSED:
		++basic.num_sock_refused;
		break;
	case ETIMEDOUT:
		++basic.num_sock_timeouts;
		break;

	case EPIPE:
	case ECONNRESET:
		++basic.num_sock_reset;
		break;

	default:
		if (first_time) {
			first_time = 0;
			fprintf(stderr,
					"%s: connection failed with unexpected error %d\n",
					prog_name, errno);
		}
		++basic.num_other_errors;
		break;
	}
}

static void
conn_created(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type c_arg)
{
	++num_active_conns;
	if (num_active_conns > basic.max_conns)
		basic.max_conns = num_active_conns;
}

static void
conn_connecting(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type c_arg)
{
	Conn           *s = (Conn *) obj;

	assert(et == EV_CONN_CONNECTING && object_is_conn(s));

	s->basic.time_connect_start = timer_now();
	++basic.num_conns_issued;
}

static void
conn_connected(Event_Type et, Object * obj, Any_Type reg_arg,
			   Any_Type call_arg)
{
	Conn           *s = (Conn *) obj;

	assert(et == EV_CONN_CONNECTED && object_is_conn(s));
	basic.conn_connect_sum += timer_now() - s->basic.time_connect_start;
	++basic.num_connects;
}

static void
conn_destroyed(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type c_arg)
{
	Conn           *s = (Conn *) obj;
	Time            lifetime;
	u_int           bin;

	assert(et == EV_CONN_DESTROYED && object_is_conn(s)
		   && num_active_conns > 0);

	if (s->basic.num_calls_completed > 0) {
		lifetime = timer_now() - s->basic.time_connect_start;
		basic.conn_lifetime_sum += lifetime;
		basic.conn_lifetime_sum2 += SQUARE(lifetime);
		if (lifetime < basic.conn_lifetime_min)
			basic.conn_lifetime_min = lifetime;
		if (lifetime > basic.conn_lifetime_max)
			basic.conn_lifetime_max = lifetime;
		++basic.num_lifetimes;

		bin = lifetime * NUM_BINS / MAX_LIFETIME;
		if (bin >= NUM_BINS)
			bin = NUM_BINS;
		++basic.conn_lifetime_hist[bin];
	}
	--num_active_conns;
}

static void
send_start(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type call_arg)
{
	Call           *c = (Call *) obj;

	assert(et == EV_CALL_SEND_START && object_is_call(c));

	c->basic.time_send_start = timer_now();
}

static void
send_stop(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type call_arg)
{
	Call           *c = (Call *) obj;

	assert(et == EV_CALL_SEND_STOP && object_is_call(c));

	basic.req_bytes_sent += c->req.size;
	++basic.num_sent;
}

static void
recv_start(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type call_arg)
{
	Call           *c = (Call *) obj;
	Time            now;

	assert(et == EV_CALL_RECV_START && object_is_call(c));

	now = timer_now();

	basic.call_response_sum += now - c->basic.time_send_start;
	c->basic.time_recv_start = now;
	++basic.num_responses;

	if (periodic_stats) {
		if (c->reply.status == 200)
			++basic.num_200;

		if (c->reply.status == 302)
			++basic.num_302;
	}
}

static void
recv_stop(Event_Type et, Object * obj, Any_Type reg_arg, Any_Type call_arg)
{
	Call           *c = (Call *) obj;
	int             index;

	assert(et == EV_CALL_RECV_STOP && object_is_call(c));
	assert(c->basic.time_recv_start > 0);

	basic.call_xfer_sum += timer_now() - c->basic.time_recv_start;

	basic.hdr_bytes_received += c->reply.header_bytes;
	basic.reply_bytes_received += c->reply.content_bytes;
	basic.footer_bytes_received += c->reply.footer_bytes;

	index = (c->reply.status / 100);
	assert((unsigned) index < NELEMS(basic.num_replies));
	++basic.num_replies[index];
	++num_replies;

	++c->conn->basic.num_calls_completed;
}

static void
one_second_timer(struct Timer *t, Any_Type arg)
{
	static u_long prev200 = 0;
	static u_long prev302 = 0;

	printf("[%.6f s] 200=%lu, 302=%lu\n",
		timer_now() - test_time_start,
		basic.num_200 - prev200,
		basic.num_302 - prev302);

	prev200 = basic.num_200;
	prev302 = basic.num_302;

	timer_schedule(one_second_timer, arg, 1);
}

static void
init(void)
{
	Any_Type        arg;

	basic.conn_lifetime_min = DBL_MAX;
	basic.reply_rate_min = DBL_MAX;

	arg.l = 0;
	event_register_handler(EV_PERF_SAMPLE, perf_sample, arg);
	event_register_handler(EV_CONN_FAILED, conn_fail, arg);
	event_register_handler(EV_CONN_TIMEOUT, conn_timeout, arg);
	event_register_handler(EV_CONN_NEW, conn_created, arg);
	event_register_handler(EV_CONN_CONNECTING, conn_connecting, arg);
	event_register_handler(EV_CONN_CONNECTED, conn_connected, arg);
	event_register_handler(EV_CONN_DESTROYED, conn_destroyed, arg);
	event_register_handler(EV_CALL_SEND_START, send_start, arg);
	event_register_handler(EV_CALL_SEND_STOP, send_stop, arg);
	event_register_handler(EV_CALL_RECV_START, recv_start, arg);
	event_register_handler(EV_CALL_RECV_STOP, recv_stop, arg);

	if (periodic_stats)
		timer_schedule(one_second_timer, arg, 1);
}

static void
dump(void)
{
	Time            conn_period = 0.0, call_period = 0.0;
	Time            conn_time = 0.0, resp_time = 0.0, xfer_time = 0.0;
	Time            call_size = 0.0, hdr_size = 0.0, reply_size =
		0.0, footer_size = 0.0;
	Time            lifetime_avg = 0.0, lifetime_stddev =
		0.0, lifetime_median = 0.0;
	double          reply_rate_avg = 0.0, reply_rate_stddev = 0.0;
	int             i;
	u_long          total_replies = 0;
	Time            delta, user, sys;
	u_wide          total_size;
	Time            time;
	u_int           n;

	for (i = 1; i < NELEMS(basic.num_replies); ++i)
		total_replies += basic.num_replies[i];

	delta = test_time_stop - test_time_start;

	if (verbose > 1) {
		printf("\nConnection lifetime histogram (time in ms):\n");
		for (i = 0; i < NUM_BINS; ++i)
			if (basic.conn_lifetime_hist[i]) {
				if (i > 0 && basic.conn_lifetime_hist[i - 1] == 0)
					printf("%14c\n", ':');
				time = (i + 0.5) * BIN_WIDTH;
				printf("%16.1f %u\n", 1e3 * time,
					   basic.conn_lifetime_hist[i]);
			}
	}

	printf("\nTotal: connections %lu requests %lu replies %lu "
		   "test-duration %.3f s\n",
		   basic.num_conns_issued, basic.num_sent, total_replies, delta);

	putchar('\n');

	if (basic.num_conns_issued)
		conn_period = delta / basic.num_conns_issued;
	printf("Connection rate: %.1f conn/s (%.1f ms/conn, "
		   "<=%lu concurrent connections)\n",
		   basic.num_conns_issued / delta, 1e3 * conn_period,
		   basic.max_conns);

	if (basic.num_lifetimes > 0) {
		lifetime_avg = (basic.conn_lifetime_sum / basic.num_lifetimes);
		if (basic.num_lifetimes > 1)
			lifetime_stddev = STDDEV(basic.conn_lifetime_sum,
									 basic.conn_lifetime_sum2,
									 basic.num_lifetimes);
		n = 0;
		for (i = 0; i < NUM_BINS; ++i) {
			n += basic.conn_lifetime_hist[i];
			if (n >= 0.5 * basic.num_lifetimes) {
				lifetime_median = (i + 0.5) * BIN_WIDTH;
				break;
			}
		}
	}
	printf("Connection time [ms]: min %.1f avg %.1f max %.1f median %.1f "
		   "stddev %.1f\n",
		   basic.num_lifetimes > 0 ? 1e3 * basic.conn_lifetime_min : 0.0,
		   1e3 * lifetime_avg,
		   1e3 * basic.conn_lifetime_max, 1e3 * lifetime_median,
		   1e3 * lifetime_stddev);
	if (basic.num_connects > 0)
		conn_time = basic.conn_connect_sum / basic.num_connects;
	printf("Connection time [ms]: connect %.1f\n", 1e3 * conn_time);
	printf("Connection length [replies/conn]: %.3f\n",
		   basic.num_lifetimes > 0
		   ? total_replies / (double) basic.num_lifetimes : 0.0);
	putchar('\n');

	if (basic.num_sent > 0)
		call_period = delta / basic.num_sent;
	printf("Request rate: %.1f req/s (%.1f ms/req)\n",
		   basic.num_sent / delta, 1e3 * call_period);

	if (basic.num_sent)
		call_size = basic.req_bytes_sent / basic.num_sent;
	printf("Request size [B]: %.1f\n", call_size);

	putchar('\n');

	if (basic.num_reply_rates > 0) {
		reply_rate_avg = (basic.reply_rate_sum / basic.num_reply_rates);
		if (basic.num_reply_rates > 1)
			reply_rate_stddev = STDDEV(basic.reply_rate_sum,
									   basic.reply_rate_sum2,
									   basic.num_reply_rates);
	}
	printf
		("Reply rate [replies/s]: min %.1f avg %.1f max %.1f stddev %.1f "
		 "(%lu samples)\n",
		 basic.num_reply_rates > 0 ? basic.reply_rate_min : 0.0,
		 reply_rate_avg, basic.reply_rate_max, reply_rate_stddev,
		 basic.num_reply_rates);

	if (basic.num_responses > 0)
		resp_time = basic.call_response_sum / basic.num_responses;
	if (total_replies > 0)
		xfer_time = basic.call_xfer_sum / total_replies;
	printf("Reply time [ms]: response %.1f transfer %.1f\n",
		   1e3 * resp_time, 1e3 * xfer_time);

	if (total_replies) {
		hdr_size = basic.hdr_bytes_received / total_replies;
		reply_size = basic.reply_bytes_received / total_replies;
		footer_size = basic.footer_bytes_received / total_replies;
	}
	printf("Reply size [B]: header %.1f content %.1f footer %.1f "
		   "(total %.1f)\n", hdr_size, reply_size, footer_size,
		   hdr_size + reply_size + footer_size);

	printf("Reply status: 1xx=%lu 2xx=%lu 3xx=%lu 4xx=%lu 5xx=%lu\n",
		   basic.num_replies[1], basic.num_replies[2],
		   basic.num_replies[3], basic.num_replies[4], basic.num_replies[5]);

	putchar('\n');

	if (periodic_stats) {
		printf("Periodic stats: 200=%lu 302=%lu\n", basic.num_200, basic.num_302);
		putchar('\n');
	}

	user = (TV_TO_SEC(test_rusage_stop.ru_utime)
			- TV_TO_SEC(test_rusage_start.ru_utime));
	sys = (TV_TO_SEC(test_rusage_stop.ru_stime)
		   - TV_TO_SEC(test_rusage_start.ru_stime));
	printf
		("CPU time [s]: user %.2f system %.2f (user %.1f%% system %.1f%% "
		 "total %.1f%%)\n", user, sys, 100.0 * user / delta,
		 100.0 * sys / delta, 100.0 * (user + sys) / delta);

	total_size = (basic.req_bytes_sent
				  + basic.hdr_bytes_received + basic.reply_bytes_received);
	printf("Net I/O: %.1f KB/s (%.1f*10^6 bps)\n",
		   total_size / delta / 1024.0, 8e-6 * total_size / delta);

	putchar('\n');

	printf("Errors: total %lu client-timo %lu socket-timo %lu "
		   "connrefused %lu connreset %lu\n"
		   "Errors: fd-unavail %lu addrunavail %lu ftab-full %lu other %lu\n",
		   (basic.num_client_timeouts + basic.num_sock_timeouts
			+ basic.num_sock_fdunavail + basic.num_sock_ftabfull
			+ basic.num_sock_refused + basic.num_sock_reset
			+ basic.num_sock_addrunavail + basic.num_other_errors),
		   basic.num_client_timeouts, basic.num_sock_timeouts,
		   basic.num_sock_refused, basic.num_sock_reset,
		   basic.num_sock_fdunavail, basic.num_sock_addrunavail,
		   basic.num_sock_ftabfull, basic.num_other_errors);
}

Stat_Collector  stats_basic = {
	"Basic statistics",
	init,
	no_op,
	no_op,
	dump
};
07070100000045000081ED0000000000000000000000015FCD000200001C9D000000000000000000000000000000000000003200000000httperf-0.9.0+git.20201206/src/stat/print_reply.c/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

/* This statistics collector simply prints the replies received from
   the server.  */

#include "config.h"

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <generic_types.h>

#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <localevent.h>

typedef struct Call_Private_Data
  {
    int done_with_reply_hdr; /* are we done printing reply header? */
    size_t size;	/* number of bytes allocated for "line" buffer */
    size_t len;		/* current length of line buffer */
    char *line;		/* line buffer */
  }
Call_Private_Data;

#define CALL_PRIVATE_DATA(c)						\
  ((Call_Private_Data *) ((char *)(c) + call_private_data_offset))

static size_t call_private_data_offset = -1;

static void
flush_print_buf (Call *call, const char *prefix)
{
  Call_Private_Data *priv = CALL_PRIVATE_DATA (call);

  if (priv->len)
    printf ("%s%ld:%.*s\n", prefix, call->id, (int) priv->len, priv->line);
  priv->len = 0;
}

static void
print_buf (Call *call, const char *prefix, const char *buf, int len)
{
  Call_Private_Data *priv = CALL_PRIVATE_DATA (call);
  const char *eol, *end;
  size_t line_len;

  for (end = buf + len; buf < end; buf += line_len)
    {
      line_len = (end - buf);
      eol = strchr (buf, '\n');
      if (eol)
	{
	  /* got a complete line: print it */
	  line_len = eol - buf;
	  printf ("%s%ld:", prefix, call->id);

	  if (priv->len)
	    printf ("%.*s", (int) priv->len, priv->line);
	  priv->len = 0;

	  if (line_len > 0)
	    printf ("%.*s", (int) line_len, buf);
	  putchar ('\n');

	  ++line_len;	/* skip over newline */
	}
      else
	{
	  /* got a partial line: buffer it */
	  if (priv->len + line_len > priv->size)
	    {
	      priv->size = priv->len + line_len;
	      if (priv->line)
		priv->line = realloc (priv->line, priv->size);
	      else
		priv->line = malloc (priv->size);
	      if (!priv->line)
		{
		  fprintf (stderr, "%s.print_buf: Out of memory\n", prog_name);
		  exit (1);
		}
	    }
	  memcpy (priv->line + priv->len, buf, line_len);
	  priv->len += line_len;
	}
    }
}

static void
print_request (Call *call)
{
  size_t hdr_len, h_len, b_len;
  int i, first, end;
  char *hdr;

  first = IE_CONTENT;
  end = IE_CONTENT;

  if ((param.print_request & PRINT_HEADER) != 0)
    first = IE_METHOD;

  if ((param.print_request & PRINT_BODY) != 0)
    end = IE_LEN;

  for (i = first; i < end; ++i)
    {
      hdr = call->req.iov[i].iov_base;
      hdr_len = call->req.iov[i].iov_len;

      if (hdr_len)
	print_buf (call, (i < IE_CONTENT) ? "SH" : "SB", hdr, hdr_len);
    }

  for (h_len = 0, i = IE_METHOD; i < IE_CONTENT; ++i)
    h_len += call->req.iov[i].iov_len;

  for (b_len = 0, i = IE_CONTENT; i < IE_LEN; ++i)
    b_len += call->req.iov[i].iov_len;

  printf ("SS%ld: header %ld content %ld\n",
	  call->id, (long) h_len, (long) b_len);
}

static void
print_reply_hdr (Call *call, const char *buf, int len)
{
  Call_Private_Data *priv = CALL_PRIVATE_DATA (call);
  const char *eoh;

  if (len <= 0 || priv->done_with_reply_hdr)
    return;

  eoh = strstr (buf, "\r\n\r\n");
  if (eoh)
    {
      priv->done_with_reply_hdr = 1;
      eoh += 4;
    }
  else
    {
      /* no CRLFCRLF: non-conforming server */
      eoh = strstr (buf, "\n\n");
      if (eoh)
	{
	  priv->done_with_reply_hdr = 1;
	  eoh += 2;
	}
      else
	eoh = buf + len;
    }
  print_buf (call, "RH", buf, eoh - buf);
}

static void
call_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  Call_Private_Data *priv;
  Call *call;

  assert (et == EV_CALL_DESTROYED && object_is_call (obj));
  call = (Call *) obj;
  priv = CALL_PRIVATE_DATA (call);

  if (priv->line)
    free (priv->line);
}

static void
send_raw_data (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  Call *call;

  assert (et == EV_CALL_SEND_RAW_DATA && object_is_call (obj));
  call = (Call *) obj;

  print_request (call);
}

static void
recv_raw_data (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  struct iovec *iov;
  Call *call;

  assert (et == EV_CALL_RECV_RAW_DATA && object_is_call (obj));
  call = (Call *) obj;
  iov = callarg.vp;

  print_reply_hdr (call, iov->iov_base, iov->iov_len);
}

static void
recv_data (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  struct iovec *iov;
  Call *call;

  assert (et == EV_CALL_RECV_DATA && object_is_call (obj));
  call = (Call *) obj;
  iov = callarg.vp;

  print_buf (call, "RB", iov->iov_base, iov->iov_len);
}

static void
recv_stop (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  Call *call;

  assert (et == EV_CALL_RECV_STOP && object_is_call (obj));
  call = (Call *) obj;

  flush_print_buf (call, "RB");

  printf ("RS%ld: header %ld content %ld footer %ld\n",
	  call->id, (long) call->reply.header_bytes,
	  (long) call->reply.content_bytes, (long) call->reply.footer_bytes);
}

static void
init (void)
{
  Any_Type arg;

  call_private_data_offset = object_expand (OBJ_CALL,
					    sizeof (Call_Private_Data));
  arg.l = 0;
  if ((param.print_request & (PRINT_HEADER | PRINT_BODY)) != 0)
    event_register_handler (EV_CALL_SEND_RAW_DATA, send_raw_data, arg);
  if ((param.print_reply & PRINT_HEADER) != 0)
    event_register_handler (EV_CALL_RECV_RAW_DATA, recv_raw_data, arg);
  if ((param.print_reply & PRINT_BODY) != 0)
    event_register_handler (EV_CALL_RECV_DATA, recv_data, arg);
  if ((param.print_reply & (PRINT_HEADER | PRINT_BODY)) != 0)
    event_register_handler (EV_CALL_RECV_STOP, recv_stop, arg);
  event_register_handler (EV_CALL_DESTROYED, call_destroyed, arg);
}

Stat_Collector stats_print_reply =
  {
    "Reply printer",
    init,
    no_op,
    no_op,
    no_op
  };
07070100000046000081ED0000000000000000000000015FCD000200001D1A000000000000000000000000000000000000003000000000httperf-0.9.0+git.20201206/src/stat/sess_stat.c/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

/* Session statistics collector.  */

#include "config.h"

#include <assert.h>
#include <float.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <generic_types.h>

#include <object.h>
#include <timer.h>
#include <httperf.h>
#include <call.h>
#include <conn.h>
#include <localevent.h>
#include <session.h>
#include <stats.h>

static struct
  {
    u_int num_rate_samples;
    u_int num_completed_since_last_sample;
    Time rate_sum;
    Time rate_sum2;
    Time rate_min;
    Time rate_max;

    u_int num_completed;
    Time lifetime_sum;

    u_int num_failed;
    Time failtime_sum;

    u_int num_conns;	/* total # of connections on successful sessions */

    /* session-length histogram: */
    u_int longest_session;
    u_int len_hist_alloced;
    u_int *len_hist;
  }
st;

#define SESS_PRIVATE_DATA(c)						\
  ((Sess_Private_Data *) ((char *)(c) + sess_private_data_offset))

typedef struct Sess_Private_Data
  {
    u_int num_calls_completed;	/* how many calls completed? */
    u_int num_conns;		/* # of connections on this session */
    Time birth_time;		/* when this session got created */
  }
Sess_Private_Data;

static size_t sess_private_data_offset = -1;


static void
perf_sample (Event_Type et, Object *obj, Any_Type reg_arg, Any_Type call_arg)
{
  Time weight = call_arg.d;
  double rate;

  assert (et == EV_PERF_SAMPLE);

  rate = weight*st.num_completed_since_last_sample;
  st.num_completed_since_last_sample = 0;

  if (verbose)
    printf ("session-rate = %-8.1f\n", rate);

  ++st.num_rate_samples;
  st.rate_sum += rate;
  st.rate_sum2 += SQUARE (rate);
  if (rate < st.rate_min)
    st.rate_min = rate;
  if (rate > st.rate_max)
    st.rate_max = rate;
}

static void
sess_created (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  Sess_Private_Data *priv;
  Sess *sess;

  assert (et == EV_SESS_NEW && object_is_sess (obj));
  sess = (Sess *) obj;
  priv = SESS_PRIVATE_DATA (sess);
  priv->birth_time = timer_now ();
}

static void
sess_destroyed (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  size_t old_size, new_size;
  Sess_Private_Data *priv;
  Sess *sess;
  Time delta, now = timer_now ();

  assert (et == EV_SESS_DESTROYED && object_is_sess (obj));
  sess = (Sess *) obj;
  priv = SESS_PRIVATE_DATA (sess);

  delta = (now - priv->birth_time);
  if (sess->failed)
    {
      ++st.num_failed;
      st.failtime_sum += delta;
    }
  else
    {
      st.num_conns += priv->num_conns;
      ++st.num_completed_since_last_sample;
      ++st.num_completed;
      st.lifetime_sum += delta;
    }

  if (priv->num_calls_completed > st.longest_session)
    {
      st.longest_session = priv->num_calls_completed;

      if (st.longest_session >= st.len_hist_alloced)
	{
	  old_size = st.len_hist_alloced*sizeof (st.len_hist[0]);
	  st.len_hist_alloced = st.longest_session + 16;
	  new_size = st.len_hist_alloced*sizeof (st.len_hist[0]);

	  st.len_hist = realloc (st.len_hist, new_size);
	  if (!st.len_hist)
	    {
	      fprintf (stderr, "%s.sess_stat: Out of memory\n", prog_name);
	      exit (1);
	    }
	  memset ((char *) st.len_hist + old_size, 0, new_size - old_size);
	}
    }
  ++st.len_hist[priv->num_calls_completed];
}

static void
conn_connected (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  Sess_Private_Data *priv;
  Sess *sess;
  Conn *conn;

  assert (et == EV_CONN_CONNECTED && object_is_conn (obj));
  conn = (Conn *) obj;
  sess = session_get_sess_from_conn (conn);
  priv = SESS_PRIVATE_DATA (sess);
  ++priv->num_conns;
}

static void
call_done (Event_Type et, Object *obj, Any_Type regarg, Any_Type callarg)
{
  Sess_Private_Data *priv;
  Sess *sess;
  Call *call;

  assert (et == EV_CALL_RECV_STOP && object_is_call (obj));
  call = (Call *) obj;
  sess = session_get_sess_from_call (call);
  priv = SESS_PRIVATE_DATA (sess);
  ++priv->num_calls_completed;
}

static void
init (void)
{
  Any_Type arg;
  size_t size;

  sess_private_data_offset = object_expand (OBJ_SESS,
					    sizeof (Sess_Private_Data));
  st.len_hist_alloced = 16;
  size = st.len_hist_alloced*sizeof (st.len_hist[0]);
  st.len_hist = malloc (size);
  memset (st.len_hist, 0, size);

  st.rate_min = DBL_MAX;

  if (!st.len_hist)
    {
      fprintf (stderr, "%s.sess_stat: Out of memory\n", prog_name);
      exit (1);
    }
  arg.l = 0;
  event_register_handler (EV_PERF_SAMPLE, perf_sample, arg);
  event_register_handler (EV_SESS_NEW, sess_created, arg);
  event_register_handler (EV_SESS_DESTROYED, sess_destroyed, arg);

  event_register_handler (EV_CONN_CONNECTED, conn_connected, arg);

  event_register_handler (EV_CALL_RECV_STOP, call_done, arg);
}

static void
dump (void)
{
  double min, avg, stddev, delta;
  int i;

  delta = test_time_stop - test_time_start;

  avg = 0;
  stddev = 0;
  if (delta > 0)
    avg = st.num_completed / delta;
  if (st.num_rate_samples > 1)
    stddev = STDDEV (st.rate_sum, st.rate_sum2, st.num_rate_samples);

  if (st.num_rate_samples > 0)
    min = st.rate_min;
  else
    min = 0.0;
  printf ("\nSession rate [sess/s]: min %.2f avg %.2f max %.2f "
	  "stddev %.2f (%u/%u)\n", min, avg, st.rate_max, stddev,
	  st.num_completed, st.num_completed + st.num_failed);

  printf ("Session: avg %.2f connections/session\n",
	  st.num_completed > 0 ? st.num_conns/(double) st.num_completed : 0.0);

  avg = 0.0;
  if (st.num_completed > 0)
    avg = st.lifetime_sum/st.num_completed;
  printf ("Session lifetime [s]: %.1f\n", avg);

  avg = 0.0;
  if (st.num_failed > 0)
    avg = st.failtime_sum/st.num_failed;
  printf ("Session failtime [s]: %.1f\n", avg);

  printf ("Session length histogram:");
  for (i = 0; i <= st.longest_session; ++i)
    printf (" %u", st.len_hist[i]);
  putchar ('\n');
}

Stat_Collector session_stat =
  {
    "collects session-related statistics",
    init,
    no_op,
    no_op,
    dump
  };
07070100000047000081ED0000000000000000000000015FCD000200000718000000000000000000000000000000000000002C00000000httperf-0.9.0+git.20201206/src/stat/stats.h/*
    httperf -- a tool for measuring web server performance
    Copyright 2000-2007 Hewlett-Packard Company

    This file is part of httperf, a web server performance measurment
    tool.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.
    
    In addition, as a special exception, the copyright holders give
    permission to link the code of this work with the OpenSSL project's
    "OpenSSL" library (or with modified versions of it that use the same
    license as the "OpenSSL" library), 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 this file, you may extend this exception to your version of the
    file, but you are not obligated to do so.  If you do not wish to do
    so, delete this exception statement from your version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  
    02110-1301, USA
*/

#ifndef stats_h
#define stats_h

#include <math.h>

#define SQUARE(f)	((f)*(f))
#define VAR(s,s2,n)	(((n) < 2) ? 0.0 : ((s2) - SQUARE(s)/(n)) / ((n) - 1))
#define STDDEV(s,s2,n)	(((n) < 2) ? 0.0 : sqrt (VAR ((s), (s2), (n))))

#endif /* stats_h */
07070100000048000081ED0000000000000000000000015FCD0002000019A9000000000000000000000000000000000000002700000000httperf-0.9.0+git.20201206/src/timer.c/*
 * Copyright (C) 2007 Ted Bullock <tbullock@comlore.com>
 * Copyright (C) 2000 Hewlett-Packard Company
 * 
 * This file is part of httperf, a web server performance measurment tool.
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 * 
 * In addition, as a special exception, the copyright holders give permission
 * to link the code of this work with the OpenSSL project's "OpenSSL" library
 * (or with modified versions of it that use the same license as the "OpenSSL" 
 * library), 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 this file, you may extend this
 * exception to your version of the file, but you are not obligated to do so.
 * If you do not wish to do so, delete this exception statement from your
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 
 */

#include "config.h"

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <string.h>

#include <generic_types.h>
#include <list.h>
#include <httperf.h>

#define NUM_TIMERS 25

static Time     now;

struct Timer {
	Time            time_started;
	Time            timeout_delay;
	bool            has_expired;

	/*
	 * Callback function called when timer expires (timeout) 
	 */
	void            (*timeout_callback) (struct Timer * t, Any_Type arg);

	/*
	 * Typically used as a void pointer to the data object being timed 
	 */
	Any_Type        timer_subject;
};

/*
 * inactive timers 
 */
static struct List *passive_timers = NULL;
/*
 * active timers 
 */
static struct List *active_timers = NULL;
/*
 * active timeoutless timers 
 */
static struct List *persistent_timers = NULL;

/*
 * Returns the time and calls the syscall gettimeofday.  This is an expensive
 * function since it requires a context switch.  Use of the cache is
 * preferable with the timer_now function
 */
Time
timer_now_forced(void)
{
	struct timeval  tv;

	gettimeofday(&tv, 0);
	return tv.tv_sec + tv.tv_usec * 1e-6;
}

/*
 * Returns the current time. If timer caching is enabled then uses the cache.
 */
Time
timer_now(void)
{
	if (param.use_timer_cache)
		return now;
	else
		return timer_now_forced();
}

/*
 * Initializes a large timer pool cache
 * This is a very expensive function.  Call before beginning measurements.
 * Returns 0 upon a memory allocation error
 */
bool
timer_init(void)
{
	passive_timers = list_create();
	if (passive_timers == NULL)
		goto init_failure;

	active_timers = list_create();
	if (active_timers == NULL)
		goto init_failure;

	persistent_timers = list_create();
	if (persistent_timers == NULL)
		goto init_failure;

	for (int i = 0; i < NUM_TIMERS; i++) {
		Any_Type        a;
		a.vp = malloc(sizeof(struct Timer));

		if (a.vp == NULL)
			goto init_failure;

		if (list_push(passive_timers, a) == false)
			goto init_failure;
	}

	now = timer_now_forced();

	return true;

      init_failure:
	fprintf(stderr, "%s.%s: %s\n", __FILE__, __func__, strerror(errno));
	return false;
}

/*
 * Frees all allocated timers, and timer queues
 */
void
timer_free_all(void)
{
	while (!is_list_empty(passive_timers)) {
		Any_Type        a = list_pop(passive_timers);
		free(a.vp);
	}
	list_free(passive_timers);
	passive_timers = NULL;

	while (!is_list_empty(active_timers)) {
		Any_Type        a = list_pop(active_timers);
		free(a.vp);
	}
	list_free(active_timers);
	active_timers = NULL;

	while (!is_list_empty(persistent_timers)) {
		Any_Type        a = list_pop(persistent_timers);
		free(a.vp);
	}
	list_free(persistent_timers);
	persistent_timers = NULL;
}

/*
 * Checks whether a timer has expired
 */
static bool
timer_has_expired(Any_Type a)
{
	struct Timer   *t = a.vp;

	/*
	 * Only expire currently processing timers 
	 */
	if (t->has_expired == false) {
		if (t->time_started + t->timeout_delay < timer_now()) {
			t->has_expired = true;
			(*t->timeout_callback) (t, t->timer_subject);
			return true;
		}
	}

	return false;
}

static bool
timer_deactivate(Any_Type a)
{
	struct Timer   *t = a.vp;

	/* TODO: Error check list_push */
	if (t->has_expired == true)
		list_push(passive_timers, a);

	return t->has_expired;
}

/*
 * Checks for timers which have had their timeout value pass and executes their
 * callback function.  The timer is then removed from the active timer list
 * and then enqueued back into the passive timer queue
 */
void
timer_tick(void)
{
	now = timer_now_forced();
	list_for_each(active_timers, &timer_has_expired);
	list_remove_if_true(active_timers, &timer_deactivate);
}

/*
 * Schedules a timer into the active_timer list.  Usually the timer is 
 * requisitioned from the passive_timer list to avoid making extra calls
 * to malloc, but will allocate memory for a new counter if there are no
 * inactive timers available
 */
struct Timer   *
timer_schedule(void (*timeout) (struct Timer * t, Any_Type arg),
	       Any_Type subject, Time delay)
{
	struct Timer   *t;

	if (!is_list_empty(passive_timers)) {
		Any_Type        a = list_pop(passive_timers);
		t = (struct Timer *) a.vp;
	} else if ((t = malloc(sizeof(struct Timer))) == NULL)
		return NULL;

	memset(t, 0, sizeof(struct Timer));
	t->timeout_callback = timeout;
	t->has_expired = false;
	t->timer_subject = subject;
	t->time_started = timer_now();
	t->timeout_delay = delay;

	if (delay > 0 || true)
	{
		Any_Type temp;
		temp.vp = (void *)t;
		list_push(active_timers, temp);
	}
	else
	{
		Any_Type temp;
		temp.vp = (void *)t;
		list_push(persistent_timers, temp);
	}

	if (DBG > 2)
		fprintf(stderr,
			"timer_schedule: t=%p, delay=%gs, subject=%lx\n", t,
			delay, subject.l);

	return t;
}

void
timer_cancel(struct Timer *t)
{
	if (DBG > 2)
		fprintf(stderr, "timer_cancel: t=%p\n", t);

	/*
	 * A module MUST NOT call timer_cancel() for a timer that is currently 
	 * being processed (whose timeout has expired).  
	 */

	t->has_expired = true;
}
07070100000049000081ED0000000000000000000000015FCD00020000080F000000000000000000000000000000000000002700000000httperf-0.9.0+git.20201206/src/timer.h/*
 * Copyright (C) 2007 Ted Bullock <tbullock@comlore.com>
 * Copyright (C) 2000 Hewlett-Packard Company
 * 
 * This file is part of httperf, a web server performance measurment tool.
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 * 
 * In addition, as a special exception, the copyright holders give permission
 * to link the code of this work with the OpenSSL project's "OpenSSL" library
 * (or with modified versions of it that use the same license as the "OpenSSL" 
 * library), 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 this file, you may extend this
 * exception to your version of the file, but you are not obligated to do so.
 * If you do not wish to do so, delete this exception statement from your
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 
 */

#ifndef timer_h
#define timer_h

struct Timer;
typedef void    (*Timer_Callback) (struct Timer * t, Any_Type arg);

Time     timer_now_forced(void);
Time     timer_now(void);

bool      timer_init(void);
void     timer_reset_all(void);
void     timer_free_all(void);
/*
 * Needs to be called at least once every TIMER_INTERVAL: 
 */
void     timer_tick(void);

struct Timer   *timer_schedule(Timer_Callback timeout, Any_Type arg,
			       Time delay);
void     timer_cancel(struct Timer * t);

#endif /* timer_h */
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!864 blocks
openSUSE Build Service is sponsored by