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