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