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