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