You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by bc...@apache.org on 2012/05/01 19:45:50 UTC

[2/2] Open sourcing benchmarking tool

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9ce0e718/tools/jtest/jtest.cc
----------------------------------------------------------------------
diff --git a/tools/jtest/jtest.cc b/tools/jtest/jtest.cc
new file mode 100644
index 0000000..05680db
--- /dev/null
+++ b/tools/jtest/jtest.cc
@@ -0,0 +1,6106 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <assert.h>
+#if !defined(_WIN32)
+#include <poll.h>
+#endif
+#if !defined(IRIX) && !defined(_WIN32)
+#include <netinet/tcp.h>
+#endif
+#include <sys/resource.h>
+#if defined(__alpha)
+#include <sys/sysinfo.h>
+#include <machine/hal_sysinfo.h>
+#include <timers.h>
+#endif
+#include <math.h>
+#include <limits.h>
+#include <sys/mman.h>
+
+#ifdef __alpha
+#define ink64 long
+#define inku64 unsigned long
+#else
+#define ink64 long long
+#define inku64 unsigned long long
+#endif
+
+#if !defined (sparc) && !defined (_WIN32)
+#  include <time.h>
+#  include <sys/time.h>
+#  include <stdlib.h>
+   typedef ink64 ink_hrtime;
+#else /* !defined (NEED_HRTIME) || defined (_WIN32) */
+#  if !defined (_WIN32)
+#      include <sys/time.h>
+       typedef hrtime_t ink_hrtime;
+#  else
+#      include <time.h>
+      typedef ink64 ink_hrtime;
+#  endif
+#endif
+
+#define bool int
+#define false 0
+#define true 1
+#define SIZE(x) (sizeof(x)/sizeof((x)[0]))
+
+/*
+ FTP - Traffic Server Template
+   220 i5 FTP server (Version wu-2.4(3) Mon Jul 8 14:39:48 PDT 1996) ready.
+   USER anonymous
+   331 Guest login ok, send your complete e-mail address as password.
+   PASS traffic_server@inktomi.com
+   230 Guest login ok, access restrictions apply.
+   CWD .
+   250 CWD command successful.
+   TYPE I
+   200 Type set to I.
+   PASV
+   227 Entering Passive Mode (128,174,5,14,16,238)
+   RETR foo
+   LIST
+   150 Opening ASCII mode data connection for /bin/ls.
+*/
+
+#define MAX_URL_LEN 1024
+
+#if defined(freebsd)
+extern "C" int gethostname(char *name, int namelen);
+#endif
+
+//
+// Compilation Options
+//
+
+#define SERVER_BUFSIZE             4096
+#define CLIENT_BUFSIZE             2048
+#define MAX_BUFSIZE                (65536 + 4096)
+
+
+//#define RETRY_CLIENT_WRITE_ERRORS
+#define VERSION_NUM "$Revision: 1.94 $"
+#if defined(BUILD_PERSON) && defined(BUILD_MACHINE)
+#define VERSION ("JTest Version %s - " __DATE__ " " __TIME__ \
+		 " (" BUILD_PERSON "@" BUILD_MACHINE ")\n" )
+#else
+#define VERSION ("JTest Version %s - " __DATE__ " " __TIME__ "\n")
+#endif
+// #define PRINT_LOCAL_PORT
+
+//
+// Contants
+//
+#define MAXFDS                65536
+#define HEADER_DONE           -1
+#define POLL_GROUP_SIZE       800
+#define MAX_RESPONSE_LENGTH   1000000
+#define HEADER_SIZE           10000
+#define POLL_TIMEOUT          10
+#define STATE_FTP_DATA_READY  0xFAD
+#define MAX_DEFERED_URLS      10000
+#define DEFERED_URLS_BLOCK    2000
+
+enum FTP_MODE {
+  FTP_NULL,
+  FTP_PORT,
+  FTP_PASV
+};
+
+typedef int (*accept_fn_t)(int);
+typedef int (*poll_cb)(int);
+struct ArgumentDescription;
+typedef void ArgumentFunction(
+  ArgumentDescription * argument_descriptions, int n_argument_descriptions,
+  char * arg);
+
+static int read_request(int sock);
+static int write_request(int sock);
+static int make_client (unsigned int addr, int port);
+static void make_bfc_client (unsigned int addr, int port);
+static int make_url_client(char * url,char * base_url = 0, bool seen = false,
+			   bool unthrottled = false);
+static int write_ftp_response(int sock);
+static void interval_report();
+static void undefer_url(bool unthrottled = false);
+static void done();
+static int is_done();
+static int open_server(unsigned short int port, accept_fn_t accept_fn);
+static int accept_ftp_data (int sock);
+
+char ** defered_urls = NULL; 
+int n_defered_urls = 0;
+int server_fd = 0;
+int server_port = 0;
+int proxy_port = 8080;
+unsigned int proxy_addr = 0;
+unsigned int local_addr = 0;
+char proxy_host[81] = "localhost";
+char local_host[81];
+int verbose = 0;
+int verbose_errors = 1;
+int debug = 0;
+int nclients = 100;
+int current_clients = 0;
+int client_speed = 0;
+int check_content = 0;
+int nocheck_length = 0;
+int obey_redirects = 1;
+int only_clients = 0;
+int only_server = 0;
+int drop_after_CL = 0;
+int server_speed = 0;
+int server_delay = 0;
+int interval = 1;
+int sbuffersize = SERVER_BUFSIZE;
+int cbuffersize = CLIENT_BUFSIZE;
+int test_time = 0;
+int last_fd = -1;
+char * response_buffer = NULL;
+int errors = 0;
+int clients = 0, running_clients = 0, new_clients = 0, total_clients = 0;
+int servers = 0, running_servers = 0, new_servers = 0, total_servers = 0;
+float running_ops = 0;
+int ops = 0, new_ops = 0;
+float total_ops = 0;
+int running_sops = 0, new_sops = 0, total_sops = 0;
+int running_latency = 0, latency = 0;
+int lat_ops = 0, b1_ops = 0, running_b1latency = 0, b1latency = 0;
+int running_cbytes = 0, new_cbytes = 0, total_cbytes = 0;
+int running_tbytes = 0, new_tbytes = 0, total_tbytes = 0;
+int average_over = 5;
+double hitrate = 0.4;
+int hotset = 1000;
+int keepalive = 4;
+int keepalive_cons = 4;
+int follow_arg = 0;
+int follow = 0;
+int follow_same_arg = 0;
+int follow_same = 0;
+char current_host[512];
+int fullpage = 0;
+int show_before = 0;
+int show_headers = 0;
+int server_keepalive = 4;
+int version = 0;
+int urls_mode = 0;
+int pipeline = 1;
+int hostrequest = 0;
+int ftp = 0;
+double ftp_mdtm_err_rate = 0.0;
+int ftp_mdtm_rate = 0;
+time_t ftp_mdtm_last_update = 0;
+char ftp_mdtm_str[64];
+int embed_url = 1;
+double ims_rate = 0.5;
+double client_abort_rate = 0.0;
+double server_abort_rate = 0.0;
+int compd_port = 0;
+int compd_suite = 0;
+int ka_cache_head[500];
+int ka_cache_tail[500];
+int n_ka_cache = 0;
+char urls_file[256] = "";
+FILE * urls_fp = NULL;
+char urlsdump_file[256] = "";
+FILE * urlsdump_fp = NULL;
+int drand_seed = 0;
+int docsize = 0;
+int url_hash_entries = 100000;
+char url_hash_filename[256] = "";
+int bandwidth_test = 0;
+int bandwidth_test_to_go = 0;
+int total_client_request_bytes = 0;
+int total_proxy_request_bytes = 0;
+int total_server_response_body_bytes = 0;
+int total_server_response_header_bytes = 0;
+int total_proxy_response_body_bytes = 0;
+int total_proxy_response_header_bytes = 0;
+ink_hrtime now = 0, start_time = 0;
+ArgumentFunction jtest_usage;
+int extra_headers = 0;
+int alternates = 0;
+int abort_retry_speed = 0;
+int abort_retry_bytes = 0;
+int abort_retry_secs = 5;
+int client_rate = 0;
+double reload_rate = 0;
+int vary_user_agent = 0;
+int server_content_type = 0;
+int request_extension = 0;
+int no_cache = 0;
+double evo_rate = 0.0;
+int evo_delta = 100;
+char evo_str[16];
+
+
+struct ArgumentDescription {
+  const char * name;
+  char   key;
+  const char *description;
+  const char *type;
+  void * location;
+  const char *env;
+  ArgumentFunction * pfn;       
+};
+
+ArgumentDescription argument_descriptions[] = {
+  {"proxy_port",'p',"Proxy Port","I",&proxy_port,"JTEST_PROXY_PORT",NULL},
+  {"proxy_host",'P',"Proxy Host","S80",&proxy_host,"JTEST_PROXY_HOST",NULL},
+  {"server_port",'s',"Server Port (0:auto select)","I",&server_port,
+   "JTEST_SERVER_PORT",NULL},
+  {"server_host",'S',"Server Host (null:localhost)","S80",
+   &local_host,"JTEST_SERVER_HOST",NULL},
+  {"server_speed",'r',"Server Bytes Per Second (0:unlimit)","I",
+   &server_speed,"JTEST_SERVER_SPEED",NULL},
+  {"server_delay", 'w', "Server Initial Delay (msec)", "I", &server_delay,
+   "JTEST_SERVER_INITIAL_DELAY", NULL},
+  {"clients",'c',"Clients","I",&nclients,"JTEST_CLIENTS",NULL},
+  {"client_speed",'R',"Client Bytes Per Second (0:unlimit)","I",
+   &client_speed,"JTEST_CLIENT_SPEED",NULL},
+  {"sbuffersize",'b',"Server Buffer Size","I",&sbuffersize,
+   "JTEST_SERVER_BUFSIZE", NULL},
+  {"cbuffersize",'B',"Client Buffer Size","I",&cbuffersize,
+   "JTEST_CLIENT_BUFSIZE", NULL},
+  {"average_over",'a',"Seconds to Average Over","I",&average_over,
+   "JTEST_AVERAGE_OVER", NULL},
+  {"hitrate",'z',"Hit Rate","D",&hitrate,"JTEST_HITRATE",NULL},
+  {"hotset",'Z',"Hotset Size","I",&hotset,"JTEST_HOTSET",NULL},
+  {"interval",'i',"Reporting Interval (seconds)","I",&interval,
+   "JTEST_INTERVAL",NULL},
+  {"keepalive",'k',"Keep-Alive Length","I",&keepalive,
+   "JTEST_KEEPALIVE",NULL},
+  {"keepalive_cons",'K',"# Keep-Alive Connections (0:unlimit)","I",
+   &keepalive_cons, "JTEST_KEEPALIVE_CONNECTIONS",NULL },
+  {"docsize", 'L', "Document Size (0:varied)","I", &docsize, 
+   "JTEST_DOCSIZE",NULL},
+  {"skeepalive",'j',"Server Keep-Alive (0:unlimit)","I",&server_keepalive,
+   "JTEST_SERVER_KEEPALIVE",NULL},
+  {"show_urls", 'x', "Show URLs before they are accessed","F", &show_before, 
+   "JTEST_SHOW_URLS",NULL},
+  {"show_headers", 'X', "Show Headers","F", &show_headers, 	
+   "JTEST_SHOW_HEADERS",NULL},
+  {"ftp",'f',"FTP Requests","F",&ftp, "JTEST_FTP",NULL},
+  {"ftp_mdtm_err_rate", ' ', "FTP MDTM 550 Error Rate", "D",
+   &ftp_mdtm_err_rate, "JTEST_FTP_MDTM_ERR_RATE", NULL},
+  {"ftp_mdtm_rate", ' ', "FTP MDTM Update Rate (sec, 0:never)", "I",
+   &ftp_mdtm_rate, "JTEST_FTP_MDTM_RATE", NULL},
+  {"fullpage",'l',"Full Page (Images)","F",&fullpage, 
+   "JTEST_FULLPAGE",NULL},
+  {"follow",'F',"Follow Links","F",&follow_arg, "JTEST_FOLLOW",NULL},
+  {"same_host",'J',"Only follow URLs on same host","F",&follow_same_arg, 
+   "JTEST_FOLLOW_SAME",NULL},
+  {"test_time",'t',"run for N seconds (0:unlimited)","I", &test_time, 
+   "TEST_TIME",NULL},
+  {"urls",'u',"URLs from File","S256", urls_file, "JTEST_URLS",NULL},
+  {"urlsdump", 'U', "URLs to File", "S256", urlsdump_file, 
+   "JTEST_URLS_DUMP", NULL},
+  {"hostrequest",'H',"Host Request(1=yes,2=transparent)","I", &hostrequest, "JTEST_HOST_REQUEST",
+   NULL},
+  {"check_content",'C',"Check returned content", "F", 
+   &check_content, "JTEST_CHECK_CONTENT", NULL},
+  {"nocheck_length",' ',"Don't check returned length", "F", 
+   &nocheck_length, "JTEST_NOCHECK_LENGTH", NULL},
+  {"obey_redirects",'m',"Obey Redirects", "f", &obey_redirects, 
+   "JTEST_OBEY_REDIRECTS", NULL},
+  {"embed URL",'M',"Embed URL in synth docs", "f", 
+   &embed_url, "JTEST_EMBED_URL", NULL},
+  {"url_hash_entries",'q',"URL Hash Table Size (-1:use file size)", 
+   "I", &url_hash_entries, "JTEST_URL_HASH_ENTRIES", NULL},
+  {"url_hash_filename",'Q',"URL Hash Table Filename", "S256", 
+   url_hash_filename, "JTEST_URL_HASH_FILENAME", NULL},
+  {"only_clients",'y',"Only Clients","F",&only_clients,
+   "JTEST_ONLY_CLIENTS",NULL},
+  {"only_server",'Y',"Only Server","F",&only_server,"JTEST_ONLY_SERVER",NULL},
+  {"bandwidth_test",'A',"Bandwidth Test","I",&bandwidth_test,
+   "JTEST_BANDWIDTH_TEST",NULL},
+  {"drop_after_CL",'T',"Drop after Content-Length","F", 
+   &drop_after_CL, "JTEST_DROP",NULL},
+  {"version",'V',"Version","F",&version,"JTEST_VERSION",NULL},
+  {"verbose",'v',"Verbose Flag","F",&verbose,"JTEST_VERBOSE",NULL},
+  {"verbose_errors",'E',"Verbose Errors Flag","f",&verbose_errors,
+   "JTEST_VERBOSE_ERRORS",NULL},
+  {"drand",'D',"Random Number Seed","I",&drand_seed,"JTEST_DRAND",NULL},
+  {"ims_rate",'I',"IMS Not-Changed Rate","D",&ims_rate, "JTEST_IMS_RATE",NULL},
+  {"client_abort_rate",'g',"Client Abort Rate","D",&client_abort_rate, 
+   "JTEST_CLIENT_ABORT_RATE",NULL},
+  {"server_abort_rate",'G',"Server Abort Rate","D",&server_abort_rate, 
+   "JTEST_SERVER_ABORT_RATE",NULL},
+  {"extra_headers",'n',"Number of Extra Headers","I",&extra_headers,
+   "JTEST_EXTRA_HEADERS",NULL},
+  {"alternates",'N',"Number of Alternates","I",&alternates,
+   "JTEST_ALTERNATES",NULL},
+  {"client_rate", 'e', "Clients Per Sec", "I", &client_rate,
+   "JTEST_CLIENT_RATE",NULL},
+  {"abort_retry_speed", 'o', "Abort/Retry Speed", "I", 
+   &abort_retry_speed, "JTEST_ABORT_RETRY_SPEED",NULL},
+  {"abort_retry_bytes", ' ', "Abort/Retry Threshhold (bytes)", "I", 
+   &abort_retry_bytes, "JTEST_ABORT_RETRY_THRESHHOLD_BYTES",NULL},
+  {"abort_retry_secs", ' ', "Abort/Retry Threshhold (secs)", "I", 
+   &abort_retry_secs, "JTEST_ABORT_RETRY_THRESHHOLD_SECS",NULL},
+  {"reload_rate", 'W', "Reload Rate", "D",
+   &reload_rate, "JTEST_RELOAD_RATE",NULL},
+  {"compd_port",'O',"Compd port","I",&compd_port, "JTEST_COMPD_PORT",NULL},
+  {"compd_suite",'1',"Compd Suite","F",&compd_suite, "JTEST_COMPD_SUITE",NULL},
+  {"vary_user_agent",'2',"Vary on User-Agent (use w/ alternates)","I",
+   &vary_user_agent,"JTEST_VARY_ON_USER_AGENT",NULL},
+  {"content_type",'3',"Server Content-Type (1 html, 2 jpeg)","I",
+   &server_content_type,"JTEST_CONTENT_TYPE",NULL},
+  {"request_extension",'4',"Request Extn (1\".html\" 2\".jpeg\" 3\"/\")","I",
+   &request_extension,"JTEST_REQUEST_EXTENSION",NULL},
+  {"no_cache",'5',"Send Server no-cache","I",&no_cache,"JTEST_NO_CACHE",NULL},
+  {"evo_rate",'9',"Evolving Hotset Rate (evolutions/hour)","D",
+   &evo_rate,"JTEST_EVOLVING_HOTSET_RATE",NULL},
+  {"debug",'d',"Debug Flag","F",&debug,"JTEST_DEBUG",NULL},
+  {"help",'h',"Help",NULL,NULL,NULL,jtest_usage}
+};
+int n_argument_descriptions = SIZE(argument_descriptions);
+
+struct FD {
+  int fd;
+  poll_cb read_cb;
+  poll_cb write_cb;
+  ink_hrtime start;
+  ink_hrtime active;
+  ink_hrtime ready;
+
+  double doc;
+  int doc_length;
+  struct sockaddr_in name;
+
+  int state;             // request parsing state
+  int req_pos;           // request read position
+  char * base_url;
+  char * req_header;
+  char * response;
+  char * response_header;
+  int length;
+  int response_length;
+  int response_remaining;
+  int keepalive;
+  int next;
+  int nalternate;
+  unsigned int ip;
+  unsigned int binary:1;
+  unsigned int ims:1;
+  unsigned int drop_after_CL:1;
+  unsigned int client_abort:1;
+  unsigned int jg_compressed:1;
+  int * count;
+  int bytes;
+  int ftp_data_fd;
+  FTP_MODE ftp_mode;
+  unsigned int ftp_peer_addr;
+  unsigned short ftp_peer_port;
+
+  void reset() {
+    next = 0;
+    fd = -1;
+    read_cb = NULL;
+    write_cb = NULL;
+    state = 0;
+    start = 0;
+    active = 0;
+    ready = 0;
+    req_pos = 0;
+    length = 0;
+    if (!urls_mode)
+      response = NULL;
+    response_header[0] = 0;
+    response_length = 0;
+    response_remaining = 0;
+    count = NULL;
+    bytes = 0;
+    doc = 0.0;
+    doc_length = 0;
+    ims = 0;
+    drop_after_CL = ::drop_after_CL;
+    client_abort = 0;
+    jg_compressed = 0;
+    ftp_mode = FTP_NULL;
+    ftp_peer_addr = 0;
+    ftp_peer_port = 0;
+  }
+  
+  void close();
+  FD() { 
+    req_header = 0; base_url = 0; keepalive = 0; 
+    response_header = 0; ftp_data_fd = 0; 
+    reset(); 
+  }
+};
+
+FD *fd = NULL;
+
+void FD::close() {
+  if (verbose) 
+    printf("close: %d\n", fd);
+  ::close(fd);
+  if (is_done())
+    done();
+  keepalive = 0;
+  ip = 0;
+  if (count) (*count)--;
+  if (count == &clients)
+    current_clients--;
+  reset();
+  if (urls_mode) undefer_url();
+  ftp_data_fd = 0;
+}
+
+#ifdef _WIN32
+/*---------------------------------------------------------------------------*\
+  $Id: poll.h,v 1.1 1997/06/21 17:05:39 bmc Exp bmc $
+
+  NAME
+
+	poll - select(2)-based poll() emulation function for BSD systems.
+
+  SYNOPSIS
+	#include "poll.h"
+
+	struct pollfd
+	{
+	    int     fd;
+	    short   events;
+	    short   revents;
+	}
+
+	int poll (struct pollfd *pArray, unsigned long n_fds, int timeout)
+
+  DESCRIPTION
+
+	This file, and the accompanying "poll.c", implement the System V
+	poll(2) system call for BSD systems (which typically do not provide
+	poll()).  Poll() provides a method for multiplexing input and output
+	on multiple open file descriptors; in traditional BSD systems, that
+	capability is provided by select().  While the semantics of select()
+	differ from those of poll(), poll() can be readily emulated in terms
+	of select() -- which is how this function is implemented.
+
+  AUTHOR
+	Brian M. Clapper
+	bmc@WillsCreek.COM
+
+  REFERENCES
+	Stevens, W. Richard. Unix Network Programming.  Prentice-Hall, 1990.
+
+  This software is copyright (c) 1994-1997 by Brian M. Clapper
+  <bm...@WillsCreek.COM>.  See the LICENSE file accompanying the source
+  distribution for details.
+\*---------------------------------------------------------------------------*/
+
+#ifndef _POLL_EMUL_H_
+#define _POLL_EMUL_H_
+
+#define POLLIN		0x01
+#define POLLPRI		0x02
+#define POLLOUT		0x04
+#define POLLERR		0x08
+#define POLLHUP		0x10
+#define POLLNVAL	0x20
+
+struct pollfd
+{
+    int     fd;
+    short   events;
+    short   revents;
+};
+
+#if __STDC__ > 0
+extern int poll (struct pollfd *pArray, unsigned long n_fds, int timeout);
+#else
+extern int poll();
+#endif;
+
+#endif /* _POLL_EMUL_H_ */
+
+
+/*---------------------------------------------------------------------------*\
+  $Id: poll.c,v 1.1 1997/06/21 17:05:34 bmc Exp bmc $
+
+  NAME
+
+	poll - select(2)-based poll() emulation function for BSD systems.
+
+  SYNOPSIS
+	#include "poll.h"
+
+	struct pollfd
+	{
+	    int     fd;
+	    short   events;
+	    short   revents;
+	}
+
+	int poll (struct pollfd *pArray, unsigned long n_fds, int timeout)
+
+  DESCRIPTION
+
+	This file, and the accompanying "poll.h", implement the System V
+	poll(2) system call for BSD systems (which typically do not provide
+	poll()).  Poll() provides a method for multiplexing input and output
+	on multiple open file descriptors; in traditional BSD systems, that
+	capability is provided by select().  While the semantics of select()
+	differ from those of poll(), poll() can be readily emulated in terms
+	of select() -- which is how this function is implemented.
+
+  AUTHOR
+	Brian M. Clapper
+	bmc@WillsCreek.COM
+
+  REFERENCES
+	Stevens, W. Richard. Unix Network Programming.  Prentice-Hall, 1990.
+
+  This software is copyright (c) 1994-1997 by Brian M. Clapper
+  <bm...@WillsCreek.COM>.  See the LICENSE file accompanying the source
+  distribution for details.
+\*---------------------------------------------------------------------------*/
+
+
+/*---------------------------------------------------------------------------*\
+				 Includes
+\*---------------------------------------------------------------------------*/
+
+#include <unistd.h>			     /* standard Unix definitions */
+#include <sys/types.h>                       /* system types */
+#include <sys/time.h>                        /* time definitions */
+#include <assert.h>                          /* assertion macros */
+#include "poll.h"                            /* this package */
+
+/*---------------------------------------------------------------------------*\
+				  Macros
+\*---------------------------------------------------------------------------*/
+
+#define MAX(a,b)	((a) > (b) ? (a) : (b))
+
+
+/*---------------------------------------------------------------------------*\
+			     Private Functions
+\*---------------------------------------------------------------------------*/
+
+static int map_poll_spec
+#if __STDC__ > 0
+			 (struct pollfd *pArray,
+			  unsigned long  n_fds,
+			  fd_set        *pReadSet,
+			  fd_set        *pWriteSet,
+			  fd_set        *pExceptSet)
+#else
+			 (pArray, n_fds, pReadSet, pWriteSet, pExceptSet)
+			  struct pollfd *pArray;
+			  unsigned long  n_fds;
+			  fd_set        *pReadSet;
+			  fd_set        *pWriteSet;
+			  fd_set        *pExceptSet;
+#endif
+{
+    register unsigned long  i;                   /* loop control */
+    register struct	    pollfd *pCur;        /* current array element */
+    register int	    max_fd = 0;          /* return value */
+
+    /*
+       Map the poll() structures into the file descriptor sets required
+       by select().
+    */
+    for (i = 0, pCur = pArray; i < n_fds; i++, pCur++)
+    {
+	if (pCur->events & POLLIN)
+	{
+	    /* "Input Ready" notification desired. */
+	    FD_SET (pCur->fd, pReadSet);
+	}
+
+	if (pCur->events & POLLOUT)
+	{
+	    /* "Output Possible" notification desired. */
+	    FD_SET (pCur->fd, pWriteSet);
+	}
+
+	if (pCur->events & POLLPRI)
+	{
+	    /*
+	       "Exception Occurred" notification desired.  (Exceptions
+	       include out of band data.
+	    */
+	    FD_SET (pCur->fd, pExceptSet);
+	}
+
+	max_fd = MAX (max_fd, pCur->fd);
+    }
+
+    return max_fd;
+}
+
+static struct timeval *map_timeout
+#if __STDC__ > 0
+			(int poll_timeout, struct timeval *pSelTimeout)
+#else
+			(poll_timeout, pSelTimeout)
+			 int             poll_timeout;
+			 struct timeval *pSelTimeout;
+#endif
+{
+    struct timeval *pResult;
+
+    /*
+      The following logic maps the poll() timeout value into a select()
+      timeout.  The possible values of the poll() timeout value, and their
+      meanings, are:
+
+      VALUE	MEANING
+
+      -1	wait indefinitely (until signal occurs)
+       0	return immediately, don't block
+      >0	wait specified number of milliseconds
+
+       select() uses a "struct timeval", which specifies the timeout in
+       seconds and microseconds, so the milliseconds value has to be mapped
+       accordingly.
+    */
+    assert (pSelTimeout != (struct timeval *) NULL);
+    switch (poll_timeout)
+    {
+	case -1:
+	    /*
+	       A NULL timeout structure tells select() to wait indefinitely.
+	    */
+	    pResult = (struct timeval *) NULL;
+	    break;
+
+	case 0:
+	    /*
+	       "Return immediately" (test) is specified by all zeros in
+	       a timeval structure.
+	    */
+	    pSelTimeout->tv_sec  = 0;
+	    pSelTimeout->tv_usec = 0;
+	    pResult = pSelTimeout;
+	    break;
+
+	default:
+	    /* Wait the specified number of milliseconds. */
+	    pSelTimeout->tv_sec  = poll_timeout / 1000; /* get seconds */
+	    poll_timeout        %= 1000;                /* remove seconds */
+	    pSelTimeout->tv_usec = poll_timeout * 1000; /* get microseconds */
+	    pResult = pSelTimeout;
+	    break;
+    }
+
+
+    return pResult;
+}
+
+static void map_select_results
+#if __STDC__ > 0
+			 (struct pollfd *pArray,
+			  unsigned long  n_fds,
+			  fd_set        *pReadSet,
+			  fd_set        *pWriteSet,
+			  fd_set        *pExceptSet)
+#else
+			 (pArray, n_fds, pReadSet, pWriteSet, pExceptSet)
+			  struct pollfd *pArray;
+			  unsigned long  n_fds;
+			  fd_set        *pReadSet;
+			  fd_set        *pWriteSet;
+			  fd_set        *pExceptSet;
+#endif
+{
+    register unsigned long  i;                   /* loop control */
+    register struct	    pollfd *pCur;        /* current array element */
+
+    for (i = 0, pCur = pArray; i < n_fds; i++, pCur++)
+    {
+	/* Exception events take priority over input events. */
+
+	pCur->revents = 0;
+	if (FD_ISSET (pCur->fd, pExceptSet))
+	    pCur->revents |= POLLPRI;
+
+	else if (FD_ISSET (pCur->fd, pReadSet))
+	    pCur->revents |= POLLIN;
+
+	if (FD_ISSET (pCur->fd, pWriteSet))
+	    pCur->revents |= POLLOUT;
+    }
+
+    return;
+}
+
+/*---------------------------------------------------------------------------*\
+			     Public Functions
+\*---------------------------------------------------------------------------*/
+
+int poll
+
+#if __STDC__ > 0
+	(struct pollfd *pArray, unsigned long n_fds, int timeout)
+#else
+	(pArray, n_fds, timeout)
+	 struct	       pollfd *pArray;
+	 unsigned long n_fds;
+	 int	       timeout;
+#endif
+
+{
+    fd_set  read_descs;                          /* input file descs */
+    fd_set  write_descs;                         /* output file descs */
+    fd_set  except_descs;                        /* exception descs */
+    struct  timeval stime;                       /* select() timeout value */
+    int	    ready_descriptors;                   /* function result */
+    int	    max_fd;                              /* maximum fd value */
+    struct  timeval *pTimeout;                   /* actually passed */
+
+    FD_ZERO (&read_descs);
+    FD_ZERO (&write_descs);
+    FD_ZERO (&except_descs);
+
+    assert (pArray != (struct pollfd *) NULL);
+
+    /* Map the poll() file descriptor list in the select() data structures. */
+
+    max_fd = map_poll_spec (pArray, n_fds,
+			    &read_descs, &write_descs, &except_descs);
+
+    /* Map the poll() timeout value in the select() timeout structure. */
+
+    pTimeout = map_timeout (timeout, &stime);
+
+    /* Make the select() call. */
+
+    ready_descriptors = select (max_fd + 1, &read_descs, &write_descs,
+				&except_descs, pTimeout);
+
+    if (ready_descriptors >= 0)
+    {
+	map_select_results (pArray, n_fds,
+			    &read_descs, &write_descs, &except_descs);
+    }
+
+    return ready_descriptors;
+}
+
+#endif
+
+// Library functions from libts
+
+const char * SPACES = "                                                                               ";
+int ink_code_md5(unsigned char *input, int input_length,
+		 unsigned char *sixteen_byte_hash_pointer);
+
+class           ParseRules
+{
+	public:
+	ParseRules();
+	enum {
+		CHAR_SP = 32,
+		CHAR_HT = 9,
+		CHAR_LF = 10,
+		CHAR_VT = 11,
+		CHAR_NP = 12,
+		CHAR_CR = 13
+	};
+	static int      is_char(char c);
+	static int      is_upalpha(char c);
+	static int      is_loalpha(char c);
+	static int      is_alpha(char c);
+	static int      is_digit(char c);
+	static int      is_ctl(char c);
+	static int      is_hex(char c);
+	static int      is_ws(char c);
+	static int      is_cr(char c);
+	static int      is_lf(char c);
+	static int      is_spcr(char c);
+	static int      is_splf(char c);
+	static int      is_wslfcr(char c);
+	static int      is_tspecials(char c);
+	static int      is_token(char c);
+	static int      is_extra(char c);
+	static int      is_safe(char c);
+	static int      is_unsafe(char c);
+	static int      is_national(char c);
+	static int      is_reserved(char c);
+	static int      is_unreserved(char c);
+	static int      is_punct(char c);
+	static int      is_end_of_url(char c);
+	static int      is_eow(char c);
+	static int      is_wildmat(char c);
+	static int      is_sep(char c);
+	static int      is_empty(char c);
+	static int      is_alnum(char c);
+	static int      is_space(char c);
+	static int      is_control(char c);
+	static int      is_mime_sep(char c);
+	static int      is_http_field_name(char c);
+	static int      is_http_field_value(char c);
+	static int      is_escape(const char *seq);
+	static int      is_uchar(const char *seq);
+	static int      is_pchar(const char *seq);
+	static int      strncasecmp_eow(const char *s1, const char *s2, int n);
+	static const char *strcasestr(const char *s1, const char *s2);
+	static int      strlen_eow(const char *s);
+	static const char *strstr_eow(const char *s1, const char *s2);
+	static char     ink_toupper(char c);
+	static char     ink_tolower(char c);
+	static const char *memchr(const char *s, char c, int max_length);
+	static const char *strchr(const char *s, char c);
+	                private:
+	                ParseRules(const ParseRules &);
+	                ParseRules & operator = (const ParseRules &);
+};
+
+extern const unsigned int parseRulesCType[];
+extern const char parseRulesCTypeToUpper[];
+extern const char parseRulesCTypeToLower[];
+
+inline int ParseRules::is_char(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 0)) ? (1) : (0));
+}
+inline int ParseRules::is_upalpha(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 1)) ? (1) : (0));
+}
+inline int ParseRules::is_loalpha(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 2)) ? (1) : (0));
+}
+inline int ParseRules::is_alpha(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 3)) ? (1) : (0));
+}
+inline int ParseRules::is_digit(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 4)) ? (1) : (0));
+}
+inline int ParseRules::is_alnum(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 26)) ? (1) : (0));
+}
+inline int ParseRules::is_ctl(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 5)) ? (1) : (0));
+}
+inline int ParseRules::is_ws(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 6)) ? (1) : (0));
+}
+inline int ParseRules::is_hex(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 7)) ? (1) : (0));
+}
+inline int ParseRules::is_cr(char c) {
+  return (c == CHAR_CR);
+}
+inline int ParseRules::is_lf(char c) {
+  return (c == CHAR_LF);
+}
+inline int ParseRules::is_splf(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 19)) ? (1) : (0));
+}
+inline int ParseRules::is_spcr(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 18)) ? (1) : (0));
+}
+inline int ParseRules::is_wslfcr(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 20)) ? (1) : (0));
+}
+inline int ParseRules::is_extra(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 9)) ? (1) : (0));
+}
+inline int ParseRules::is_safe(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 10)) ? (1) : (0));
+}
+inline int ParseRules::is_unsafe(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 11)) ? (1) : (0));
+}
+inline int ParseRules::is_reserved(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 13)) ? (1) : (0));
+}
+inline int ParseRules::is_national(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 12)) ? (1) : (0));
+}
+inline int ParseRules::is_unreserved(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 14)) ? (1) : (0));
+}
+inline int ParseRules::is_punct(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 15)) ? (1) : (0));
+}
+inline int ParseRules::is_end_of_url(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 16)) ? (1) : (0));
+}
+inline int ParseRules::is_escape(const char *seq) {
+  return (seq[0] == '%' && is_hex(seq[1]) && is_hex(seq[2]));
+}
+inline int ParseRules::is_uchar(const char *seq) {
+  return (is_unreserved(seq[0]) || is_escape(seq));
+}
+inline int ParseRules::is_pchar(const char *seq) {
+  if (*seq != '%')
+    return ((parseRulesCType[*seq] & (1 << 8)) ? (1) : (0));
+  else
+    return is_hex(seq[1]) && is_hex(seq[2]);
+}
+inline int ParseRules::is_tspecials(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 17)) ? (1) : (0));
+}
+inline int ParseRules::is_token(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 22)) ? (1) : (0));
+}
+inline char ParseRules::ink_toupper(char c) {
+  return parseRulesCTypeToUpper[(unsigned char) c];
+}
+inline char ParseRules::ink_tolower(char c) {
+  return parseRulesCTypeToLower[(unsigned char) c];
+}
+inline int ParseRules::is_eow(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 21)) ? (1) : (0));
+}
+inline int ParseRules::is_wildmat(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 23)) ? (1) : (0));
+}
+inline int ParseRules::is_sep(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 24)) ? (1) : (0));
+}
+inline int ParseRules::is_empty(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 25)) ? (1) : (0));
+}
+inline int ParseRules::is_space(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 27)) ? (1) : (0));
+}
+inline int ParseRules::is_control(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 28)) ? (1) : (0));
+}
+inline int ParseRules::is_mime_sep(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 29)) ? (1) : (0));
+}
+inline int ParseRules::is_http_field_name(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (1 << 30)) ? (1) : (0));
+}
+inline int ParseRules::is_http_field_value(char c) {
+  return ((parseRulesCType[(unsigned char) c] & (((unsigned int)1) << 31)) ? (1) : (0));
+}
+inline int ParseRules::strncasecmp_eow(
+  const char *s1, const char *s2, int count)
+{
+  for (int i = 0; i < count; i++) {
+    const char &a = s1[i];
+    const char &b = s2[i];
+    if (ink_tolower(a) != ink_tolower(b))
+      return (is_eow(a) && is_eow(b));
+  }
+  return (1);
+}
+inline int ParseRules::strlen_eow(const char *s){
+  for (int i = 0; 1; i++) {
+    if (is_eow(s[i]))
+      return (i);
+  }
+}
+inline const char *ParseRules::strstr_eow(const char *s1, const char *s2) {
+  int i1;
+  int s2_len = strlen_eow(s2);
+  for (i1 = 0; !is_eow(s1[i1]); i1++)
+    if (ink_tolower(s1[i1]) == ink_tolower(s2[0]))
+      if (strncasecmp_eow(&s1[i1], &s2[0], s2_len))
+	return (&s1[i1]);
+  return (0);
+}
+inline const char *ParseRules::
+strcasestr(const char *s1, const char *s2) {
+  int i1;
+  int s2_len = strlen(s2);
+  for (i1 = 0; s1[i1] != '\0'; i1++)
+    if (ink_tolower(s1[i1]) == ink_tolower(s2[0]))
+      if (strncasecmp_eow(&s1[i1], &s2[0], s2_len))
+	return (&s1[i1]);
+  return (0);
+}
+inline const char *ParseRules::memchr(const char *s, char c, int max_length) {
+  for (int i = 0; i < max_length; i++)
+    if (s[i] == c)
+      return (&s[i]);
+  return (0);
+}
+inline const char *ParseRules::strchr(const char *s, char c) {
+  for (int i = 0; s[i] != '\0'; i++)
+    if (s[i] == c)
+      return (&s[i]);
+  return (0);
+}
+inline int
+ink_atoi(const char *str) {
+  int num = 0;
+  int negative = 0;
+  while (*str && ParseRules::is_wslfcr(*str))
+    str += 1;
+  if (*str == '-') {
+    negative = 1;
+    str += 1;
+  }
+  while (*str && ParseRules::is_digit(*str))
+    num = (num * 10) - (*str++ - '0');
+  if (!negative)
+    num = -num;
+  return num;
+}
+inline unsigned int
+ink_atoui(const char *str) {
+  unsigned int num = 0;
+  while (*str && ParseRules::is_wslfcr(*str))
+    str += 1;
+  while (*str && ParseRules::is_digit(*str))
+    num = (num * 10) + (*str++ - '0');
+  return num;
+}
+inline ink64 
+ink_atoll(const char *str) {
+  ink64 num = 0;
+  int negative = 0;
+  while (*str && ParseRules::is_wslfcr(*str))
+    str += 1;
+  if (*str == '-') {
+    negative = 1;
+    str += 1;
+  }
+  while (*str && ParseRules::is_digit(*str))
+    num = (num * 10) - (*str++ - '0');
+  if (!negative)
+    num = -num;
+  return num;
+}
+
+#define HRTIME_FOREVER  (10*HRTIME_DECADE)
+#define HRTIME_DECADE   (10*HRTIME_YEAR)
+#define HRTIME_YEAR     (365*HRTIME_DAY+HRTIME_DAY/4)
+#define HRTIME_WEEK     (7*HRTIME_DAY)
+#define HRTIME_DAY      (24*HRTIME_HOUR)
+#define HRTIME_HOUR     (60*HRTIME_MINUTE)
+#define HRTIME_MINUTE   (60*HRTIME_SECOND)
+#define HRTIME_SECOND   (1000*HRTIME_MSECOND)
+#define HRTIME_MSECOND  (1000*HRTIME_USECOND)
+#define HRTIME_USECOND  (1000*HRTIME_NSECOND)
+#if !defined (_WIN32)
+#define HRTIME_NSECOND	(1LL)
+#else
+#define HRTIME_NSECOND	(1i64)
+#endif
+
+#define MAX_FILE_ARGUMENTS 100
+
+char * file_arguments[MAX_FILE_ARGUMENTS] = { 0 };
+char * program_name = (char *)"inktomi";
+int n_file_arguments = 0;
+
+static char * argument_types_keys = (char *)"ISDfFTL";
+static char * argument_types_descriptions[] = {
+  (char *)"int  ",
+  (char *)"str  ",
+  (char *)"dbl  ",
+  (char *)"off  ",
+  (char *)"on   ",
+  (char *)"tog  ",
+  (char *)"i64  ",
+  (char *)"     "
+};
+
+void show_argument_configuration(ArgumentDescription * argument_descriptions,
+				 int n_argument_descriptions);
+
+void usage(ArgumentDescription * argument_descriptions,
+	   int n_argument_descriptions,
+	   char * arg_unused);
+
+void process_args(ArgumentDescription * argument_descriptions,
+		  int n_argument_descriptions,
+		  char **argv);
+
+#ifdef RELEASE
+#define ink_assert(EX) (void)(EX)
+#define ink_debug_assert(EX)
+#else
+#define ink_assert(EX) (\
+            void)((EX) || (_ink_assert(#EX, __FILE__, __LINE__)))
+#define ink_debug_assert(EX) \
+            (void)((EX) || (_ink_assert(#EX, __FILE__, __LINE__)))
+#endif
+
+#define ink_release_assert(EX) \
+            (void)((EX) || (_ink_assert(#EX, __FILE__, __LINE__)))
+
+int _ink_assert(const char * a, const char * f, int l);
+void ink_fatal(int return_code, const char *message_format, ...);
+void ink_warning(const char *message_format, ...);
+#define min(_a,_b) ((_a)<(_b)?(_a):(_b))
+
+inline ink_hrtime ink_get_hrtime()
+{
+#if !defined (_WIN32)
+#  if !defined (sparc)
+#    if defined (irix)
+      timespec ts;
+      clock_gettime(CLOCK_SGI_CYCLE, &ts);
+      return (ts.tv_sec * HRTIME_SECOND + ts.tv_nsec * HRTIME_NSECOND);
+#    elif defined(FreeBSD)
+      timespec ts;
+      clock_gettime(CLOCK_REALTIME, &ts);
+      return (ts.tv_sec * HRTIME_SECOND + ts.tv_nsec * HRTIME_NSECOND);
+#    else /* !defined (__alpha) && !defined(IRIX) */
+      timeval tv;
+      gettimeofday(&tv,NULL);
+      return (tv.tv_sec * HRTIME_SECOND + tv.tv_usec * HRTIME_USECOND);
+#    endif
+#  else /* !defined (NEED_HRTIME) */
+      return gethrtime();
+#  endif
+#else /* WIN32 */
+    return ink_getHiResTime();
+#endif
+}
+
+typedef struct {
+  char sche[MAX_URL_LEN + 1];
+  char host[MAX_URL_LEN + 1];
+  char port[MAX_URL_LEN + 1];
+  char path[MAX_URL_LEN + 1];
+  char frag[MAX_URL_LEN + 1];
+  char quer[MAX_URL_LEN + 1];
+  char para[MAX_URL_LEN + 1];
+
+  int sche_exists;
+  int host_exists;
+  int port_exists;
+  int path_exists;
+  int frag_exists;
+  int quer_exists;
+  int para_exists;
+
+  int rel_url;
+  int leading_slash;
+  int is_path_name;
+} InkWebURLComponents;
+
+static  int ink_web_remove_dots(
+  char *src, char *dest, int *leadingslash, int max_dest_len);
+
+static int ink_web_unescapify_string(
+  char *dest_in, char *src_in, int max_dest_len);
+
+int ink_web_escapify_string(char *dest_in, char *src_in, int max_dest_len);
+
+static void ink_web_decompose_url(
+  char *src_url,
+  char *sche, char *host, char *port, char *path,
+  char *frag, char *quer, char *para,
+  int *real_sche_exists, int *real_host_exists,
+  int *real_port_exists, int *real_path_exists, 
+  int *real_frag_exists, int *real_quer_exists,
+  int *real_para_exists,
+  int *real_relative_url, int *real_leading_slash);
+
+static void ink_web_canonicalize_url(char *base_url, char *emb_url, 
+				     char *dest_url, int max_dest_url_len);
+
+static void ink_web_decompose_url_into_structure(char *url, 
+						 InkWebURLComponents *c);
+
+static void remove_last_seg(char *src, char *dest)
+{ 
+  char *ptr;
+  for (ptr = src + strlen(src) - 1; ptr >= src; ptr--)
+    if (*ptr == '/') break;
+  while (src <= ptr) *dest++ = *src++;
+  *dest = '\0';
+}
+
+static inline void remove_multiple_slash(char *src, char *dest)
+{ 
+  char *ptr=NULL;
+  
+  for (ptr = src; *ptr;) {
+      *(dest++) = *ptr;
+      if (*ptr == '/')  {
+	while ((*ptr== '/') && *ptr) {
+	  ptr++;
+	}
+      } else {
+	ptr++;
+      }
+    }
+  *dest = '\0';
+}
+
+static inline void append_string(char *dest, char *src, int *offset_ptr, 
+				 int max_len)
+{
+  int num = strlen(src);
+  if (*offset_ptr + num > max_len)
+    num = max_len-*offset_ptr;
+  strncpy(dest+*offset_ptr, src, num+1);
+  (*offset_ptr) += num;
+}
+
+// End Library functions
+
+void show_version() {
+  char b[] = VERSION_NUM;
+  char * v = strchr(b,':');
+  v += 2;
+  *strchr(v,'$') = 0;
+  printf(VERSION, v);
+}
+
+void jtest_usage(ArgumentDescription * argument_descriptions, 
+		 int n_argument_descriptions, char * arg) 
+{
+  show_version();
+  usage(argument_descriptions, n_argument_descriptions, arg);
+}
+
+static void panic(char * s) {
+  fprintf(stderr, s);
+  exit(1);
+}
+
+static void panic_perror(char * s) {
+  perror(s);
+  exit(1);
+}
+
+int max_limit_fd() {
+#ifdef __alpha
+#ifdef GSI_FD_NEWMAX
+  assert(!setsysinfo(SSI_FD_NEWMAX,0,0,0,1));
+#endif
+#endif
+
+  struct rlimit rl;
+  if (getrlimit(RLIMIT_NOFILE,&rl) >= 0) {
+    rl.rlim_cur = rl.rlim_max;
+    if (setrlimit(RLIMIT_NOFILE,&rl) >= 0) {
+      if (getrlimit(RLIMIT_NOFILE,&rl) >= 0) {
+	return rl.rlim_cur;
+      }
+    }
+  }
+  panic("couldn't set RLIMIT_NOFILE\n");
+  return -1;
+}
+
+int read_ready(int fd) {
+#if !defined (_WIN32)
+  struct pollfd p;
+  p.events = POLLIN;
+  p.fd = fd;
+  int r = poll(&p,1,0);
+  if (r<=0) return r;
+  if (p.revents & (POLLERR|POLLNVAL)) return -1;
+  if (p.revents & (POLLIN|POLLHUP)) return 1;
+#else /* WIN32 */
+  return 1;
+#endif
+  return 0;
+}
+
+static void poll_init(int sock) { 
+  if (!fd[sock].req_header) 
+    fd[sock].req_header = (char*)malloc(HEADER_SIZE * pipeline);
+  if (!fd[sock].response_header)
+    fd[sock].response_header = (char*)malloc(HEADER_SIZE);
+  if (!fd[sock].base_url)
+    fd[sock].base_url = (char*)malloc(HEADER_SIZE);
+  fd[sock].reset(); 
+}
+
+static void poll_set(int sock, poll_cb read_cb, poll_cb write_cb = NULL) {
+  if (verbose) printf("adding poll %d\n", sock);
+  fd[sock].fd = sock;
+  fd[sock].read_cb = read_cb;
+  fd[sock].write_cb = write_cb;
+  if (last_fd < sock)
+    last_fd = sock;
+}
+
+static void poll_init_set(int sock, poll_cb read_cb, poll_cb write_cb = NULL) {
+  poll_init(sock);
+  poll_set(sock, read_cb, write_cb);
+}
+
+static int fast(int sock, int speed, int d) {
+  if (!speed) return 0;
+  ink64 t = now - fd[sock].start + 1;
+  int target = (int)(((t / HRTIME_MSECOND) * speed) / 1000);
+  int delta = d - target;
+  if (delta > 0) {
+    int mwait = (delta * 1000) / speed;
+    fd[sock].ready = now + (mwait * HRTIME_MSECOND);
+    return 1;
+  } else
+    fd[sock].ready = now;
+  return 0;
+}
+
+static int faster_than(int sock, int speed, int d) {
+  if (!speed) return 1;
+  ink64 t = now - fd[sock].start + 1;
+  int target = (int)(((t / HRTIME_MSECOND) * speed) / 1000);
+  int delta = d - target;
+  if (delta > 0)
+    return 1;
+  return 0;
+}
+
+static void get_path_from_req(char * buf,char ** purl_start, char ** purl_end)
+{
+  char * url_start = buf;
+  char * url_end = NULL;
+  if (!strncasecmp(url_start, "GET ", sizeof("GET ")-1)) {
+    url_start += sizeof("GET ")-1;
+    url_end = (char*)memchr(url_start,' ',70);
+  } else
+    url_end = (char*)memchr(url_start,0,70);
+  if (!url_end) panic("malformed request\n");
+  if (url_end - url_start > 10)
+    if (!strncasecmp(url_start, "http://", sizeof("http://")-1)) {
+      url_start += sizeof("http://")-1;
+      url_start = (char*)memchr(url_start,'/',70);
+    }
+  *purl_start = url_start;  
+  *purl_end = url_end;
+}
+
+static int send_response (int sock) {
+  int err, towrite;
+
+  if (fd[sock].req_pos >= 0) {
+    char header[1024], *url_end = NULL, *url_start = NULL;
+    int url_len = 0;
+    char* content_type;
+    switch (server_content_type) {
+    case 1: content_type = "text/html"; break;
+    case 2: content_type = "image/jpeg"; break;
+    default:
+      content_type = 
+	(char*)((compd_suite || alternates) ? "image/jpeg" : "text/html");
+      if (only_server && strstr(fd[sock].req_header, "Cookie:"))
+	content_type = "image/jpeg";
+    }
+    if (!ftp && embed_url) {
+      get_path_from_req(fd[sock].req_header, &url_start, &url_end);
+      *url_end = 0;
+      url_len = url_end - url_start;
+    }
+    int print_len = 0;
+    if (!ftp) {
+      if (fd[sock].ims) {
+	print_len = sprintf(
+	  header,"HTTP/1.0 304 Not-Modified\r\n"
+	  "Content-Type: %s\r\n"
+	  "Last-Modified: Mon, 05 Oct 1998 01:00:00 GMT\r\n"
+	  "%s"
+	  "\r\n",
+	  content_type,
+	  fd[sock].keepalive>0?"Connection: Keep-Alive\r\n":"");
+	url_len = 0;
+      } else
+	print_len = sprintf(
+	  header,"HTTP/1.0 200 OK\r\n"
+	  "Content-Type: %s\r\n"
+	  "Last-Modified: Mon, 05 Oct 1998 01:00:00 GMT\r\n"
+	  "%s"
+	  "Content-Length: %d\r\n"
+	  "%s"
+	  "\r\n%s",
+	  content_type,
+	  fd[sock].keepalive>0?"Connection: Keep-Alive\r\n":"",
+	  fd[sock].response_length,
+	  no_cache?"Pragma: no-cache\r\nCache-Control: no-cache\r\n":"",
+	  url_start ? url_start : "");
+    } else
+      url_len = print_len = 
+	sprintf(header, "ftp://%s:%d/%12.10f/%d", 
+		local_host, server_port,
+		fd[sock].doc, fd[sock].length);
+    if (show_headers) printf("Response to Proxy: {\n%s}\n", header);
+    int len = print_len - fd[sock].req_pos;
+    ink_assert(len>0);
+    do {
+      err = write(sock, header + fd[sock].req_pos, len);
+    } while ((err == -1) && (errno == EINTR));
+    if (err <= 0) {
+      if (!err) return -1;
+      if (errno == EAGAIN || errno == ENOTCONN) return 0;
+      return -1;
+    }
+    if (verbose) printf("wrote %d %d\n", sock, err);
+    new_tbytes += err;
+    fd[sock].req_pos += err;
+    fd[sock].bytes += err;
+    if (fd[sock].req_pos >= len)
+      fd[sock].req_pos = -1;
+    else
+      return 0;
+    fd[sock].response += url_len;
+    fd[sock].length -= url_len;
+    total_server_response_header_bytes += print_len - url_len;
+    total_server_response_body_bytes += url_len;
+  }
+
+  /* then the response */
+  towrite = server_speed?server_speed:MAX_RESPONSE_LENGTH;
+  if (fd[sock].length < towrite)
+    towrite = fd[sock].length;
+  if (towrite > 0) {
+    if (fast(sock,server_speed,fd[sock].bytes)) 
+      return 0;
+    do {
+      err = write (sock, fd[sock].response, towrite);
+    } while ((err == -1) && (errno == EINTR));
+    if (err < 0) {
+      if (errno == EAGAIN || errno == ENOTCONN) return 0;
+      fprintf(stderr,"write errno %d length %d sock %d\n",errno,towrite,sock);
+      errors++;
+      return -1;
+    }
+    if (verbose) printf("wrote %d %d\n", sock, err);
+
+    new_tbytes += err;
+    total_server_response_body_bytes += err;
+    fd[sock].response += err;
+    fd[sock].length -= err;
+    fd[sock].bytes += err;
+  }
+  
+  if (fast(sock,server_speed,fd[sock].bytes)) 
+    return 0;
+  if (fd[sock].length <= 0 || !err) {
+    if (fd[sock].response) 
+      new_sops++;
+    if (verbose) printf("write %d done\n", sock);
+    if (fd[sock].keepalive > 0 && !ftp) {
+      poll_init_set(sock, read_request);
+      fd[sock].start = now;
+      fd[sock].ready = now + server_delay * HRTIME_MSECOND;
+      return 0;
+    }
+    return 1;
+  }
+    
+  return 0;
+}
+
+char * strncasestr (char *s, char * find, int len) {
+  int findlen = strlen(find);
+  char * e = s + len;
+  while (1) {
+    char * x = (char*)memchr(s,*find,e - s);
+    if (!x) { 
+      if (ParseRules::is_upalpha(*find))
+	x = (char*)memchr(s,ParseRules::ink_tolower(*find),e - s);
+      else
+	x = (char*)memchr(s,ParseRules::ink_toupper(*find),e - s);
+      if (!x) break;
+    }
+    if (!strncasecmp(find, x, findlen)) return x;
+    s = x + 1;
+  }
+  return NULL;
+}
+
+static char * check_keepalive(char * r, int length) {
+  char * ka = strncasestr(r,"Connection:", length);
+  if (ka) {
+    int l = length - (ka - r);
+    char * e = (char*)memchr(ka,'\n',l);
+    if (!e) e = (char*)memchr(ka,'\r',l);
+    if (strncasestr(ka,"close",e-ka)) 
+      return NULL;
+  }
+  return ka;
+}
+
+static int check_alt(char * r, int length) {
+  char * s = strncasestr(r, "Cookie:", length);
+  if (!s) {
+    s = strncasestr(r, "User-Agent:", length);
+    if (s) s += sizeof("User-Agent:");
+  } else
+    s += sizeof("Cookie:");
+  if (s) {
+    int l = length - (s - r);
+    char * e = (char*)memchr(s, '\n', l);
+    if (!e) e = (char*)memchr(s, '\r', l);
+    if (!(s = strncasestr(s, "jtest", e - s))) 
+      return NULL;
+    s = (char*)memchr(s, '-', l);
+    if (!s)
+      return NULL;
+    s = (char*)memchr(s + 1, '-', l);
+    if (!s)
+      return NULL;
+    return ink_atoi(s + 1);
+  }
+  return 0;
+}
+
+static void make_response(int sock, int code) {
+  fd[sock].response = fd[sock].req_header;
+  fd[sock].length = sprintf( fd[sock].req_header, "%d\r\n", code);
+  fd[sock].req_pos = 0;
+  fd[sock].response_length = strlen(fd[sock].req_header);
+  poll_set(sock, NULL, write_ftp_response);
+}
+
+static void make_long_response(int sock) {
+  fd[sock].response = fd[sock].req_header;
+  fd[sock].req_pos = 0;
+  fd[sock].response_length = strlen(fd[sock].req_header);
+  poll_set(sock, NULL, write_ftp_response);
+}
+
+static int send_ftp_data_when_ready(int sock) {
+  if (fd[sock].state == STATE_FTP_DATA_READY && fd[sock].doc_length) {
+    fd[sock].response = fd[sock].req_header;
+    fd[sock].response_length = fd[sock].length = fd[sock].doc_length;
+    if (verbose) printf("ftp data %d >-< %d\n", sock, fd[sock].ftp_data_fd);
+    fd[sock].response = response_buffer + fd[sock].doc_length % 256;
+    fd[sock].req_pos = 0;
+    poll_set(sock, NULL, send_response);
+  }
+  return 0;
+}
+
+static int send_ftp_data(int sock, char * start, char * end) {
+  int data_fd = fd[sock].ftp_data_fd;
+  if (sscanf(start,"%d",&fd[data_fd].doc_length) != 1)
+    return -1;
+  fd[data_fd].doc = fd[sock].doc;
+  send_ftp_data_when_ready(data_fd);
+  return 0;
+}
+
+static int read_request(int sock) {
+  if (verbose) printf("read_request %d\n", sock);
+  int err = 0;
+  int i;
+
+  int maxleft = HEADER_SIZE - fd[sock].req_pos - 1;
+
+  do {
+    err = read (sock, &fd[sock].req_header[fd[sock].req_pos], 
+		maxleft);
+  } while ((err < 0) && (errno == EINTR));
+  
+  if (err < 0) {
+    if (errno == EAGAIN || errno == ENOTCONN) return 0;
+    if (fd[sock].req_pos || errno != ECONNRESET)
+      perror ("read");
+    return -1;
+  } else if (err == 0) {
+    if (verbose) printf("eof\n");
+    return -1;
+  } else {
+    if (verbose) printf("read %d got %d\n", sock, err);
+    total_proxy_request_bytes += err;
+    new_tbytes += err;
+    fd[sock].req_pos += err;
+    fd[sock].req_header[fd[sock].req_pos] = 0;
+    char *buffer = fd[sock].req_header;
+    for (i = fd[sock].req_pos - err; 
+	 i < fd[sock].req_pos; i++) 
+    {
+      switch (fd[sock].state) {
+	case 0:
+	  if (buffer[i] == '\r')
+	    fd[sock].state = 1;
+	  else if (buffer[i] == '\n')
+	    fd[sock].state = 2;
+	  break;
+	case 1:
+	  if (buffer[i] == '\n')
+	    fd[sock].state = 2;
+	  else
+	    fd[sock].state = 0;
+	  break;
+	case 2:
+	  if (buffer[i] == '\r')
+	    fd[sock].state = 3;
+	  else if (buffer[i] == '\n') {
+	    fd[sock].state = 3;
+	    goto L3;
+	  } else
+	    fd[sock].state = 0;
+	  break;
+      L3:
+	case 3:
+	  if (buffer[i] == '\n') {
+	    if (show_headers)
+	      printf("Request from Proxy: {\n%s}\n",buffer);
+	    char host[80];
+	    int port, length;
+	    float r;
+	    if (sscanf(buffer,"GET http://%[^:]:%d/%f/%d",
+		       host,&port,&r,&length) == 4) {
+	    } else if (sscanf(buffer,"GET /%f/%d",&r,&length) == 2) {
+	    } else {
+	      if (verbose) printf("misscan: %s\n",buffer);
+	      fd[sock].close();
+	      return 0;
+	    }
+	    if (verbose) 
+	      printf("read_request %d got request %d\n", sock, length);
+	    char * ims = strncasestr(buffer,"If-Modified-Since:", i);
+	    if (drand48() > ims_rate) ims = NULL;
+	    fd[sock].ims = ims?1:0;
+	    if (!ims) {
+	      fd[sock].response_length = fd[sock].length = length;
+	      fd[sock].nalternate = check_alt(fd[sock].req_header, 
+					      strlen(fd[sock].req_header));
+	      fd[sock].response = response_buffer + length % 256 +
+		fd[sock].nalternate;
+	    } else {
+	      fd[sock].nalternate = 0;
+	      if (verbose)
+		printf("sending IMS 304: Not-Modified\n");
+	      fd[sock].response = NULL;
+	      fd[sock].response_length = fd[sock].length = 0;
+	    }
+	    fd[sock].req_pos = 0;
+	    if (!check_keepalive(fd[sock].req_header, 
+				 strlen(fd[sock].req_header)))
+	      fd[sock].keepalive = 0;
+	    else 
+	      fd[sock].keepalive--;
+	    if (fd[sock].length && drand48() < server_abort_rate) {
+	      fd[sock].length = (int)(drand48() * (fd[sock].length -1));
+	      fd[sock].keepalive = 0;
+	    }
+	    poll_set(sock,NULL,send_response);
+	    return 0;
+	  } else
+	    fd[sock].state = 0;
+	  break;
+      }
+    }
+  }
+  return 0;
+}
+
+static int send_compd_response(int sock) {
+  int err = 0;
+
+  struct {
+    unsigned int code;
+    unsigned int len;
+  } compd_header;
+  if (fd[sock].req_pos < sizeof(compd_header)) {
+    compd_header.code = 0;
+    compd_header.len = htonl((fd[sock].length * 2) / 3);
+    do {
+      err = write(sock, (char*)&compd_header + fd[sock].req_pos, 
+		  sizeof(compd_header) - fd[sock].req_pos);
+    } while ((err == -1) && (errno == EINTR));
+    if (err <= 0) {
+      if (!err) {
+	if (verbose_errors) printf("write %d closed early\n", sock);
+	goto Lerror;
+      }
+      if (errno == EAGAIN || errno == ENOTCONN) return 0;
+      perror("write");
+      goto Lerror;
+    }
+    if (verbose) printf("write %d %d\n", sock, err);
+
+    new_tbytes += err;
+    fd[sock].req_pos += err;
+    fd[sock].bytes += err;
+    fd[sock].response = response_buffer + (((fd[sock].length * 2) / 3) % 256);
+  }
+ 
+  if (fd[sock].req_pos < ((fd[sock].length * 2) / 3) + sizeof(compd_header)) {
+    int towrite = cbuffersize;
+    int desired = ((fd[sock].length * 2) / 3) + sizeof (compd_header) - fd[sock].req_pos;
+    if (towrite > desired) {
+      towrite = desired;
+    }
+    if (fast(sock,client_speed,fd[sock].bytes)) return 0;
+    do {
+      err = write (sock, fd[sock].response + fd[sock].req_pos - sizeof(compd_header) , towrite);
+    } while ((err == -1) && (errno == EINTR));
+    if (err < 0) {
+      if (errno == EAGAIN || errno == ENOTCONN) return 0;
+      fprintf(stderr,"write errno %d length %d sock %d\n",errno,towrite,sock);
+      errors++;
+      return -1;
+    }
+    if (verbose) printf("wrote %d %d\n", sock, err);
+
+    new_tbytes += err;
+    total_server_response_body_bytes += err;
+    fd[sock].req_pos += err;
+    fd[sock].bytes += err;
+  }
+
+  if (fd[sock].req_pos >= ((fd[sock].length * 2) / 3) + 4) 
+    return -1;
+  
+  return 0;
+Lerror:
+  errors++;
+  return 1;
+}
+
+static int read_compd_request(int sock) {
+  if (verbose) printf("read_compd_request %d\n", sock);
+  int err = 0;
+  int i;
+
+  if (fd[sock].req_pos < 4) {
+    int maxleft = HEADER_SIZE - fd[sock].req_pos - 1;
+    do {
+      err = read (sock, &fd[sock].req_header[fd[sock].req_pos], 
+		  maxleft);
+    } while ((err < 0) && (errno == EINTR));
+  
+    if (err < 0) {
+      if (errno == EAGAIN || errno == ENOTCONN) return 0;
+      perror ("read");
+      return -1;
+    } else if (err == 0) {
+      if (verbose) printf("eof\n");
+      return -1;
+    } else {
+      if (verbose) printf("read %d got %d\n", sock, err);
+      total_proxy_request_bytes += err;
+      new_tbytes += err;
+      fd[sock].req_pos += err;
+      if (fd[sock].req_pos < 4)
+	return 0;
+      fd[sock].length = ntohl(*(unsigned int*)fd[sock].req_header);
+    }
+  }
+
+  if (fd[sock].req_pos >= fd[sock].length + 4) 
+    goto Lcont;
+  
+  {
+    char buf[MAX_BUFSIZE];
+    int toread = cbuffersize;
+    if (fast(sock,client_speed,fd[sock].bytes)) return 0;
+    do {
+      err = read(sock, buf, toread);
+    } while ((err == -1) && (errno == EINTR));
+    if (err < 0) {
+      if (errno == EAGAIN || errno == ENOTCONN) return 0;
+      if (errno == ECONNRESET) {
+	if (verbose || verbose_errors)
+	  perror ("read");
+	errors++;
+	return -1;
+      }
+      panic_perror("read");
+    }
+    if (!err) {
+      if (verbose || verbose_errors)
+	perror ("read");
+      errors++;
+      return -1;
+    }
+    total_proxy_request_bytes += err;
+    new_tbytes += err;
+    fd[sock].req_pos += err;
+  }
+    
+  if (fd[sock].req_pos >= fd[sock].length + 4) 
+    goto Lcont;
+    
+  return 0;
+
+Lcont:
+  fd[sock].req_pos = 0;
+  fd[sock].keepalive = 0;
+  poll_set(sock,NULL,send_compd_response);
+  return 0;
+}
+
+static int read_ftp_request(int sock) {
+  if (verbose) printf("read_ftp_request %d\n", sock);
+  int err = 0;
+  int i;
+
+  int maxleft = HEADER_SIZE - fd[sock].req_pos - 1;
+
+  do {
+    err = read (sock, &fd[sock].req_header[fd[sock].req_pos], 
+		maxleft);
+  } while ((err < 0) && (errno == EINTR));
+  
+  if (err < 0) {
+    if (errno == EAGAIN || errno == ENOTCONN) return 0;
+    perror ("read");
+    return -1;
+  } else if (err == 0) {
+    if (verbose) printf("eof\n");
+    return -1;
+  } else {
+    if (verbose) printf("read %d got %d\n", sock, err);
+    new_tbytes += err;
+    fd[sock].req_pos += err;
+    fd[sock].req_header[fd[sock].req_pos] = 0;
+    char *buffer = fd[sock].req_header, *n;
+    int res = 0;
+    buffer[fd[sock].req_pos] = 0;
+    if (verbose) printf("buffer [%s]\n", buffer);
+#define STREQ(_x,_s) (!strncasecmp(_x,_s,sizeof(_s)-1))
+    if (STREQ(buffer,"USER")) {
+	res = 331; goto Lhere;
+    } else if (STREQ(buffer,"PASS")) {
+	res = 230; goto Lhere;
+    } else if (STREQ(buffer,"CWD")) {
+      // TS used to send "CWD 1.2110000000..."
+      // TS now sends "CWD /1.2110000000^M\n", so skip 5 instead of 4
+      fd[sock].doc = (buffer[4]=='/') ? atof(buffer + 5) : atof(buffer + 4);
+      res = 250; goto Lhere;
+    } else if (STREQ(buffer,"TYPE")) {
+	res = 200;
+    Lhere:
+	n = (char*)memchr(buffer,'\n',fd[sock].req_pos);
+	if (!n) return 0;
+	make_response(sock,res);
+	return 0;
+    } else if (STREQ(buffer,"SIZE")) {
+	fd[sock].length = 
+	  sprintf(fd[sock].req_header, "213 %d\r\n", atoi(buffer + 5));
+	make_long_response(sock);
+	return 0;
+    } else if (STREQ(buffer,"MDTM")) {
+      double err_rand = 1.0;
+      if (ftp_mdtm_err_rate != 0.0) err_rand = drand48();
+      if (err_rand < ftp_mdtm_err_rate) {
+	fd[sock].length =
+	  sprintf (fd[sock].req_header, "550 mdtm file not found\r\n");
+      } else {
+	if (ftp_mdtm_rate == 0) {
+	  fd[sock].length =
+	    sprintf (fd[sock].req_header, "213 19900615100045\r\n");
+	} else {
+	  time_t mdtm_now;
+	  time(&mdtm_now);
+	  if (mdtm_now-ftp_mdtm_last_update > ftp_mdtm_rate) {
+	    struct tm *mdtm_tm;
+	    ftp_mdtm_last_update = mdtm_now;
+	    mdtm_tm = localtime(&ftp_mdtm_last_update);
+	    sprintf(ftp_mdtm_str, "213 %.4d%.2d%.2d%.2d%.2d%.2d",
+		    mdtm_tm->tm_year + 1900,
+		    mdtm_tm->tm_mon + 1,
+		    mdtm_tm->tm_mday,
+		    mdtm_tm->tm_hour,
+		    mdtm_tm->tm_min,
+		    mdtm_tm->tm_sec);
+	  }
+	  fd[sock].length =
+	    sprintf (fd[sock].req_header, "%s\r\n", ftp_mdtm_str);
+	}
+      }
+      make_long_response(sock);
+      return 0;
+    } else if (STREQ(buffer,"PASV")) {
+	n = (char*)memchr(buffer,'\n',fd[sock].req_pos);
+	if (!n) return 0;
+	if ((fd[sock].ftp_data_fd = open_server(0, accept_ftp_data)) < 0)
+	  panic("could not open ftp data PASV accept port\n");
+	fd[fd[sock].ftp_data_fd].ftp_data_fd = sock;
+	if (verbose) printf("ftp PASV %d <-> %d\n", sock,fd[sock].ftp_data_fd);
+        unsigned short p = fd[fd[sock].ftp_data_fd].name.sin_port;
+	fd[sock].length = 
+	  sprintf(fd[sock].req_header, "227 (%u,%u,%u,%u,%u,%u)\r\n",
+		  ((unsigned char*)&local_addr)[0],
+		  ((unsigned char*)&local_addr)[1],
+		  ((unsigned char*)&local_addr)[2],
+		  ((unsigned char*)&local_addr)[3],
+		  ((unsigned char*)&p)[0],
+		  ((unsigned char*)&p)[1]);
+	if (verbose) printf(fd[sock].req_header);
+	make_long_response(sock);
+	fd[sock].ftp_mode = FTP_PASV;
+	return 0;
+    } else if (STREQ(buffer,"PORT")) {
+        // watch out for an endian problems !!!
+	char *start, *stop;
+	for (start = buffer; !ParseRules::is_digit(*start); start++);
+	for (stop = start; *stop != ','; stop++);
+	for (i = 0; i < 4; i++) {
+	  ((unsigned char*)&(fd[sock].ftp_peer_addr))[i] =
+	    strtol(start, &stop, 10);
+	  for (start = ++stop; *stop != ','; stop++);
+	}
+	((unsigned char*)&(fd[sock].ftp_peer_port))[0] =
+	  strtol(start, &stop, 10);
+	start = ++stop;
+	((unsigned char*)&(fd[sock].ftp_peer_port))[1] =
+	  strtol(start, NULL, 10);
+	fd[sock].length = 
+	  sprintf(fd[sock].req_header, "200 Okay\r\n");
+	if (verbose) printf(fd[sock].req_header);
+	make_long_response(sock);
+	fd[sock].ftp_mode = FTP_PORT;
+	return 0;
+    } else if (STREQ(buffer,"RETR")) {
+      	if (fd[sock].ftp_mode == FTP_NULL) {
+	  // default to PORT ftp
+	  struct sockaddr_in ftp_peer;
+	  int ftp_peer_addr_len = sizeof(ftp_peer);
+	  if (getpeername(sock, (struct sockaddr*)&ftp_peer,
+#if 0
+			  &ftp_peer_addr_len
+#else
+			  (socklen_t*)ftp_peer_addr_len
+#endif
+	    ) < 0) {
+	    perror("getsockname");
+	    exit(EXIT_FAILURE);
+	  }
+	  fd[sock].ftp_peer_addr = ftp_peer.sin_addr.s_addr;
+	  fd[sock].ftp_peer_port = ftp_peer.sin_port;
+	  fd[sock].ftp_mode = FTP_PORT;
+	}
+	if (fd[sock].ftp_mode == FTP_PORT) {
+	  if ((fd[sock].ftp_data_fd =
+	       make_client(fd[sock].ftp_peer_addr,fd[sock].ftp_peer_port)) < 0)
+	    panic("could not open ftp PORT data connection to client\n");
+	  fd[fd[sock].ftp_data_fd].ftp_data_fd = sock;
+	  fd[fd[sock].ftp_data_fd].state = STATE_FTP_DATA_READY;
+	  if (verbose)
+	    printf("ftp PORT %d <-> %d\n", sock, fd[sock].ftp_data_fd);
+	}
+	n = (char*)memchr(buffer,'\n',fd[sock].req_pos);
+	if (!n) return 0;
+	if (send_ftp_data(sock, buffer+5, n)<0) {
+	  errors++;
+	  *n = 0;
+	  if (verbose)
+	    printf("badly formed ftp request: %s\n", buffer);
+	  return 1;
+	}
+	fd[sock].response = fd[sock].req_header;
+	fd[sock].length = sprintf( fd[sock].req_header, "150 %d bytes\r\n", 
+				   fd[fd[sock].ftp_data_fd].length);
+	fd[sock].req_pos = 0;
+	fd[sock].response_length = strlen(fd[sock].req_header);
+	poll_set(sock, NULL, write_ftp_response);
+	buffer = n+1;
+	return 0;
+    } else {
+      if (verbose || verbose_errors) printf("ftp junk : %s\n", buffer);
+      fd[sock].req_pos = 0;
+      return 0;
+    }
+  }
+}
+
+static int accept_sock(int sock) {
+  struct sockaddr_in clientname;
+  int size = sizeof (clientname);
+  int new_fd = 0;
+  do {
+    new_fd = accept(sock, (struct sockaddr *) &clientname,
+#if 0
+		    &size
+#else
+		    (socklen_t*)&size
+#endif
+      );
+    if (new_fd < 0) {
+      if (errno == EAGAIN || errno == ENOTCONN) return 0;
+      if (errno == EINTR || errno == ECONNABORTED) continue;
+      printf("accept socket was %d\n",sock);
+      panic_perror("accept");
+    }
+  } while (new_fd < 0);
+
+  if (fcntl (new_fd, F_SETFL, O_NONBLOCK) < 0)
+    panic_perror("fcntl");
+
+#if 0
+#ifdef BUFSIZE				//  make default
+  int bufsize = BUFSIZE;
+  if (setsockopt(new_fd,SOL_SOCKET,SO_SNDBUF,
+		 (const char *)&bufsize,sizeof(bufsize)) < 0) {
+    perror("setsockopt");
+  }
+  if (setsockopt(new_fd,SOL_SOCKET,SO_SNDBUF,
+		 (const char *)&bufsize,sizeof(bufsize)) < 0) {
+    perror("setsockopt");
+  }
+#endif
+#endif
+  int enable =1;
+  if (setsockopt(new_fd,IPPROTO_TCP,TCP_NODELAY,
+		 (const char *)&enable,sizeof(enable)) < 0) {
+    perror("setsockopt");
+  }
+#ifdef PRINT_LOCAL_PORT
+  struct sockaddr_in local_sa;
+  size = sizeof(local_sa);
+  getsockname(new_fd, (struct sockaddr*)&local_sa, &size);
+  printf("local_sa.sin_port = %d\n", local_sa.sin_port);
+#endif
+  return new_fd;
+}
+
+static int accept_compd (int sock) {
+  int new_fd = accept_sock(sock);
+  servers++;
+  new_servers++;
+  poll_init_set(new_fd, NULL, read_compd_request);
+  fd[new_fd].count = &servers;
+  fd[new_fd].start = now;
+  fd[new_fd].ready = now + server_delay * HRTIME_MSECOND;
+  fd[new_fd].keepalive = server_keepalive?server_keepalive:INT_MAX;
+
+  return 0;
+}
+
+static int accept_read (int sock) {
+  int new_fd = accept_sock(sock);
+  servers++;
+  new_servers++;
+  if (ftp) {
+    poll_init_set(new_fd, NULL, write_ftp_response);
+    make_response(new_fd, 220);
+  } else
+    poll_init_set(new_fd, read_request);
+  fd[new_fd].count = &servers;
+  fd[new_fd].start = now;
+  fd[new_fd].ready = now + server_delay * HRTIME_MSECOND;
+  fd[new_fd].keepalive = server_keepalive?server_keepalive:INT_MAX;
+
+  return 0;
+}
+
+static int accept_ftp_data (int sock) {
+  int new_fd = accept_sock(sock);
+  servers++;
+  new_servers++;
+  poll_init(new_fd);
+  fd[new_fd].ftp_data_fd = fd[sock].ftp_data_fd;
+  fd[fd[sock].ftp_data_fd].ftp_data_fd = new_fd;
+  fd[new_fd].state = STATE_FTP_DATA_READY;
+  fd[new_fd].count = &servers;
+  fd[new_fd].start = now;
+  fd[new_fd].ready = now + server_delay * HRTIME_MSECOND;
+  fd[new_fd].keepalive = server_keepalive?server_keepalive:INT_MAX;
+  fd[new_fd].state = STATE_FTP_DATA_READY;
+  fd[new_fd].doc = fd[sock].doc;
+  fd[new_fd].doc_length = fd[sock].doc_length;
+  if (verbose) 
+    printf("accept_ftp_data %d for %d\n", new_fd, sock);
+  send_ftp_data_when_ready(new_fd);
+  return 1;
+}
+
+static int open_server(unsigned short int port, accept_fn_t accept_fn) {
+  struct linger lngr;
+  int sock;
+  int one = 1;
+  int err = 0;
+
+  /* Create the socket. */
+  sock = socket (AF_INET, SOCK_STREAM, 0);
+  if (sock < 0) {
+    perror ("socket");
+    exit (EXIT_FAILURE);
+  }
+  struct sockaddr_in & name = fd[sock].name;
+
+  /* Give the socket a name. */
+  name.sin_family = AF_INET;
+  name.sin_port = htons (port);
+  name.sin_addr.s_addr = htonl (INADDR_ANY);
+  if (setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
+		  sizeof (one)) < 0) {
+    perror((char*)"setsockopt");
+    exit( EXIT_FAILURE);
+  }
+  if ((err=bind (sock, (struct sockaddr *) &name, sizeof (name))) < 0) {
+    if (errno == EADDRINUSE)
+      return -EADDRINUSE;
+    perror("bind");
+    exit( EXIT_FAILURE);
+  }
+
+  int addrlen = sizeof(name);
+  if ((err = getsockname(sock, (struct sockaddr *) &name, 
+#if 0
+			 &addrlen
+#else
+			 (socklen_t*)&addrlen
+#endif
+    )) < 0) {
+    perror("getsockname");
+    exit( EXIT_FAILURE);
+  }
+  ink_assert(addrlen);
+
+  /* Tell the socket not to linger on exit */
+  lngr.l_onoff = 0;
+  lngr.l_linger = 0;
+  if (setsockopt (sock, SOL_SOCKET, SO_LINGER, (char*) &lngr, 
+  		  sizeof (struct linger)) < 0) {
+    perror ("setsockopt");
+    exit (EXIT_FAILURE);
+  }
+
+  if (listen (sock, 1024) < 0) {
+    perror ("listen");
+    exit (EXIT_FAILURE);
+  }
+
+  /* put the socket in non-blocking mode */
+  if (fcntl (sock, F_SETFL, O_NONBLOCK) < 0) {
+    perror ("fcntl");
+    exit (EXIT_FAILURE);
+  }
+
+  if (verbose) 
+    printf("opening server on %d port %d\n", sock, name.sin_port);
+
+  poll_init_set(sock, accept_fn);
+
+  return sock;
+}
+
+//   perform poll and invoke callbacks on active descriptors 
+int poll_loop() {
+  if (server_fd > 0) {
+    while (read_ready(server_fd) > 0)
+      accept_read(server_fd);
+  }
+  pollfd pfd[POLL_GROUP_SIZE];
+  int ip = 0;
+  int last_fd_in_block = 0;
+  now = ink_get_hrtime();
+  for (int i = 0 ; i <= last_fd ; i++) {
+    if (fd[i].fd > 0 && (!fd[i].ready || now >= fd[i].ready)) {
+      pfd[ip].fd = i;
+      pfd[ip].events = 0;
+      pfd[ip].revents = 0;
+      if (fd[i].read_cb)
+	pfd[ip].events |= POLLIN;
+      if (fd[i].write_cb)
+	pfd[ip].events |= POLLOUT;
+      ip++;
+    }
+    if (ip >= POLL_GROUP_SIZE || i == last_fd) {
+      int n = poll(pfd,ip,POLL_TIMEOUT);
+      if (n > 0) {
+	for (int j = 0; j < ip ; j++) {
+	  if (pfd[j].revents & (POLLIN|POLLERR|POLLHUP|POLLNVAL)) {
+	    if (verbose) printf("poll read %d %X\n",pfd[j].fd,pfd[j].revents);
+	    if (fd[pfd[j].fd].read_cb && fd[pfd[j].fd].read_cb(pfd[j].fd)) {
+	      fd[pfd[j].fd].close();
+	      continue;
+	    }
+	  }
+	  if (pfd[j].revents & (POLLOUT|POLLERR|POLLHUP|POLLNVAL)) {
+	    if (verbose) printf("poll write %d %X\n",pfd[j].fd,pfd[j].revents);
+	    if (fd[pfd[j].fd].write_cb && fd[pfd[j].fd].write_cb(pfd[j].fd)) {
+	      fd[pfd[j].fd].close();
+	      continue;
+	    }
+	  }
+	}
+      }
+      ip = 0;
+    }
+  }
+  return 0;
+}
+
+int gen_bfc_dist(double f = 10.0) {
+
+  if (docsize) return docsize;
+
+  double rand = 0.0;
+  double rand2 = 0.0;
+  bool f_given = f < 9.0;
+  if (!f_given) {
+    rand = drand48();
+    rand2 = drand48();
+  } else {
+    rand = f;
+    rand2 = (f * 13.0) - floor(f * 13.0);
+  }
+
+  int class_no;
+  int file_no;
+
+  if(rand < 0.35){
+    class_no = 0;
+  }else if(rand < 0.85){
+    class_no = 1;
+  }else if (rand < 0.99){
+    class_no = 2;
+  }else {
+    class_no = 3;
+    if (f_given) rand2 = (f * 113.0) - floor(f * 113.0);
+  }
+  
+  if(rand2 < 0.018){
+    file_no=0;
+  }else if(rand2 < 0.091){
+    file_no=1;
+  }else if(rand2 < 0.237){
+    file_no=2;
+  }else if(rand2 < 0.432){
+    file_no=3;
+  }else if(rand2 < 0.627){
+    file_no=4;
+  }else if(rand2 < 0.783){
+    file_no=5;
+  }else if(rand2 < 0.887){
+    file_no=6;
+  }else if(rand2 < 0.945){
+    file_no=7;
+  }else if(rand2 < 1.000){
+    file_no=8;
+  }
+  int size = 100;
+  int i;
+  for (i = 0; i < class_no; i++) {
+    size = size * 10;
+  }
+  int increment = size;
+  size = size * (file_no+1);
+  // vary about the mean doc size for
+  // that class/size
+  if (!f_given)
+    size += (int)((-increment * 0.5) + (increment * drand48()));
+  if (verbose) printf("gen_bfc_dist %d\n",size);
+  return size;
+}
+
+void build_response() {
+  int maxsize = docsize > MAX_RESPONSE_LENGTH ? docsize : MAX_RESPONSE_LENGTH;
+  response_buffer = (char*) malloc (maxsize + HEADER_SIZE);
+  for (int i = 0 ; i < maxsize + HEADER_SIZE; i++)
+    response_buffer[i] = i % 256;
+}
+
+static void put_ka(int sock) {
+  int i = 0;
+  for (; i < n_ka_cache; i++ ) 
+    if (!ka_cache_head[i] || fd[ka_cache_head[i]].ip == fd[sock].ip) 
+      goto Lpush;
+  i = n_ka_cache++;
+Lpush:	
+  if (ka_cache_tail[i])
+    fd[ka_cache_tail[i]].next = sock;
+  else
+    ka_cache_head[i] = sock;
+  ka_cache_tail[i] = sock;
+}
+
+static int get_ka(unsigned int ip) {
+  for (int i = 0; i < n_ka_cache; i++ ) 
+    if (fd[ka_cache_head[i]].ip == ip) {
+      int res = ka_cache_head[i];
+      ka_cache_head[i] = fd[ka_cache_head[i]].next;
+      if (res == ka_cache_tail[i]) {
+	ink_assert(!ka_cache_head[i]);
+	ka_cache_tail[i] = 0;
+      }
+      return res;
+    }
+  return -1;
+}
+
+static void defer_url(char * url) {
+  if (n_defered_urls < MAX_DEFERED_URLS -1)
+    defered_urls[n_defered_urls++] = strdup(url);
+  else
+    fprintf(stderr, "too many defered urls, dropping '%s'\n", url);
+}
+
+static int throttling_connections() {
+  return client_rate && keepalive_cons && current_clients >= keepalive_cons;
+}
+
+static void done() {
+  interval_report();
+  exit(0);
+}
+
+static int is_done() { 
+  return 
+    (urls_mode && !current_clients && !n_defered_urls) ||
+    (bandwidth_test && bandwidth_test_to_go <= 0 && !current_clients);
+}
+
+static void undefer_url(bool unthrottled) {
+  if ((unthrottled || !throttling_connections()) && n_defered_urls) {
+    --n_defered_urls;
+    char * url =  defered_urls[n_defered_urls];
+    make_url_client(url,0,true,unthrottled);
+    free(url);
+    if (verbose) 
+      printf("undefer_url: made client %d clients\n", current_clients);
+  } else
+    if (verbose) printf("undefer_url: throttle\n");
+  if (is_done())
+    done();
+}
+
+static void init_client(int sock) {
+  poll_init(sock);
+  fd[sock].start = now;
+  fd[sock].ready = now;
+  fd[sock].count = &clients;
+  poll_set(sock,NULL,write_request);
+}
+
+static unsigned int get_addr(char * host) {
+  unsigned int addr = inet_addr(host);
+  struct hostent *host_info = NULL;
+
+  if (!addr || (-1 == (int)addr)) {
+    host_info = gethostbyname(host);
+    if (!host_info) {
+      perror ("gethostbyname");
+      return (unsigned int)-1;
+    }
+    addr = *((unsigned int*) host_info->h_addr);
+  }
+
+  return addr;
+}
+
+char * find_href_end (char *start, int len)
+{
+  char *end = start;
+  if (!start) return NULL;
+
+  while (*end && len > 0) {
+      if (*end == '\"') break;	/* " */
+      if (*end == '\'') break;		       
+      if (*end == '>')	break;
+      if (*end == ' ')	break;
+      if (*end == '\t')	break;
+      if (*end == '\n')	break;
+      if (*end == '<')	break;
+      if(*end & 0x80) break; /* hi order bit! */
+      len--;
+      end++;
+    }
+
+  if (*end == 0 || len == 0) 
+    return NULL;
+  else
+    return end;
+} // find_href_end
+
+char * find_href_start(char * tag, char *base, int len)
+{
+  int taglen = strlen(tag);
+  if (base == NULL) return NULL;
+
+  char *start = base;
+  char *end = base + len;
+
+ Lagain:
+  {
+    start = strncasestr(start, tag, len);
+    if ((start == NULL) || (end-start < 6)) {
+      return NULL;
+    }
+    start += taglen;
+    len -= taglen;
+  } // block
+
+  while (ParseRules::is_ws (*start) && (end - start > 1)) {
+    start++;
+    len--;
+  }
+  if (*start == '=' && (end - start > 1)) {
+    start++;
+    len--;
+  } else {
+    goto Lagain;
+  }
+  while (ParseRules::is_ws(*start) && (end - start > 1)) {
+    start++;
+    len--;
+  }
+  //
+  // Optional quotes:  href="x" or href='x' or href=x
+  //
+  if ((*start == '\"'||(*start == '\'')) && (end - start > 1)) { /*"'*/
+    start++;
+    len--;
+  }
+  while (ParseRules::is_ws(*start) && (end - start > 1)) {
+    start++;
+    len--;
+  }
+
+  return start;
+} // find_href_start
+
+int compose_url(char * new_url, char * base, char *input) {
+  char sche[8],host[512],port[10],path[512],frag[512],quer[512],para[512];
+  char curl[512];
+  int xsche,xhost,xport,xpath,xfrag,xquer,xpar,rel,slash;
+  ink_web_decompose_url(base,sche,host,port,path,frag,quer,para,
+			&xsche,&xhost,&xport,&xpath,&xfrag,&xquer,
+			&xpar,&rel,&slash);
+  strcpy(curl, "http://");
+  strcat(curl,host);
+  if (xport) {
+    strcat(curl,":");
+    strcat(curl,port);
+  }
+  strcat(curl,"/");
+  strcat(curl,path);
+
+  ink_web_canonicalize_url(curl, input, new_url, 512);
+  return 0;
+} // compose_urls
+
+void compose_all_urls( char * tag, char * buf, char * start, char * end, 
+		       int buflen, char * base_url)
+{
+  char old;
+  while ((start = find_href_start(tag, end, buflen - (end - buf)))) {
+    char newurl[512];
+    end = (char *) find_href_end(start, min(buflen - (start-buf), 512 - 10)); 
+    if (!end) { 
+      end = start + strlen(tag);
+      continue;
+    }
+    old = *end;
+    *end = 0;
+    compose_url(newurl, base_url, start);
+    make_url_client(newurl,base_url);
+    *end = old;
+  } // while
+}
+//
+// Input is a NULL-terminated string (buf of buflen)
+//       also, a read-write base_url
+//
+void extract_urls(char * buf, int buflen, char * base_url) {
+  //if (verbose) printf("EXTRACT<<%s\n>>", buf);
+  char old;
+  char *start = NULL;
+  char *end = NULL;
+  char old_base[512];
+  strcpy(old_base, base_url);
+
+  start = strncasestr(buf, "<base ", buflen);
+  if (start) {
+    end = (char*)memchr(start, '>', buflen - (start - buf));
+    if (end) {
+      char * rover = strncasestr(start, "href", end - start);
+      if (rover) {
+	rover += 4;
+	while (rover < end && 
+	       (ParseRules::is_ws(*rover) || *rover == '=' || *rover == '\'' 
+		|| *rover == '\"'))    /* " */
+	  rover++;
+	start = rover;
+	while (rover < end && 
+	       !(ParseRules::is_ws(*rover) || *rover == '\'' 
+		 || *rover == '\"'))
+	  rover++;
+	old = *rover;
+	*rover = 0;
+	compose_url(base_url,old_base,start);
+	// fixup unqualified hostnames (e.g. http://internal/foo)
+	char * he = strchr(base_url + 8, '/'); 
+	if (!memchr(base_url,'.',he-base_url)) {
+	  char t[512]; strcpy(t,base_url);
+	  char * old_he = strchr(old_base + 8, '.');
+	  if (old_he) {
+	    char * old_hee = strchr(old_he, '/');
+	    if (old_hee) {
+	      memcpy(base_url,t,(he-base_url));
+	      memcpy(base_url + (he-base_url), old_he, (old_hee-old_he));
+	      memcpy(base_url + (he-base_url) + (old_hee-old_he),
+		     t+(he-base_url), strlen(t+(he-base_url)));
+	      base_url[(he-base_url) + (old_hee-old_he) +
+		      strlen(t+(he-base_url))] = 0;
+	    }}}}}}
+
+  end = buf;
+  if (follow)
+    compose_all_urls("href", buf, start, end, buflen, base_url);
+  if (fullpage) {
+    char *tags[] = { "src", "image", "object", "archive", "background", 
+		     // "location", "code" 
+    };
+    for (int i = 0 ; i < sizeof(tags)/sizeof(tags[0]) ; i++)
+      compose_all_urls(tags[i], buf, start, end, buflen, base_url);
+  }
+} // extract_urls
+
+void follow_links(int sock) {
+  if (urls_mode) {
+    if (fd[sock].binary) return;
+    int l = fd[sock].response_remaining;
+    char * r = fd[sock].response, *p = r, *n = r;
+    if (r)
+      extract_urls(r,l,fd[sock].base_url);
+    if (l < MAX_BUFSIZE) {
+      while (n) {
+	n = (char*)memchr(p,'\n',l - (p - r));
+	if (!n) n = (char*)memchr(p,'\r',l - (p - r));
+	if (n) p = n + 1;
+      }
+      int done = p - r, remaining = l - done;
+      if (done) {
+	memmove(r, p, remaining);
+	fd[sock].response_remaining = remaining;
+      }
+    } else  // bail
+      fd[sock].response_length = 0;
+  }
+}
+
+static int verify_content(int sock, char * buf, int done) {
+  if (urls_mode && !check_content)
+    return 1;
+  int l = fd[sock].response_length;
+  char * d = response_buffer + (l % 256) + fd[sock].nalternate;
+  int left = fd[sock].length;
+  if (left > 0) {
+    if (embed_url && !fd[sock].jg_compressed) {
+      if (l == left && left > 64) {
+      	char *url_end = NULL, *url_start = NULL;
+	get_path_from_req(fd[sock].base_url,&url_start,&url_end);
+	if (url_end - url_start < done) {
+	  if (memcmp(url_start,buf,url_end - url_start)) 
+	    return 0;
+	}
+      }
+      // skip past the URL which is embedded in the document
+      // to confound the fingerprinting code
+      if (l - left < 64) {
+	int skip = 64 - (l - left);
+	left -= skip;
+	done -= skip;
+	buf += skip;
+	if (done < 0)
+	  done = 0;
+      }
+    }
+    if (!check_content)
+      return 1;
+    if (done > left)
+      done = left;
+    if (memcmp(buf, d + (fd[sock].response_length - left), done))
+      return 0;
+  }
+  return 1;
+}
+
+static int read_response_error(int sock) {
+  errors++;
+  fd[sock].close();
+  if (!urls_mode)
+    make_bfc_client(proxy_addr, proxy_port);
+  return 0;
+}
+
+static int read_for_error(int sock) {
+  char b[8192];
+  int err = read(sock, &b, 8192);
+  if (err < 0)
+    return read_response_error(sock);
+  return 0;
+}
+
+static int read_response(int sock) {
+  int err = 0;
+
+  if (fd[sock].req_pos >= 0) {
+    if (!fd[sock].req_pos) 
+      memset(fd[sock].req_header,0,HEADER_SIZE);
+    do {
+      int l = HEADER_SIZE - fd[sock].req_pos - 1;
+      if (l <= 0) { 
+	if (verbose || verbose_errors) 
+	  printf("header too long '%s'", fd[sock].req_header);
+	return read_response_error(sock);
+      }
+      err = read(sock, fd[sock].req_header + fd[sock].req_pos, 
+		 HEADER_SIZE - fd[sock].req_pos - 1);
+    } while ((err == -1) && (errno == EINTR));
+    if (err <= 0) {
+      if (!err) {
+	if (verbose_errors) 
+	  printf("read_response %d closed during header for '%s' after %d%s\n",
+		 sock, fd[sock].base_url, fd[sock].req_pos,
+		 (keepalive && (fd[sock].keepalive != keepalive) 
+		  && !fd[sock].req_pos) ? " -- keepalive timeout" : "");
+	return read_response_error(sock);
+      }
+      if (errno == EAGAIN || errno == ENOTCONN) return 0;
+      if (errno == ECONNRESET) {
+	if (!fd[sock].req_pos && keepalive > 0 && 
+	    fd[sock].keepalive != keepalive) {
+	  fd[sock].close();
+	  if (!urls_mode)
+	    make_bfc_client(proxy_addr, proxy_port);
+	  return 0;
+	}
+	if (verbose || verbose_errors)
+	  perror ("read");
+	goto Ldone;
+      }
+      panic_perror("read");
+    }
+    if (verbose) 
+      printf("read %d header %d [%s]\n", 
+	     sock, err, fd[sock].req_header);
+    b1_ops++;
+
+    strcpy(fd[sock].response_header, fd[sock].req_header);
+
+    b1latency += ((ink_get_hrtime() - fd[sock].start) / HRTIME_MSECOND);
+    new_cbytes += err;
+    new_tbytes += err;
+    fd[sock].req_pos += err;
+    fd[sock].bytes += err;
+    fd[sock].active = ink_get_hrtime();
+    int total_read = fd[sock].req_pos;
+    char * p = fd[sock].req_header;
+    char * cl = NULL;
+    int cli = 0;
+    while ((p = strchr(p,'\n'))) {
+      if (verbose) printf("read header end? [%s]\n", p);
+      if (p[1] == '\n' || (p[1] == '\r' && p[2] == '\n')) {
+	int off = 1 + (p[1] == '\r' ? 2 : 1);
+	p += off;
+	strncpy(fd[sock].response_header, fd[sock].req_header, p - fd[sock].req_header);
+	fd[sock].response_header[p - fd[sock].req_header] = '\0';
+	int lbody =  fd[sock].req_pos - (p - fd[sock].req_header);
+	cl = strncasestr(fd[sock].req_header,"Content-Length:", 
+			 p - fd[sock].req_header);
+	if (cl) {
+	  cli = atoi(cl + 16);
+	  if (!cli && verbose_errors)
+	    fprintf(stderr, "bad Content-Length '%s': %d\n", cl, cli);
+	  else {
+	    int expected_length = fd[sock].response_length;
+	    if (compd_suite) {
+	      if (strstr(fd[sock].req_header, "x-jg")) {
+		fd[sock].jg_compressed = 1;
+		expected_length = (fd[sock].response_length * 2) / 3;
+	      }
+	    } 
+	    if (fd[sock].response_length && verbose_errors &&
+		expected_length != cli && !nocheck_length) 
+	      fprintf(stderr, "bad Content-Length expected %d got %d orig %d",
+		      expected_length, cli, fd[sock].response_length);
+	    fd[sock].response_length = fd[sock].length = cli;
+	  }
+	}
+	if (fd[sock].req_header[9] == '2') {
+	  if (!verify_content(sock,p,lbody)) {
+	    if (verbose || verbose_errors)
+	      printf("content verification error '%s'\n", fd[sock].base_url);
+	    return read_response_error(sock);
+	  }
+	}
+	total_proxy_response_body_bytes += lbody;
+	total_proxy_response_header_bytes += p - fd[sock].req_header;
+	fd[sock].length -= lbody;
+	fd[sock].req_pos = -1;
+	if (fd[sock].length && drand48() < client_abort_rate) {
+	  fd[sock].client_abort = 1;
+	  fd[sock].length = (int)(drand48() * (fd[sock].length -1)); 
+	  fd[sock].keepalive = 0;
+	  fd[sock].drop_after_CL = 1;
+	}
+	if (verbose) printf("read %d header done\n", sock);
+	break;
+      }
+      p++;
+    }
+    if (!p) return 0;
+    int hlen = p -  fd[sock].req_header;
+    if (show_headers) {
+      printf("Response From Proxy: {\n");
+      for (char * c = fd[sock].req_header; c < p ; c++)
+	putc(*c,stdout);
+      printf("}\n");
+    }
+    if (obey_redirects && urls_mode && 
+	fd[sock].req_header[9] == '3' &&
+	fd[sock].req_header[10] == '0' &&
+	(fd[sock].req_header[11] == '1' || fd[sock].req_header[11] == '2')) 
+    {
+      char * redirect = strstr(fd[sock].req_header,"http://");
+      char * e = redirect?(char*)memchr(redirect,'\n',hlen):0;
+      if (!redirect || !e)
+	fprintf(stderr, "bad redirect '%s'",fd[sock].req_header);
+      else {
+	if (e[-1]=='\r') e--;
+	*e = 0;
+	make_url_client(redirect);
+      }
+      fd[sock].close();
+      return 0;
+    }
+    if (fd[sock].req_header[9] != '2') {
+      if (verbose_errors) {
+	char * e = (char*)memchr(fd[sock].req_header, '\r', hlen);
+	if (e) *e = 0;
+	else {
+	  char * e = (char*)memchr(fd[sock].req_header, '\n', hlen);
+	  if (e) *e = 0;
+	  else *p = 0;
+	}
+	printf("error response %d: '%s':'%s'\n", sock, 
+	       fd[sock].base_url, fd[sock].req_header);
+      }
+      return read_response_error(sock);
+    }
+    char * r = fd[sock].req_header;
+    int length = p - r;
+    char * ka = check_keepalive(r, length);
+    if (urls_mode) {
+      fd[sock].response_remaining = total_read - length;
+      if (fd[sock].response_remaining) 
+	memcpy(fd[sock].response,p,fd[sock].response_remaining);
+      if (check_content && !cl) {
+	if (verbose || verbose_errors)
+	  printf("missiing Content-Length '%s'\n", fd[sock].base_url);
+	return read_response_error(sock);
+      }
+    } else
+      fd[sock].response = 0;      
+    if (!cl || !ka)
+      fd[sock].keepalive = -1;
+    if (!cl)
+      fd[sock].length = INT_MAX;
+  }
+  
+  if (fd[sock].length <= 0 && 
+      (fd[sock].keepalive > 0 || fd[sock].drop_after_CL))
+    goto Ldone;
+
+  {
+    char * r = NULL;
+    char buf[MAX_BUFSIZE];
+    int toread = cbuffersize;
+    if (urls_mode) {
+      if (fd[sock].response_remaining + cbuffersize < MAX_BUFSIZE)
+	r = fd[sock].response + fd[sock].response_remaining;
+      else {
+	toread = MAX_BUFSIZE - fd[sock].response_remaining;
+	if (!toread) {
+	  if (verbose_errors || verbose)
+	    fprintf(stderr,"line exceeds buffer, unable to follow links\n");
+	  toread = cbuffersize;
+	  r = fd[sock].response;
+	  fd[sock].response_remaining = 0;
+	} else
+	  r = fd[sock].response + fd[sock].response_remaining;
+      }
+    } else
+      r = buf;
+    if (fast(sock,client_speed,fd[sock].bytes)) return 0;
+    if (fd[sock].bytes > abort_retry_bytes && 
+	(((now - fd[sock].start + 1)/HRTIME_SECOND) > abort_retry_secs) && 
+	!faster_than(sock,abort_retry_speed,fd[sock].bytes)) 
+    {
+      fd[sock].client_abort = 1;
+      fd[sock].keepalive = 0;
+      if (!urls_mode && !client_rate)
+	make_bfc_client(proxy_addr, proxy_port);
+      goto Ldone;
+    }
+    do {
+      err = read(sock, r, toread);
+    } while ((err == -1) && (errno == EINTR));
+    if (err < 0) {
+      if (errno == EAGAIN || errno == ENOTCONN) return 0;
+      if (errno == ECONNRESET) {
+	if (verbose || verbose_errors)
+	  perror ("read");
+	goto Ldone;
+      }
+      panic_perror("read");
+    }
+    if (!err)
+      goto Ldone;
+    if (!verify_content(sock,buf,err)) {
+      if (verbose || verbose_errors)
+	printf("content verification error '%s'\n", fd[sock].base_url);
+      return read_response_error(sock);
+    }
+    total_proxy_response_body_bytes += err;
+    new_cbytes += err;
+    new_tbytes += err;
+    fd[sock].response_remaining += err;
+    fd[sock].bytes += err;
+    follow_links(sock);
+    if (fd[sock].length != INT_MAX)
+      fd[sock].length -= err;
+    fd[sock].active = ink_get_hrtime();
+    if (verbose) 
+      printf("read %d got %d togo %d %d %d\n", sock, err, fd[sock].length,
+	     fd[sock].keepalive, fd[sock].drop_after_CL);
+  }
+  
+  if (fd[sock].length <= 0 && 
+      (fd[sock].keepalive > 0 || fd[sock].drop_after_CL))
+    goto Ldone;
+
+  return 0;
+  
+Ldone:
+  int ok = false;
+  if (!fd[sock].client_abort && 
+      !(server_abort_rate > 0) &&
+      fd[sock].length && fd[sock].length != INT_MAX) 
+  {
+    if (verbose || verbose_errors) 
+      printf("bad length %d wanted %d after %d ms: '%s'\n", 
+	     fd[sock].response_length - fd[sock].length,
+	     fd[sock].response_length,
+	     (int)((ink_get_hrtime() - fd[sock].active)/HRTIME_MSECOND),
+	     fd[sock].base_url);
+    return read_response_error(sock);
+  }
+  if (verbose) printf("read %d done\n", sock);
+  new_ops++;
+  double thislatency =((ink_get_hrtime() - fd[sock].start) / HRTIME_MSECOND);
+  latency += (int)thislatency;
+  lat_ops++;
+  if (fd[sock].keepalive > 0) {
+    fd[sock].reset();
+    put_ka(sock);
+    current_clients--;
+    if (urls_mode) {
+      undefer_url();
+      return 0;
+    }
+  } else
+    fd[sock].close();
+  if (!urls_mode && !client_rate)
+    make_bfc_client(proxy_addr, proxy_port);
+  return 0;
+}
+  
+static int write_request(int sock) {
+  int err = 0;
+  
+  do {
+    err = write(sock, fd[sock].req_header + fd[sock].req_pos, 
+		fd[sock].length - fd[sock].req_pos);
+  } while ((err == -1) && (errno == EINTR));
+  if (err <= 0) {
+    if (!err) {
+      if (verbose_errors) printf("write %d closed early\n", sock);
+      goto Lerror;
+    }
+    if (errno == EAGAIN || errno == ENOTCONN) return 0;
+    perror("write");
+    goto Lerror;
+  }
+  if (verbose) printf("write %d %d\n", sock, err);
+
+  new_tbytes += err;
+  total_client_request_bytes += err;
+  fd[sock].req_pos += err;
+  fd[sock].active = ink_get_hrtime();
+  
+  if (fd[sock].req_pos >= fd[sock].length) {
+    if (verbose) printf("write complete %d %d\n", sock, fd[sock].length);
+    fd[sock].req_pos = 0;
+    fd[sock].length = fd[sock].response_length;
+    poll_set(sock, read_response);
+  }
+  return 0;
+Lerror:
+  errors++;
+#ifndef RETRY_CLIENT_WRITE_ERRORS
+  if (!--nclients)
+    panic("no more clients\n");
+  return 1;
+#else
+  if (!urls_mode)
+    make_bfc_client(proxy_host, proxy_port);
+  fd[sock].close();
+  return 0;
+#endif
+}
+
+static int write_ftp_response(int sock) {
+  int err = 0;
+  
+  do {
+    err = write(sock, fd[sock].req_header + fd[sock].req_pos, 
+		fd[sock].length - fd[sock].req_pos);
+  } while ((err == -1) && (errno == EINTR));
+  
+  if (err <= 0) {
+    if (!err) {
+      if (verbose_errors) printf("write %d closed early\n", sock);
+      goto Lerror;
+    }
+    if (errno == EAGAIN || errno == ENOTCONN) return 0;
+    perror("write");
+    goto Lerror;
+  }
+  if (verbose) printf("write %d %d\n", sock, err);
+
+  new_tbytes += err;
+  fd[sock].req_pos += err;
+  
+  if (fd[sock].req_pos >= fd[sock].length) {
+    if (verbose) printf("write complete %d %d\n", sock, fd[sock].length);
+    fd[sock].req_pos = 0;
+    fd[sock].length = fd[sock].response_length;
+    poll_set(sock, read_ftp_request);
+  }
+  return 0;
+Lerror:
+  errors++;
+  return 1;
+}
+
+static int make_client (unsigned int addr, int port) {
+  struct linger lngr;
+
+  int sock = socket (PF_INET, SOCK_STREAM, 0);
+  if (sock < 0)
+    panic_perror ("socket");
+
+  if (fcntl (sock, F_SETFL, O_NONBLOCK) < 0)
+    panic_perror ("fcntl");
+
+  /* tweak buffer size so that remote end can't close connection too fast */
+
+#if 0
+  int bufsize = cbuffersize;
+  if (setsockopt(sock,SOL_SOCKET,SO_RCVBUF,
+		 (const char *)&bufsize,sizeof(bufsize)) < 0)
+    panic_perror("setsockopt");
+  if (setsockopt(sock,SOL_SOCKET,SO_SNDBUF,
+		 (const char *)&bufsize,sizeof(bufsize)) < 0)
+    panic_perror("setsockopt");
+#endif
+  int enable =1;
+  if (setsockopt(sock,IPPROTO_TCP,TCP_NODELAY,
+		 (const char *)&enable,sizeof(enable)) < 0)
+    panic_perror("setsockopt");
+
+  /* Tell the socket not to linger on exit */
+  lngr.l_onoff = 1;
+  lngr.l_linger = 0;
+  if (!ftp) {  // this causes problems for PORT ftp -- ewong
+    if (setsockopt (sock, SOL_SOCKET, SO_LINGER, (char*) &lngr, 
+		    sizeof (struct linger)) < 0) {
+      perror ("setsockopt");
+      exit (EXIT_FAILURE);
+    }
+  }
+
+  /* Give the socket a name. */
+  struct sockaddr_in name;
+  name.sin_family = AF_INET;
+  name.sin_port = htons(port);
+  name.sin_addr.s_addr = addr;
+  
+  if (verbose) printf("connecting to %u.%u.%u.%u:%d\n",
+		      ((unsigned char*)&addr)[0], ((unsigned char*)&addr)[1], 
+		      ((unsigned char*)&addr)[2], ((unsigned char*)&addr)[3],
+		      port);
+  
+  

<TRUNCATED>

Re: [2/2] Open sourcing benchmarking tool

Posted by Bryan Call <bc...@yahoo-inc.com>.
Yeah, I open sourced as is, after removing some old Inktomi information.

I wouldn't use the FTP support in the tool myself and wouldn't be against removing it.

-Bryan

On May 1, 2012, at 9:29 PM, Igor Galić wrote:

> 
> 
> ----- Original Message -----
>> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9ce0e718/tools/jtest/jtest.cc
>> ----------------------------------------------------------------------
>> diff --git a/tools/jtest/jtest.cc b/tools/jtest/jtest.cc
>> new file mode 100644
>> index 0000000..05680db
>> --- /dev/null
>> +++ b/tools/jtest/jtest.cc
>> @@ -0,0 +1,6106 @@
> [snip]
>> +/*
>> + FTP - Traffic Server Template
> 
> Given how we don't actually support FTP anymore, and how it's 2012, we
> might want to consider dropping FTP support from this benchmark tool too.
> 
> [snip]
> 
> i
> 
> -- 
> Igor Galić
> 
> Tel: +43 (0) 664 886 22 883
> Mail: i.galic@brainsware.org
> URL: http://brainsware.org/
> GPG: 6880 4155 74BD FD7C B515  2EA5 4B1D 9E08 A097 C9AE
> 


Re: [2/2] Open sourcing benchmarking tool

Posted by Igor Galić <i....@brainsware.org>.

----- Original Message -----
> http://git-wip-us.apache.org/repos/asf/trafficserver/blob/9ce0e718/tools/jtest/jtest.cc
> ----------------------------------------------------------------------
> diff --git a/tools/jtest/jtest.cc b/tools/jtest/jtest.cc
> new file mode 100644
> index 0000000..05680db
> --- /dev/null
> +++ b/tools/jtest/jtest.cc
> @@ -0,0 +1,6106 @@
[snip]
> +/*
> + FTP - Traffic Server Template

Given how we don't actually support FTP anymore, and how it's 2012, we
might want to consider dropping FTP support from this benchmark tool too.

[snip]

i

-- 
Igor Galić

Tel: +43 (0) 664 886 22 883
Mail: i.galic@brainsware.org
URL: http://brainsware.org/
GPG: 6880 4155 74BD FD7C B515  2EA5 4B1D 9E08 A097 C9AE