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