]> git.ipfire.org Git - thirdparty/squid.git/blame - test-suite/tcp-banger2.c
From: Robert Side <rside@aiinc.bc.ca>
[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"
735246e8 88#define READ_BUF_SZ 4096
89
90static int proxy_port = PROXY_PORT;
91static char *proxy_addr = PROXY_ADDR;
92static char *progname;
93static int reqpersec;
94static int nrequests;
95static int opt_ims = 0;
c4248266 96static int opt_range = 0;
735246e8 97static int max_connections = 64;
33fda8cd 98static time_t lifetime = 60;
15dd95c1 99static time_t process_lifetime = 86400;
33fda8cd 100static struct timeval now;
14ff08f1 101static long total_bytes_written = 0;
102static long total_bytes_read = 0;
103static int opt_checksum = 0;
55095043 104static int accepted_status = 200;
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;
7d97d03c 117 char *url;
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;
55095043 125 int status;
126 int validstatus;
14ff08f1 127 long validsum;
14ff08f1 128 long sum;
14ff08f1 129};
735246e8 130
b61d6f3d 131struct _f FD[SQUID_MAXFD];
735246e8 132int nfds = 0;
133int maxfd = 0;
134
135
7d97d03c 136static void
137free_request(struct _request *r)
138{
55095043 139 if (r->url)
140 free(r->url);
141 free(r);
7d97d03c 142}
143
735246e8 144char *
145mkrfc850(t)
6a54c60e 146 time_t *t;
735246e8 147{
148 static char buf[128];
149 struct tm *gmt = gmtime(t);
150 buf[0] = '\0';
151 (void) strftime(buf, 127, "%A, %d-%b-%y %H:%M:%S GMT", gmt);
152 return buf;
153}
154
155void
156fd_close(int fd)
157{
158 close(fd);
14ff08f1 159 if (FD[fd].ccb)
160 FD[fd].ccb(fd, FD[fd].data);
161 FD[fd].ccb = NULL;
735246e8 162 FD[fd].cb = NULL;
163 FD[fd].data = NULL;
164 nfds--;
165 if (fd == maxfd) {
aeb649da 166 while (fd > 0 && FD[fd].cb == NULL)
6a54c60e 167 fd--;
735246e8 168 maxfd = fd;
169 }
170}
171
172void
3cd80ef8 173fd_open(int fd, CB * cb, void *data, CB * ccb)
735246e8 174{
b61d6f3d 175 assert(fd < SQUID_MAXFD);
735246e8 176 FD[fd].cb = cb;
14ff08f1 177 FD[fd].ccb = ccb;
735246e8 178 FD[fd].data = data;
33fda8cd 179 FD[fd].start = now.tv_sec;
735246e8 180 if (fd > maxfd)
181 maxfd = fd;
182 nfds++;
183}
184
185void
186sig_intr(int sig)
187{
6a54c60e 188 fd_close(0);
14ff08f1 189 nfds++;
6a54c60e 190 printf("\rWaiting for open connections to finish...\n");
191 signal(sig, SIG_DFL);
735246e8 192}
193
6a54c60e 194void
735246e8 195read_reply(int fd, void *data)
196{
14ff08f1 197 struct _request *r = data;
198 static unsigned char buf[READ_BUF_SZ];
199 int len;
1707acbe 200 int used = 0;
3cd80ef8 201 if ((len = read(fd, buf, READ_BUF_SZ)) <= 0) {
6a54c60e 202 fd_close(fd);
203 reqpersec++;
204 nrequests++;
1707acbe 205 return;
206 }
207 total_bytes_read += len;
208 if (r->headfound < 2) {
209 char *p, *header = NULL;
210 int oldlen = strlen(r->buf);
211 int newlen = oldlen + len;
212 assert(oldlen <= READ_BUF_SZ);
213 memcpy(r->buf + oldlen, buf, len);
214 r->buf[newlen + 1] = '\0';
215 for (p = r->buf; r->headfound < 2 && used < newlen; p++, used++) {
216 switch (*p) {
217 case '\n':
218 r->headfound++;
219 if (!header)
14ff08f1 220 break;
1707acbe 221 /* Decode header */
55095043 222 if (strncasecmp(header, "HTTP/1", 6) == 0)
223 r->status = atoi(header + 9);
1707acbe 224 if (strncasecmp(header, "Content-Length:", 15) == 0)
225 r->content_length = atoi(header + 15);
226 if (strncasecmp(header, "X-Request-URI:", 14) == 0) {
227 /* Check URI */
228 if (strncmp(r->url, header + 15, strcspn(header + 15, "\r\n"))) {
229 char url[8192];
230 strncpy(url, header + 15, strcspn(header + 15, "\r\n"));
231 url[strcspn(header + 15, "\r\n")] = '\n';
232 fprintf(stderr, "ERROR: Sent %s received %s\n",
233 r->url, url);
234 }
14ff08f1 235 }
1707acbe 236 header = NULL;
237 break;
238 case '\r':
239 break;
240 default:
241 r->headfound = 0;
242 if (!header)
243 header = p;
244 break;
14ff08f1 245 }
14ff08f1 246 }
1707acbe 247 if (header) {
248 memmove(r->buf, header, newlen - (header - r->buf) + 1);
249 }
250 assert(used >= oldlen);
251 used -= oldlen;
252 }
253 r->bodysize += len - used;
254 if (opt_checksum) {
255 for (; used < len; used++) {
256 r->sum += buf[used];
14ff08f1 257 }
6a54c60e 258 }
735246e8 259}
260
14ff08f1 261void
262reply_done(int fd, void *data)
735246e8 263{
14ff08f1 264 struct _request *r = data;
3cd80ef8 265 if (opt_range); /* skip size checks for now */
2444b689 266 else if (r->bodysize != r->content_length && r->content_length >= 0)
3cd80ef8 267 fprintf(stderr, "ERROR: %s got %d of %d bytes\n",
268 r->url, r->bodysize, r->content_length);
14ff08f1 269 else if (r->validsize >= 0) {
270 if (r->validsize != r->bodysize)
3cd80ef8 271 fprintf(stderr, "WARNING: %s size mismatch wanted %d bytes got %d\n",
272 r->url, r->validsize, r->bodysize);
14ff08f1 273 else if (opt_checksum && r->validsum != r->sum)
3cd80ef8 274 fprintf(stderr, "WARNING: %s invalid checksum wanted 0x%lx got 0x%lx\n",
275 r->url, r->validsum, r->sum);
ed2ab7db 276 }
55095043 277 if (r->status != r->validstatus && r->validstatus)
278 fprintf(stderr, "WARNING: %s status %d\n", r->url, r->status);
ed2ab7db 279 if (trace_file) {
55095043 280 fprintf(trace_file, "%s %s %s %d 0x%lx %d\n",
281 r->method, r->url, r->requestbodyfile, r->bodysize, r->sum, r->status);
14ff08f1 282 }
7d97d03c 283 free_request(r);
14ff08f1 284}
285
286struct _request *
287request(char *urlin)
288{
3cd80ef8 289 int s = -1, f = -1;
735246e8 290 char buf[4096];
14ff08f1 291 char msg[8192];
55095043 292 char *method, *url, *file, *size, *checksum, *status;
14ff08f1 293 char urlbuf[8192];
3cd80ef8 294 int len, len2;
735246e8 295 time_t w;
14ff08f1 296 struct stat st;
735246e8 297 struct sockaddr_in S;
14ff08f1 298 struct _request *r;
206b24db 299 if (*urlin == '\0')
300 return NULL;
735246e8 301 if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
302 perror("socket");
14ff08f1 303 return NULL;
735246e8 304 }
305 memset(&S, '\0', sizeof(struct sockaddr_in));
306 S.sin_family = AF_INET;
307 S.sin_port = htons(proxy_port);
308 S.sin_addr.s_addr = inet_addr(proxy_addr);
6a54c60e 309 if (connect(s, (struct sockaddr *) &S, sizeof(S)) < 0) {
735246e8 310 close(s);
311 perror("connect");
14ff08f1 312 return NULL;
735246e8 313 }
3cd80ef8 314 strcpy(urlbuf, urlin);
315 method = strtok(urlbuf, " ");
316 url = strtok(NULL, " ");
317 file = strtok(NULL, " ");
318 size = strtok(NULL, " ");
319 checksum = strtok(NULL, " ");
55095043 320 status = strtok(NULL, " ");
14ff08f1 321 if (!url) {
3cd80ef8 322 url = method;
323 method = "GET";
14ff08f1 324 }
ed2ab7db 325 if (!file)
3cd80ef8 326 file = "-";
ed2ab7db 327 if (!size)
3cd80ef8 328 size = "-";
ed2ab7db 329 if (!checksum)
3cd80ef8 330 checksum = "-";
331 r = calloc(1, sizeof *r);
332 assert(r != NULL);
7d97d03c 333 r->url = strdup(url);
334 assert(r->url);
ed2ab7db 335 strcpy(r->method, method);
336 strcpy(r->requestbodyfile, file);
14ff08f1 337 r->fd = s;
3cd80ef8 338 if (size && strcmp(size, "-") != 0)
339 r->validsize = atoi(size);
14ff08f1 340 else
3cd80ef8 341 r->validsize = -1; /* Unknown */
342 if (checksum && strcmp(checksum, "-") != 0)
343 r->validsum = strtoul(checksum, NULL, 0);
344 r->content_length = -1; /* Unknown */
55095043 345 if (status && strcmp(status, "-") != 0)
346 r->validstatus = strtoul(status, NULL, 0);
347 else
348 r->validstatus = accepted_status;
14ff08f1 349 msg[0] = '\0';
3cd80ef8 350 sprintf(buf, "%s %s HTTP/1.0\r\n", method, url);
14ff08f1 351 strcat(msg, buf);
352 strcat(msg, "Accept: */*\r\n");
735246e8 353 if (opt_ims && (lrand48() & 0x03) == 0) {
354 w = time(NULL) - (lrand48() & 0x3FFFF);
14ff08f1 355 sprintf(buf, "If-Modified-Since: %s\r\n", mkrfc850(&w));
3cd80ef8 356 strcat(msg, buf);
735246e8 357 }
3cd80ef8 358 if (file && strcmp(file, "-") != 0) {
359 f = open(file, O_RDONLY);
14ff08f1 360 if (f < 0) {
361 perror("open file");
362 exit(1);
363 }
364 fstat(f, &st);
3cd80ef8 365 sprintf(buf, "Content-Length: %d\r\n", (int) st.st_size);
366 strcat(msg, buf);
14ff08f1 367 }
c4248266 368 if (opt_range && (lrand48() & 0x03) == 0) {
369 int len;
370 int count = 0;
371 strcat(msg, "Range: bytes=");
3cd80ef8 372 while (((len = (int) lrand48()) & 0x03) == 0 || !count) {
c4248266 373 const int offset = (int) lrand48();
374 if (count)
375 strcat(msg, ",");
376 switch (lrand48() & 0x03) {
3cd80ef8 377 case 0:
378 sprintf(buf, "-%d", len);
379 break;
380 case 1:
381 sprintf(buf, "%d-", offset);
382 break;
383 default:
384 sprintf(buf, "%d-%d", offset, offset + len);
385 break;
c4248266 386 }
3cd80ef8 387 strcat(msg, buf);
c4248266 388 count++;
389 }
3cd80ef8 390 strcat(msg, "\r\n");
c4248266 391 }
14ff08f1 392 strcat(msg, "\r\n");
393 len = strlen(msg);
3cd80ef8 394 if ((len2 = write(s, msg, len)) != len) {
735246e8 395 close(s);
14ff08f1 396 perror("write request");
7d97d03c 397 free_request(r);
14ff08f1 398 return NULL;
399 } else
400 total_bytes_written += len2;
3cd80ef8 401 if (f >= 0) {
402 while ((len = read(f, buf, sizeof(buf))) > 0) {
14ff08f1 403 len2 = write(s, buf, len);
404 if (len2 < 0) {
405 perror("write body");
406 close(s);
7d97d03c 407 free_request(r);
14ff08f1 408 }
409 }
410 if (len < 0) {
411 perror("read body");
412 exit(1);
413 }
735246e8 414 }
415/*
6a54c60e 416 * if (fcntl(s, F_SETFL, O_NDELAY) < 0)
417 * perror("fcntl O_NDELAY");
418 */
14ff08f1 419 return r;
735246e8 420}
421
422void
423read_url(int fd, void *junk)
424{
14ff08f1 425 struct _request *r;
735246e8 426 static char buf[8192];
427 char *t;
206b24db 428 buf[0] = '\0';
735246e8 429 if (fgets(buf, 8191, stdin) == NULL) {
430 printf("Done Reading URLS\n");
431 fd_close(0);
14ff08f1 432 nfds++;
735246e8 433 return;
434 }
206b24db 435 if (buf[0] == '\0')
436 return;
735246e8 437 if ((t = strchr(buf, '\n')))
438 *t = '\0';
14ff08f1 439 r = request(buf);
440 if (!r) {
6a54c60e 441 max_connections = nfds - 1;
735246e8 442 printf("NOTE: max_connections set at %d\n", max_connections);
14ff08f1 443 } else {
444 fd_open(r->fd, read_reply, r, reply_done);
735246e8 445 }
735246e8 446}
447
448void
449usage(void)
450{
ed2ab7db 451 fprintf(stderr, "usage: %s: -p port -h host -n max\n", progname);
452 fprintf(stderr, " -t <tracefile> Save request trace\n");
453 fprintf(stderr, " -c Check checksum agains trace\n");
454 fprintf(stderr, " -i Send random If-Modified-Since times\n");
455 fprintf(stderr, " -l <seconds> Connection lifetime timeout (default 60)\n");
55095043 456 fprintf(stderr, " -s <status> HTTP status expected (default 200, 0 == ignore)\n");
735246e8 457}
458
6a54c60e 459int
735246e8 460main(argc, argv)
6a54c60e 461 int argc;
462 char *argv[];
735246e8 463{
464 int i;
465 int c;
466 int dt;
14ff08f1 467 int j;
3cd80ef8 468 fd_set R, R2;
735246e8 469 struct timeval start;
735246e8 470 struct timeval last;
471 struct timeval to;
472 setbuf(stdout, NULL);
473 setbuf(stderr, NULL);
474 progname = strdup(argv[0]);
14ff08f1 475 gettimeofday(&now, NULL);
476 start = last = now;
15dd95c1 477 while ((c = getopt(argc, argv, "p:h:n:icrl:L:t:")) != -1) {
6a54c60e 478 switch (c) {
735246e8 479 case 'p':
6a54c60e 480 proxy_port = atoi(optarg);
481 break;
735246e8 482 case 'h':
6a54c60e 483 proxy_addr = strdup(optarg);
484 break;
735246e8 485 case 'n':
6a54c60e 486 max_connections = atoi(optarg);
487 break;
735246e8 488 case 'i':
6a54c60e 489 opt_ims = 1;
490 break;
33fda8cd 491 case 'l':
492 lifetime = (time_t) atoi(optarg);
493 break;
15dd95c1 494 case 'L':
495 process_lifetime = (time_t) atoi(optarg);
496 break;
14ff08f1 497 case 'c':
498 opt_checksum = 1;
499 break;
ed2ab7db 500 case 't':
3cd80ef8 501 opt_checksum = 1; /* Tracing requires checksums */
502 trace_file = fopen(optarg, "w");
ed2ab7db 503 assert(trace_file);
504 break;
c4248266 505 case 'r':
506 opt_range = 1;
507 break;
735246e8 508 default:
6a54c60e 509 usage();
510 return 1;
511 }
735246e8 512 }
14ff08f1 513 fd_open(0, read_url, NULL, NULL);
514 nfds--;
735246e8 515 signal(SIGINT, sig_intr);
5616e319 516 signal(SIGPIPE, SIG_IGN);
14ff08f1 517 FD_ZERO(&R2);
518 while (nfds || FD[0].cb) {
735246e8 519 FD_ZERO(&R);
520 to.tv_sec = 0;
521 to.tv_usec = 100000;
522 if (nfds < max_connections && FD[0].cb)
523 FD_SET(0, &R);
524 for (i = 1; i <= maxfd; i++) {
525 if (FD[i].cb == NULL)
526 continue;
33fda8cd 527 if (now.tv_sec - FD[i].start > lifetime) {
a19d9329 528 fprintf(stderr, "WARNING: fd %d lifetime timeout\n", i);
33fda8cd 529 fd_close(i);
530 continue;
531 }
735246e8 532 FD_SET(i, &R);
533 }
534 if (select(maxfd + 1, &R, NULL, NULL, &to) < 0) {
14ff08f1 535 fprintf(stderr, "maxfd=%d\n", maxfd);
735246e8 536 if (errno != EINTR)
6a54c60e 537 perror("select");
735246e8 538 continue;
539 }
14ff08f1 540 gettimeofday(&now, NULL);
735246e8 541 for (i = 0; i <= maxfd; i++) {
542 if (!FD_ISSET(i, &R))
543 continue;
544 FD[i].cb(i, FD[i].data);
3cd80ef8 545 if (nfds < max_connections && FD[0].cb) {
546 j = 0;
547 FD_SET(0, &R2);
548 to.tv_sec = 0;
549 to.tv_usec = 0;
550 if (select(1, &R2, NULL, NULL, &to) == 1)
14ff08f1 551 FD[0].cb(0, FD[0].data);
552 }
735246e8 553 }
735246e8 554 if (now.tv_sec > last.tv_sec) {
6a54c60e 555 last = now;
556 dt = (int) (now.tv_sec - start.tv_sec);
14ff08f1 557 printf("T+ %6d: %9d req (%+4d), %4d conn, %3d/sec avg, %dmb, %dkb/sec avg\n",
6a54c60e 558 dt,
559 nrequests,
560 reqpersec,
561 nfds,
14ff08f1 562 (int) (nrequests / dt),
3cd80ef8 563 (int) total_bytes_read / 1024 / 1024,
564 (int) total_bytes_read / 1024 / dt);
6a54c60e 565 reqpersec = 0;
15dd95c1 566 if (dt > process_lifetime)
567 exit(0);
735246e8 568 }
569 }
570 return 0;
571}