]> git.ipfire.org Git - thirdparty/squid.git/blame - test-suite/tcp-banger2.c
file_close should call the read_handler if set so we can free
[thirdparty/squid.git] / test-suite / tcp-banger2.c
CommitLineData
58a96f63 1#include "config.h"
6a54c60e 2
58a96f63 3/*
4 * On some systems, FD_SETSIZE is set to something lower than the
5 * actual number of files which can be opened. IRIX is one case,
6 * NetBSD is another. So here we increase FD_SETSIZE to our
7 * configure-discovered maximum *before* any system includes.
8 */
9#define CHANGE_FD_SETSIZE 1
735246e8 10
58a96f63 11/* Cannot increase FD_SETSIZE on Linux */
12#if defined(_SQUID_LINUX_)
13#undef CHANGE_FD_SETSIZE
14#define CHANGE_FD_SETSIZE 0
15#endif
16
17/* Cannot increase FD_SETSIZE on FreeBSD before 2.2.0, causes select(2)
18 * to return EINVAL. */
19/* Marian Durkovic <marian@svf.stuba.sk> */
20/* Peter Wemm <peter@spinner.DIALix.COM> */
21#if defined(_SQUID_FREEBSD_)
22#include <osreldate.h>
23#if __FreeBSD_version < 220000
24#undef CHANGE_FD_SETSIZE
25#define CHANGE_FD_SETSIZE 0
26#endif
27#endif
28
29/* Increase FD_SETSIZE if SQUID_MAXFD is bigger */
30#if CHANGE_FD_SETSIZE && SQUID_MAXFD > DEFAULT_FD_SETSIZE
31#define FD_SETSIZE SQUID_MAXFD
32#endif
33
34#if HAVE_UNISTD_H
735246e8 35#include <unistd.h>
58a96f63 36#endif
37#if HAVE_STDLIB_H
735246e8 38#include <stdlib.h>
58a96f63 39#endif
40#if HAVE_STDIO_H
41#include <stdio.h>
42#endif
43#if HAVE_FCNTL_H
735246e8 44#include <fcntl.h>
58a96f63 45#endif
46#ifdef HAVE_STRING_H
735246e8 47#include <string.h>
58a96f63 48#endif
49#ifdef HAVE_STRINGS_H
735246e8 50#include <strings.h>
58a96f63 51#endif
52#if HAVE_SYS_TYPES_H
735246e8 53#include <sys/types.h>
58a96f63 54#endif
55#if HAVE_SYS_SELECT_H
735246e8 56#include <sys/select.h>
58a96f63 57#endif
58#if HAVE_SIGNAL_H
735246e8 59#include <signal.h>
58a96f63 60#endif
61#if HAVE_TIME_H
735246e8 62#include <time.h>
58a96f63 63#endif
64#if HAVE_SYS_TIME_H
735246e8 65#include <sys/time.h>
58a96f63 66#endif
67#if HAVE_SYS_SOCKET_H
735246e8 68#include <sys/socket.h>
58a96f63 69#endif
70#if HAVE_NETINET_IN_H
735246e8 71#include <netinet/in.h>
58a96f63 72#endif
73#if HAVE_ARPA_INET_H
735246e8 74#include <arpa/inet.h>
58a96f63 75#endif
76#if HAVE_ERRNO_H
735246e8 77#include <errno.h>
58a96f63 78#endif
14ff08f1 79#if HAVE_SYS_STAT_H
80#include <sys/stat.h>
81#endif
82#if HAVE_ASSERT_H
83#include <assert.h>
84#endif
735246e8 85
86#define PROXY_PORT 3128
87#define PROXY_ADDR "127.0.0.1"
88#define MAX_FDS 1024
89#define READ_BUF_SZ 4096
90
91static int proxy_port = PROXY_PORT;
92static char *proxy_addr = PROXY_ADDR;
93static char *progname;
94static int reqpersec;
95static int nrequests;
96static int opt_ims = 0;
c4248266 97static int opt_range = 0;
735246e8 98static int max_connections = 64;
33fda8cd 99static time_t lifetime = 60;
15dd95c1 100static time_t process_lifetime = 86400;
33fda8cd 101static struct timeval now;
14ff08f1 102static long total_bytes_written = 0;
103static long total_bytes_read = 0;
104static int opt_checksum = 0;
ed2ab7db 105FILE *trace_file = NULL;
735246e8 106
107typedef void (CB) (int, void *);
108
109struct _f {
110 CB *cb;
14ff08f1 111 CB *ccb;
735246e8 112 void *data;
33fda8cd 113 time_t start;
735246e8 114};
14ff08f1 115struct _request {
116 int fd;
117 char url[8192];
ed2ab7db 118 char method[16];
119 char requestbodyfile[256];
14ff08f1 120 char buf[READ_BUF_SZ * 2 + 1];
121 int headfound;
2444b689 122 int validsize;
123 int bodysize;
124 int content_length;
14ff08f1 125 long validsum;
14ff08f1 126 long sum;
14ff08f1 127};
735246e8 128
129struct _f FD[MAX_FDS];
130int nfds = 0;
131int maxfd = 0;
132
133
134char *
135mkrfc850(t)
6a54c60e 136 time_t *t;
735246e8 137{
138 static char buf[128];
139 struct tm *gmt = gmtime(t);
140 buf[0] = '\0';
141 (void) strftime(buf, 127, "%A, %d-%b-%y %H:%M:%S GMT", gmt);
142 return buf;
143}
144
145void
146fd_close(int fd)
147{
148 close(fd);
14ff08f1 149 if (FD[fd].ccb)
150 FD[fd].ccb(fd, FD[fd].data);
151 FD[fd].ccb = NULL;
735246e8 152 FD[fd].cb = NULL;
153 FD[fd].data = NULL;
154 nfds--;
155 if (fd == maxfd) {
aeb649da 156 while (fd > 0 && FD[fd].cb == NULL)
6a54c60e 157 fd--;
735246e8 158 maxfd = fd;
159 }
160}
161
162void
14ff08f1 163fd_open(int fd, CB * cb, void *data, CB *ccb)
735246e8 164{
165 FD[fd].cb = cb;
14ff08f1 166 FD[fd].ccb = ccb;
735246e8 167 FD[fd].data = data;
33fda8cd 168 FD[fd].start = now.tv_sec;
735246e8 169 if (fd > maxfd)
170 maxfd = fd;
171 nfds++;
172}
173
174void
175sig_intr(int sig)
176{
6a54c60e 177 fd_close(0);
14ff08f1 178 nfds++;
6a54c60e 179 printf("\rWaiting for open connections to finish...\n");
180 signal(sig, SIG_DFL);
735246e8 181}
182
6a54c60e 183void
735246e8 184read_reply(int fd, void *data)
185{
14ff08f1 186 struct _request *r = data;
187 static unsigned char buf[READ_BUF_SZ];
188 int len;
14ff08f1 189 if ((len=read(fd, buf, READ_BUF_SZ)) <= 0) {
6a54c60e 190 fd_close(fd);
191 reqpersec++;
192 nrequests++;
14ff08f1 193 } else {
194 int used=0;
195 total_bytes_read+=len;
196 if (r->headfound < 2) {
197 char *p,*header = NULL;
198 int oldlen = strlen(r->buf);
199 int newlen = oldlen + len;
200 assert(oldlen <= READ_BUF_SZ);
201 memcpy(r->buf+oldlen, buf, len);
202 r->buf[newlen+1]='\0';
203 for(p=r->buf; r->headfound < 2 && used<newlen; p++,used++) {
204 switch(*p) {
205 case '\n':
206 r->headfound++;
207 if (header) {
208 /* Decode header */
209 if (strncasecmp(header,"Content-Length:",15)==0)
210 r->content_length = atoi(header+15);
211 if (strncasecmp(header,"X-Request-URI:",14)==0) {
212 /* Check URI */
213 if (strncmp(r->url, header+15, strcspn(header+15,"\r\n"))) {
214 char url[8192];
215 strncpy(url, header+15, strcspn(header+15,"\r\n"));
216 url[strcspn(header+15, "\r\n")]='\n';
217 fprintf(stderr,"ERROR: Sent %s received %s\n",
218 r->url, url);
219 }
220 }
221 header=NULL;
222 }
223 break;
224 case '\r':
225 break;
226 default:
227 r->headfound=0;
228 if (!header)
229 header = p;
230 break;
231 }
232 }
233 if (header) {
234 memmove(r->buf, header, newlen - (header - r->buf) + 1);
235 }
236 }
237 r->bodysize+=len-used;
238 if (opt_checksum) {
239 for (; used<len ; used++) {
240 r->sum += buf[used];
241 }
242 }
6a54c60e 243 }
735246e8 244}
245
14ff08f1 246void
247reply_done(int fd, void *data)
735246e8 248{
14ff08f1 249 struct _request *r = data;
c4248266 250 if (opt_range)
251 ; /* skip size checks for now */
2444b689 252 else if (r->bodysize != r->content_length && r->content_length >= 0)
253 fprintf(stderr,"ERROR: %s got %d of %d bytes\n",
254 r->url, r->bodysize, r->content_length);
14ff08f1 255 else if (r->validsize >= 0) {
256 if (r->validsize != r->bodysize)
257 fprintf(stderr,"WARNING: %s size mismatch wanted %d bytes got %d\n",
258 r->url, r->validsize, r->bodysize);
259 else if (opt_checksum && r->validsum != r->sum)
2444b689 260 fprintf(stderr,"WARNING: %s invalid checksum wanted 0x%lx got 0x%lx\n",
14ff08f1 261 r->url, r->validsum, r->sum);
ed2ab7db 262 }
263 if (trace_file) {
2444b689 264 fprintf(trace_file,"%s %s %s %d 0x%lx\n",
ed2ab7db 265 r->method, r->url, r->requestbodyfile, r->bodysize, r->sum);
14ff08f1 266 }
267 free(r);
268}
269
270struct _request *
271request(char *urlin)
272{
273 int s=-1,f=-1;
735246e8 274 char buf[4096];
14ff08f1 275 char msg[8192];
276 char *method, *url, *file, *size, *checksum;
277 char urlbuf[8192];
278 int len,len2;
735246e8 279 time_t w;
14ff08f1 280 struct stat st;
735246e8 281 struct sockaddr_in S;
14ff08f1 282 struct _request *r;
735246e8 283 if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
284 perror("socket");
14ff08f1 285 return NULL;
735246e8 286 }
287 memset(&S, '\0', sizeof(struct sockaddr_in));
288 S.sin_family = AF_INET;
289 S.sin_port = htons(proxy_port);
290 S.sin_addr.s_addr = inet_addr(proxy_addr);
6a54c60e 291 if (connect(s, (struct sockaddr *) &S, sizeof(S)) < 0) {
735246e8 292 close(s);
293 perror("connect");
14ff08f1 294 return NULL;
735246e8 295 }
14ff08f1 296 strcpy(urlbuf,urlin);
297 method=strtok(urlbuf," ");
298 url=strtok(NULL," ");
299 file=strtok(NULL," ");
300 size=strtok(NULL," ");
301 checksum=strtok(NULL," ");
302 if (!url) {
303 url=method;
304 method="GET";
305 }
ed2ab7db 306 if (!file)
307 file="-";
308 if (!size)
309 size="-";
310 if (!checksum)
311 checksum="-";
14ff08f1 312 r=calloc(1,sizeof *r);
313 assert(r!=NULL);
314 strcpy(r->url, url);
ed2ab7db 315 strcpy(r->method, method);
316 strcpy(r->requestbodyfile, file);
14ff08f1 317 r->fd = s;
318 if (size && strcmp(size,"-")!=0)
319 r->validsize=atoi(size);
320 else
321 r->validsize=-1; /* Unknown */
322 if (checksum && strcmp(checksum,"-")!=0)
2444b689 323 r->validsum=strtoul(checksum,NULL,0);
ed2ab7db 324 r->content_length=-1; /* Unknown */
14ff08f1 325 msg[0] = '\0';
326 sprintf(buf,"%s %s HTTP/1.0\r\n", method, url);
327 strcat(msg, buf);
328 strcat(msg, "Accept: */*\r\n");
735246e8 329 if (opt_ims && (lrand48() & 0x03) == 0) {
330 w = time(NULL) - (lrand48() & 0x3FFFF);
14ff08f1 331 sprintf(buf, "If-Modified-Since: %s\r\n", mkrfc850(&w));
332 strcat(msg,buf);
735246e8 333 }
14ff08f1 334 if (file && strcmp(file, "-")!=0) {
335 f = open(file,O_RDONLY);
336 if (f < 0) {
337 perror("open file");
338 exit(1);
339 }
340 fstat(f, &st);
2444b689 341 sprintf(buf,"Content-Length: %d\r\n", (int)st.st_size);
14ff08f1 342 strcat(msg,buf);
343 }
c4248266 344 if (opt_range && (lrand48() & 0x03) == 0) {
345 int len;
346 int count = 0;
347 strcat(msg, "Range: bytes=");
348 while (((len = (int)lrand48()) & 0x03) == 0 || !count) {
349 const int offset = (int) lrand48();
350 if (count)
351 strcat(msg, ",");
352 switch (lrand48() & 0x03) {
353 case 0:
354 sprintf(buf, "-%d", len);
355 break;
356 case 1:
357 sprintf(buf, "%d-", offset);
358 break;
359 default:
360 sprintf(buf, "%d-%d", offset, offset+len);
361 break;
362 }
363 strcat(msg,buf);
364 count++;
365 }
366 strcat(msg,"\r\n");
367 }
14ff08f1 368 strcat(msg, "\r\n");
369 len = strlen(msg);
370 if ((len2=write(s, msg, len)) != len) {
735246e8 371 close(s);
14ff08f1 372 perror("write request");
373 free(r);
374 return NULL;
375 } else
376 total_bytes_written += len2;
377 if (f>=0) {
378 while ((len = read(f, buf, sizeof(buf)))>0) {
379 len2 = write(s, buf, len);
380 if (len2 < 0) {
381 perror("write body");
382 close(s);
383 free(r);
384 }
385 }
386 if (len < 0) {
387 perror("read body");
388 exit(1);
389 }
735246e8 390 }
14ff08f1 391
735246e8 392/*
6a54c60e 393 * if (fcntl(s, F_SETFL, O_NDELAY) < 0)
394 * perror("fcntl O_NDELAY");
395 */
14ff08f1 396 return r;
735246e8 397}
398
399void
400read_url(int fd, void *junk)
401{
14ff08f1 402 struct _request *r;
735246e8 403 static char buf[8192];
404 char *t;
735246e8 405 if (fgets(buf, 8191, stdin) == NULL) {
406 printf("Done Reading URLS\n");
407 fd_close(0);
14ff08f1 408 nfds++;
735246e8 409 return;
410 }
411 if ((t = strchr(buf, '\n')))
412 *t = '\0';
14ff08f1 413 r = request(buf);
414 if (!r) {
6a54c60e 415 max_connections = nfds - 1;
735246e8 416 printf("NOTE: max_connections set at %d\n", max_connections);
14ff08f1 417 } else {
418 fd_open(r->fd, read_reply, r, reply_done);
735246e8 419 }
735246e8 420}
421
422void
423usage(void)
424{
ed2ab7db 425 fprintf(stderr, "usage: %s: -p port -h host -n max\n", progname);
426 fprintf(stderr, " -t <tracefile> Save request trace\n");
427 fprintf(stderr, " -c Check checksum agains trace\n");
428 fprintf(stderr, " -i Send random If-Modified-Since times\n");
429 fprintf(stderr, " -l <seconds> Connection lifetime timeout (default 60)\n");
735246e8 430}
431
6a54c60e 432int
735246e8 433main(argc, argv)
6a54c60e 434 int argc;
435 char *argv[];
735246e8 436{
437 int i;
438 int c;
439 int dt;
14ff08f1 440 int j;
441 fd_set R,R2;
735246e8 442 struct timeval start;
735246e8 443 struct timeval last;
444 struct timeval to;
445 setbuf(stdout, NULL);
446 setbuf(stderr, NULL);
447 progname = strdup(argv[0]);
14ff08f1 448 gettimeofday(&now, NULL);
449 start = last = now;
15dd95c1 450 while ((c = getopt(argc, argv, "p:h:n:icrl:L:t:")) != -1) {
6a54c60e 451 switch (c) {
735246e8 452 case 'p':
6a54c60e 453 proxy_port = atoi(optarg);
454 break;
735246e8 455 case 'h':
6a54c60e 456 proxy_addr = strdup(optarg);
457 break;
735246e8 458 case 'n':
6a54c60e 459 max_connections = atoi(optarg);
460 break;
735246e8 461 case 'i':
6a54c60e 462 opt_ims = 1;
463 break;
33fda8cd 464 case 'l':
465 lifetime = (time_t) atoi(optarg);
466 break;
15dd95c1 467 case 'L':
468 process_lifetime = (time_t) atoi(optarg);
469 break;
14ff08f1 470 case 'c':
471 opt_checksum = 1;
472 break;
ed2ab7db 473 case 't':
474 opt_checksum = 1; /* Tracing requires checksums */
475 trace_file = fopen(optarg,"w");
476 assert(trace_file);
477 break;
c4248266 478 case 'r':
479 opt_range = 1;
480 break;
735246e8 481 default:
6a54c60e 482 usage();
483 return 1;
484 }
735246e8 485 }
14ff08f1 486 fd_open(0, read_url, NULL, NULL);
487 nfds--;
735246e8 488 signal(SIGINT, sig_intr);
5616e319 489 signal(SIGPIPE, SIG_IGN);
14ff08f1 490 FD_ZERO(&R2);
491 while (nfds || FD[0].cb) {
735246e8 492 FD_ZERO(&R);
493 to.tv_sec = 0;
494 to.tv_usec = 100000;
495 if (nfds < max_connections && FD[0].cb)
496 FD_SET(0, &R);
497 for (i = 1; i <= maxfd; i++) {
498 if (FD[i].cb == NULL)
499 continue;
33fda8cd 500 if (now.tv_sec - FD[i].start > lifetime) {
a19d9329 501 fprintf(stderr, "WARNING: fd %d lifetime timeout\n", i);
33fda8cd 502 fd_close(i);
503 continue;
504 }
735246e8 505 FD_SET(i, &R);
506 }
507 if (select(maxfd + 1, &R, NULL, NULL, &to) < 0) {
14ff08f1 508 fprintf(stderr, "maxfd=%d\n", maxfd);
735246e8 509 if (errno != EINTR)
6a54c60e 510 perror("select");
735246e8 511 continue;
512 }
14ff08f1 513 gettimeofday(&now, NULL);
735246e8 514 for (i = 0; i <= maxfd; i++) {
515 if (!FD_ISSET(i, &R))
516 continue;
517 FD[i].cb(i, FD[i].data);
14ff08f1 518 if (nfds < max_connections && FD[0].cb) {
519 j=0;
520 FD_SET(0,&R2);
521 to.tv_sec=0;
522 to.tv_usec=0;
523 if(select(1,&R2,NULL,NULL,&to) == 1)
524 FD[0].cb(0, FD[0].data);
525 }
735246e8 526 }
735246e8 527 if (now.tv_sec > last.tv_sec) {
6a54c60e 528 last = now;
529 dt = (int) (now.tv_sec - start.tv_sec);
14ff08f1 530 printf("T+ %6d: %9d req (%+4d), %4d conn, %3d/sec avg, %dmb, %dkb/sec avg\n",
6a54c60e 531 dt,
532 nrequests,
533 reqpersec,
534 nfds,
14ff08f1 535 (int) (nrequests / dt),
536 (int)total_bytes_read / 1024 / 1024,
537 (int)total_bytes_read / 1024 / dt);
6a54c60e 538 reqpersec = 0;
15dd95c1 539 if (dt > process_lifetime)
540 exit(0);
735246e8 541 }
542 }
543 return 0;
544}