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