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