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