]> git.ipfire.org Git - thirdparty/squid.git/blame - test-suite/pconn-banger.c
Import IPv6 support from squid3-ipv6 branch to 3-HEAD.
[thirdparty/squid.git] / test-suite / pconn-banger.c
CommitLineData
47130615 1
eb5f55b3 2#include "config.h"
3
4/*
5 * On some systems, FD_SETSIZE is set to something lower than the
6 * actual number of files which can be opened. IRIX is one case,
7 * NetBSD is another. So here we increase FD_SETSIZE to our
8 * configure-discovered maximum *before* any system includes.
9 */
10#define CHANGE_FD_SETSIZE 1
11
12/* Cannot increase FD_SETSIZE on Linux */
13#if defined(_SQUID_LINUX_)
14#undef CHANGE_FD_SETSIZE
15#define CHANGE_FD_SETSIZE 0
16#endif
17
18/* Cannot increase FD_SETSIZE on FreeBSD before 2.2.0, causes select(2)
19 * to return EINVAL. */
20/* Marian Durkovic <marian@svf.stuba.sk> */
21/* Peter Wemm <peter@spinner.DIALix.COM> */
22#if defined(_SQUID_FREEBSD_)
23#include <osreldate.h>
24#if __FreeBSD_version < 220000
25#undef CHANGE_FD_SETSIZE
26#define CHANGE_FD_SETSIZE 0
27#endif
28#endif
29
30/* Increase FD_SETSIZE if SQUID_MAXFD is bigger */
31#if CHANGE_FD_SETSIZE && SQUID_MAXFD > DEFAULT_FD_SETSIZE
32#define FD_SETSIZE SQUID_MAXFD
33#endif
34
35#if HAVE_UNISTD_H
36#include <unistd.h>
37#endif
38#if HAVE_STDLIB_H
39#include <stdlib.h>
40#endif
41#if HAVE_STDIO_H
42#include <stdio.h>
43#endif
44#if HAVE_FCNTL_H
45#include <fcntl.h>
46#endif
47#ifdef HAVE_STRING_H
48#include <string.h>
49#endif
50#ifdef HAVE_STRINGS_H
51#include <strings.h>
52#endif
53#ifdef HAVE_BSTRING_H
54#include <bstring.h>
55#endif
56#if HAVE_SYS_TYPES_H
57#include <sys/types.h>
58#endif
59#if HAVE_SYS_SELECT_H
60#include <sys/select.h>
61#endif
62#if HAVE_SIGNAL_H
63#include <signal.h>
64#endif
65#if HAVE_TIME_H
66#include <time.h>
67#endif
68#if HAVE_SYS_TIME_H
69#include <sys/time.h>
70#endif
71#if HAVE_SYS_SOCKET_H
72#include <sys/socket.h>
73#endif
74#if HAVE_NETINET_IN_H
75#include <netinet/in.h>
76#endif
77#if HAVE_ARPA_INET_H
78#include <arpa/inet.h>
79#endif
80#if HAVE_ERRNO_H
81#include <errno.h>
82#endif
83#if HAVE_CTYPE_H
84#include <ctype.h>
85#endif
86#if HAVE_ASSERT_H
87#include <assert.h>
88#endif
14ff08f1 89#if HAVE_SYS_STAT_H
90#include <sys/stat.h>
91#endif
eb5f55b3 92
cc192b50 93#include "getaddrinfo.h"
94
95#define PROXY_PORT "3128"
eb5f55b3 96#define PROXY_ADDR "127.0.0.1"
97#define MAX_FDS 1024
98#define READ_BUF_SZ 4096
99#define min(x,y) ((x)<(y)? (x) : (y))
100
101static int proxy_port = PROXY_PORT;
102static char *proxy_addr = PROXY_ADDR;
103static char *progname;
104static int noutstanding = 0;
105static int done_reading_urls = 0;
106static int opt_ims = 0;
14ff08f1 107static int opt_checksum = 0;
108static int opt_reopen = 1;
109static int max_outstanding = 10;
eb5f55b3 110static time_t lifetime = 60;
111static const char *const crlf = "\r\n";
14ff08f1 112static int trace_fd = -1;
113static int total_bytes_read = 0;
eb5f55b3 114
115#define REPLY_HDR_SZ 8192
116
117struct _r {
14ff08f1 118 char url[1024];
eb5f55b3 119 int content_length;
120 int hdr_length;
121 int hdr_offset;
122 int bytes_read;
123 char reply_hdrs[REPLY_HDR_SZ];
124 struct _r *next;
14ff08f1 125 long sum;
126 long validsize;
127 long validsum;
eb5f55b3 128};
129
130static struct _r *Requests;
131
132char *
133mkrfc850(t)
134 time_t *t;
135{
136 static char buf[128];
137 struct tm *gmt = gmtime(t);
138 buf[0] = '\0';
139 (void) strftime(buf, 127, "%A, %d-%b-%y %H:%M:%S GMT", gmt);
140 return buf;
141}
142
143
144char *
145mime_headers_end(const char *mime)
146{
147 const char *p1, *p2;
148 const char *end = NULL;
149
150 p1 = strstr(mime, "\n\r\n");
151 p2 = strstr(mime, "\n\n");
152
153 if (p1 && p2)
154 end = p1 < p2 ? p1 : p2;
155 else
156 end = p1 ? p1 : p2;
157 if (end)
158 end += (end == p1 ? 3 : 2);
159
160 return (char *) end;
161}
162
163void
164sig_intr(int sig)
165{
14ff08f1 166 fprintf(stderr, "\rWaiting for open connections to finish...\n");
eb5f55b3 167 signal(sig, SIG_DFL);
14ff08f1 168 done_reading_urls = 1;
eb5f55b3 169}
170
171int
172open_http_socket(void)
173{
174 int s;
cc192b50 175 struct addrinfo *AI = NULL;
176 struct addrinfo hints;
177
178 memset(&hints, '\0', sizeof(struct addrinfo));
179 hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV;
180 hints.ai_family = AF_UNSPEC;
181 hints.ai_socktype = SOCK_STREAM;
182
183 xgetaddrinfo(proxy_addr, proxy_port, &hints, AI);
184
185 if ((s = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0) {
eb5f55b3 186 perror("socket");
cc192b50 187 xfreeaddrinfo(AI);
eb5f55b3 188 return -1;
189 }
cc192b50 190
191 if (connect(s, AI->ai_addr, AI->ai_addrlen) < 0) {
eb5f55b3 192 close(s);
193 perror("connect");
cc192b50 194 xfreeaddrinfo(AI);
eb5f55b3 195 return -1;
196 }
cc192b50 197
198 xfreeaddrinfo(AI);
eb5f55b3 199 return s;
200}
201
202int
14ff08f1 203send_request(int fd, const char *data)
eb5f55b3 204{
468ae12b 205 char msg[4096], buf[4096];
eb5f55b3 206 int len;
207 time_t w;
208 struct _r *r;
209 struct _r **R;
14ff08f1 210 char *method, *url, *file, *size, *checksum;
211 char *tmp = strdup(data);
212 struct stat st;
213 int file_fd = -1;
468ae12b 214 method = strtok(tmp, " ");
215 url = strtok(NULL, " ");
216 file = strtok(NULL, " ");
217 size = strtok(NULL, " ");
218 checksum = strtok(NULL, " ");
14ff08f1 219 if (!url) {
468ae12b 220 url = method;
221 method = "GET";
14ff08f1 222 }
468ae12b 223 if (file && strcmp(file, "-") == 0)
224 file = NULL;
225 if (size && strcmp(size, "-") == 0)
226 size = NULL;
227 if (checksum && strcmp(checksum, "-") == 0)
228 checksum = NULL;
14ff08f1 229 msg[0] = '\0';
230 sprintf(buf, "%s %s HTTP/1.0\r\n", method, url);
468ae12b 231 strcat(msg, buf);
14ff08f1 232 strcat(msg, "Accept: */*\r\n");
233 strcat(msg, "Proxy-Connection: Keep-Alive\r\n");
eb5f55b3 234 if (opt_ims && (lrand48() & 0x03) == 0) {
235 w = time(NULL) - (lrand48() & 0x3FFFF);
14ff08f1 236 sprintf(buf, "If-Modified-Since: %s\r\n", mkrfc850(&w));
237 strcat(msg, buf);
eb5f55b3 238 }
14ff08f1 239 if (file) {
468ae12b 240 if ((file_fd = open(file, O_RDONLY)) < 0) {
14ff08f1 241 perror("open");
242 return -1;
243 }
468ae12b 244 if (fstat(file_fd, &st)) {
14ff08f1 245 perror("fstat");
246 close(file_fd);
247 return -1;
248 }
249 sprintf(buf, "Content-length: %d\r\n", st.st_size);
250 strcat(msg, buf);
251 }
252 strcat(msg, "\r\n");
253 len = strlen(msg);
254 if (write(fd, msg, len) < 0) {
eb5f55b3 255 close(fd);
14ff08f1 256 perror("request write");
257 close(file_fd);
eb5f55b3 258 return -1;
259 }
14ff08f1 260 if (file) {
468ae12b 261 while ((len = read(file_fd, buf, sizeof buf)) > 0) {
14ff08f1 262 if (write(fd, buf, len) < 0) {
263 close(fd);
264 perror("body write");
265 close(file_fd);
266 return -1;
267 }
268 }
269 if (len < 0) {
270 perror("file read");
271 close(file_fd);
272 return -1;
273 }
274 close(file_fd);
275 }
eb5f55b3 276 r = calloc(1, sizeof(struct _r));
14ff08f1 277 strcpy(r->url, url);
278 if (size)
279 r->validsize = atoi(size);
280 else
281 r->validsize = -1;
282 if (checksum)
283 r->validsum = atoi(checksum);
eb5f55b3 284 for (R = &Requests; *R; R = &(*R)->next);
285 *R = r;
14ff08f1 286/* fprintf(stderr, "REQUESTED %s\n", url); */
eb5f55b3 287 noutstanding++;
288 return 0;
289}
290
291static int
292get_header_int_value(const char *hdr, const char *buf, const char *end)
293{
294 const char *t;
295 for (t = buf; t < end; t += strcspn(t, crlf), t += strspn(t, crlf)) {
296 if (strncasecmp(t, hdr, strlen(hdr)) == 0) {
297 t += strlen(hdr);
e4755e29 298 while (xisspace(*t))
eb5f55b3 299 t++;
300 return atoi(t);
301 }
302 }
14ff08f1 303 return -1;
eb5f55b3 304}
305
14ff08f1 306static const char *
307get_header_string_value(const char *hdr, const char *buf, const char *end)
308{
309 const char *t;
310 static char result[8192];
311 for (t = buf; t < end; t += strcspn(t, crlf), t += strspn(t, crlf)) {
312 if (strncasecmp(t, hdr, strlen(hdr)) == 0) {
313 t += strlen(hdr);
e4755e29 314 while (xisspace(*t))
14ff08f1 315 t++;
468ae12b 316 strcpy(result, "");
317 strncat(result, t, strcspn(t, crlf));
14ff08f1 318 return result;
319 }
320 }
321 return NULL;
322}
323
324void
325request_done(struct _r *r)
326{
327#if 0
328 fprintf(stderr, "DONE: %s, (%d+%d)\n",
468ae12b 329 r->url,
330 r->hdr_length,
331 r->content_length);
14ff08f1 332#endif
333 if (r->content_length != r->bytes_read)
334 fprintf(stderr, "ERROR! Short reply, expected %d bytes got %d\n",
335 r->content_length, r->bytes_read);
336 else if (r->validsize >= 0) {
337 if (r->validsize != r->bytes_read)
338 fprintf(stderr, "WARNING: %s Object size mismatch, expected %d got %d\n",
468ae12b 339 r->url, r->validsize, r->bytes_read);
14ff08f1 340 else if (opt_checksum && r->sum != r->validsum)
341 fprintf(stderr, "WARNING: %s Checksum error. Expected %d got %d\n",
468ae12b 342 r->url, r->validsum, r->sum);
14ff08f1 343 }
344}
eb5f55b3 345int
14ff08f1 346handle_read(char *inbuf, int len)
eb5f55b3 347{
348 struct _r *r = Requests;
349 const char *end;
14ff08f1 350 const char *url;
351 static char buf[READ_BUF_SZ];
468ae12b 352 int hlen, blen;
353 if (len < 0) {
eb5f55b3 354 perror("read");
355 Requests = r->next;
14ff08f1 356 request_done(r);
e3fc4f10 357 free(r);
358 noutstanding--;
14ff08f1 359 if (trace_fd >= 0)
468ae12b 360 write(trace_fd, "\n[CLOSED]\n", 10);
eb5f55b3 361 return -1;
362 }
14ff08f1 363 total_bytes_read += len;
468ae12b 364 xmemcpy(buf, inbuf, len);
eb5f55b3 365 if (len == 0) {
14ff08f1 366 fprintf(stderr, "WARNING: %s, server closed socket after %d+%d bytes\n", r->url, r->hdr_offset, r->bytes_read);
367 /* XXX, If no data was received and it isn't the first request on this
368 * connection then the request should be restarted rather than aborted
369 * but this is a simple test program an not a full blown HTTP client.
370 */
371 request_done(r);
eb5f55b3 372 Requests = r->next;
373 free(r);
374 noutstanding--;
375 return -1;
376 }
14ff08f1 377 if (trace_fd > 0)
378 write(trace_fd, buf, len);
379 while (len > 0) {
380 /* Build headers */
381 if (r->hdr_length == 0) {
382 hlen = min(len, REPLY_HDR_SZ - r->hdr_offset - 1);
383 xmemcpy(r->reply_hdrs + r->hdr_offset, buf, hlen);
384 r->hdr_offset += hlen;
385 r->reply_hdrs[r->hdr_offset] = '\0';
386 len -= hlen;
387 /* Save any remaining read data */
388 xmemmove(buf, buf + hlen, len);
389 }
390 /* Process headers */
391 if (r->hdr_length == 0 && (end = mime_headers_end(r->reply_hdrs)) != NULL) {
392#if 0
468ae12b 393 fprintf(stderr, "FOUND EOH FOR %s\n", r->url);
394 */
14ff08f1 395#endif
468ae12b 396 r->hdr_length = end - r->reply_hdrs;
14ff08f1 397#if 0
468ae12b 398 fprintf(stderr, "HDR_LENGTH = %d\n", r->hdr_length);
14ff08f1 399#endif
400 /* "unread" any body contents received */
401 blen = r->hdr_offset - r->hdr_length;
402 assert(blen >= 0);
403 if (blen > 0) {
404 xmemmove(buf + blen, buf, len);
405 xmemcpy(buf, r->reply_hdrs + r->hdr_length, blen);
406 len += blen;
407 }
468ae12b 408 r->reply_hdrs[r->hdr_length] = '\0'; /* Null terminate headers */
14ff08f1 409 /* Parse headers */
410 r->content_length = get_header_int_value("content-length:", r->reply_hdrs, end);
468ae12b 411/* fprintf(stderr, "CONTENT_LENGTH = %d\n", r->content_length); */
14ff08f1 412 url = get_header_string_value("X-Request-URI:", r->reply_hdrs, end);
413 if (url != NULL && strcmp(r->url, url) != 0)
414 fprintf(stderr, "WARNING: %s got reply %s\n", r->url, url);
415#if XREQUESTURI || 0
468ae12b 416 fprintf(stderr, "LOCATION = %s\n", get_header_string_value("X-Request-URI:", r->reply_hdrs, end));
14ff08f1 417#endif
418 }
468ae12b 419 if (!(len == 0 || r->hdr_length > 0)) {
14ff08f1 420 fprintf(stderr, "ERROR!!!\n");
468ae12b 421 assert((len == 0 || r->hdr_length > 0));
eb5f55b3 422 }
14ff08f1 423 /* Process body */
424 if (r->hdr_length != 0) {
425 int i;
426 int bytes_left, bytes_used;
427 if (r->content_length >= 0) {
428 bytes_left = r->content_length - r->bytes_read;
429 assert(bytes_left >= 0);
468ae12b 430 bytes_used = len < bytes_left ? len : bytes_left;
14ff08f1 431 } else {
468ae12b 432 bytes_left = len + 1; /* Unknown end... */
14ff08f1 433 bytes_used = len;
434 }
435 if (opt_checksum) {
468ae12b 436 for (i = 0; i < bytes_used; i++)
437 r->sum += (int) buf[i] & 0xFF;
14ff08f1 438 }
439 r->bytes_read += bytes_used;
440 len -= bytes_used;
441 if (bytes_left == bytes_used) {
442 request_done(r);
443 Requests = r->next;
444 free(r);
445 noutstanding--;
446 r = Requests;
5a91f458 447 } else if (r->content_length > -1) {
14ff08f1 448 assert(r->bytes_read < r->content_length);
449 }
dbfed404 450 xmemmove(buf, buf + bytes_used, len);
eb5f55b3 451 }
452 }
453 return 0;
454}
455
456int
457read_reply(int fd)
458{
459 static char buf[READ_BUF_SZ];
460 int len;
461 int x;
462 len = read(fd, buf, READ_BUF_SZ);
463 x = handle_read(buf, len);
14ff08f1 464 if (x < 0) {
465 perror("read reply");
eb5f55b3 466 close(fd);
14ff08f1 467 }
eb5f55b3 468 return x;
469}
470
471void
472main_loop(void)
473{
474 static int pconn_fd = -1;
475 static char buf[8192];
476 struct timeval to;
468ae12b 477 struct timeval now, last, start;
eb5f55b3 478 fd_set R;
479 struct _r *r;
480 struct _r *nextr;
481 int x;
e3fc4f10 482 int timeouts;
14ff08f1 483 int nrequests = 0, rrequests = 0, reqpersec = 0;
484
485 gettimeofday(&start, NULL);
486 last = start;
487
488 pconn_fd = open_http_socket();
489 if (pconn_fd < 0) {
490 perror("socket");
491 exit(1);
492 }
eb5f55b3 493 while (!done_reading_urls || noutstanding) {
14ff08f1 494 if (!opt_reopen && pconn_fd < 0) {
468ae12b 495 fprintf(stderr, "TERMINATED: Connection closed\n");
14ff08f1 496 break;
e3fc4f10 497 }
468ae12b 498 if (pconn_fd < 0) {
eb5f55b3 499 pconn_fd = open_http_socket();
500 if (pconn_fd < 0) {
501 perror("socket");
502 exit(1);
503 }
504 nextr = Requests;
505 Requests = NULL;
468ae12b 506 noutstanding = 0;
eb5f55b3 507 while ((r = nextr) != NULL) {
508 nextr = r->next;
14ff08f1 509 if (send_request(pconn_fd, r->url) != 0) {
510 close(pconn_fd);
468ae12b 511 pconn_fd = -1;
14ff08f1 512 nextr = r;
468ae12b 513 for (r = Requests; r != NULL && r->next; r = r->next);
14ff08f1 514 if (r != NULL)
515 r->next = nextr;
516 else
517 Requests = nextr;
518 break;
519 }
eb5f55b3 520 free(r);
eb5f55b3 521 }
e3fc4f10 522 timeouts = 0;
468ae12b 523 if (pconn_fd < 0)
14ff08f1 524 continue;
eb5f55b3 525 }
14ff08f1 526 if (timeouts == 200) {
527 close(pconn_fd);
528 pconn_fd = -1;
529 r = Requests;
530 Requests = Requests->next;
531 fprintf(stderr, "ABORT %s\n", Requests->url);
532 free(r);
533 noutstanding--;
534 }
468ae12b 535 if (pconn_fd >= 0 && noutstanding < max_outstanding && !done_reading_urls) {
eb5f55b3 536 char *t;
537 if (fgets(buf, 8191, stdin) == NULL) {
14ff08f1 538 fprintf(stderr, "Done Reading URLS\n");
eb5f55b3 539 done_reading_urls = 1;
14ff08f1 540 continue;
eb5f55b3 541 }
14ff08f1 542 rrequests++;
eb5f55b3 543 if ((t = strchr(buf, '\n')))
544 *t = '\0';
14ff08f1 545 if (send_request(pconn_fd, buf) != 0) {
546 close(pconn_fd);
468ae12b 547 pconn_fd = -1;
14ff08f1 548 continue;
549 }
550 nrequests++;
551 reqpersec++;
e3fc4f10 552 timeouts = 0;
eb5f55b3 553 }
554 FD_ZERO(&R);
555 to.tv_sec = 1;
556 to.tv_usec = 0;
557 FD_SET(pconn_fd, &R);
558 x = select(pconn_fd + 1, &R, NULL, NULL, &to);
559 if (x < 0) {
560 if (errno != EINTR)
561 perror("select");
562 continue;
563 } else if (x == 0) {
564 assert(Requests != NULL);
e3fc4f10 565 fprintf(stderr, "TIMEOUT %s; %d, %d\n", Requests->url,
566 ++timeouts, noutstanding);
eb5f55b3 567 continue;
568 }
569 if (FD_ISSET(pconn_fd, &R)) {
e3fc4f10 570 timeouts = 0;
eb5f55b3 571 if (read_reply(pconn_fd) != 0)
572 pconn_fd = -1;
573 }
14ff08f1 574 gettimeofday(&now, NULL);
468ae12b 575 if (now.tv_sec > last.tv_sec) {
14ff08f1 576 int dt;
577 int nreq;
578 last = now;
579 dt = (int) (now.tv_sec - start.tv_sec);
468ae12b 580 nreq = 0;
581 for (r = Requests; r; r = r->next)
582 nreq++;
14ff08f1 583 printf("T+ %6d: %9d req (%+4d), %4d pend, %3d/sec avg, %dmb, %dkb/sec avg\n",
468ae12b 584 dt,
585 nrequests,
586 reqpersec,
587 nreq,
588 (int) (nrequests / dt),
589 (int) total_bytes_read / 1024 / 1024,
590 (int) total_bytes_read / 1024 / dt);
14ff08f1 591 reqpersec = 0;
592 }
eb5f55b3 593 }
594}
595
596void
597usage(void)
598{
14ff08f1 599 fprintf(stderr, "usage: %s: -p port -h host -n max -t tracefile -i -c -l lifetime\n", progname);
eb5f55b3 600}
601
602int
603main(argc, argv)
604 int argc;
605 char *argv[];
606{
607 int c;
608 setbuf(stdout, NULL);
609 setbuf(stderr, NULL);
610 progname = strdup(argv[0]);
14ff08f1 611 while ((c = getopt(argc, argv, "p:h:n:t:icl:r")) != -1) {
eb5f55b3 612 switch (c) {
613 case 'p':
614 proxy_port = atoi(optarg);
615 break;
616 case 'h':
617 proxy_addr = strdup(optarg);
618 break;
619 case 'n':
14ff08f1 620 max_outstanding = atoi(optarg);
eb5f55b3 621 break;
622 case 'i':
623 opt_ims = 1;
624 break;
14ff08f1 625 case 'c':
626 opt_checksum = 1;
627 break;
eb5f55b3 628 case 'l':
629 lifetime = (time_t) atoi(optarg);
630 break;
14ff08f1 631 case 't':
468ae12b 632 trace_fd = open(optarg, O_WRONLY | O_CREAT | O_TRUNC, 0666);
14ff08f1 633 break;
634 case 'r':
635 opt_reopen = !opt_reopen;
636 break;
eb5f55b3 637 default:
638 usage();
639 return 1;
640 }
641 }
642 signal(SIGINT, sig_intr);
5616e319 643 signal(SIGPIPE, SIG_IGN);
eb5f55b3 644 main_loop();
645 return 0;
646}