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