pconn-banger.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2017 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 
11 #if HAVE_UNISTD_H
12 #include <unistd.h>
13 #endif
14 #if HAVE_FCNTL_H
15 #include <fcntl.h>
16 #endif
17 #if HAVE_STRING_H
18 #include <string.h>
19 #endif
20 #if HAVE_STRINGS_H
21 #include <strings.h>
22 #endif
23 #if HAVE_BSTRING_H
24 #include <bstring.h>
25 #endif
26 #if HAVE_SIGNAL_H
27 #include <signal.h>
28 #endif
29 #if HAVE_TIME_H
30 #include <time.h>
31 #endif
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
50 #if HAVE_SYS_STAT_H
51 #include <sys/stat.h>
52 #endif
53 
54 #define PROXY_PORT "3128"
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 
60 static int proxy_port = PROXY_PORT;
61 static char *proxy_addr = PROXY_ADDR;
62 static char *progname;
63 static int noutstanding = 0;
64 static int done_reading_urls = 0;
65 static int opt_ims = 0;
66 static int opt_checksum = 0;
67 static int opt_reopen = 1;
68 static int max_outstanding = 10;
69 static time_t lifetime = 60;
70 static const char *const crlf = "\r\n";
71 static int trace_fd = -1;
72 static int total_bytes_read = 0;
73 
74 #define REPLY_HDR_SZ 8192
75 
76 struct _r {
77  char url[1024];
83  struct _r *next;
84  long sum;
85  long validsize;
86  long validsum;
87 };
88 
89 static struct _r *Requests;
90 
91 char *
93 time_t *t;
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 
102 char *
103 mime_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)
112  end = p1 < p2 ? p1 : p2;
113  else
114  end = p1 ? p1 : p2;
115  if (end)
116  end += (end == p1 ? 3 : 2);
117 
118  return (char *) end;
119 }
120 
121 void
122 sig_intr(int sig)
123 {
124  fprintf(stderr, "\rWaiting for open connections to finish...\n");
125  signal(sig, SIG_DFL);
126  done_reading_urls = 1;
127 }
128 
129 int
131 {
132  int s;
133  struct addrinfo *AI = NULL;
134  struct addrinfo hints;
135 
136  memset(&hints, '\0', sizeof(struct addrinfo));
138  hints.ai_family = AF_UNSPEC;
139  hints.ai_socktype = SOCK_STREAM;
140 
141  getaddrinfo(proxy_addr, proxy_port, &hints, AI);
142 
143  if ((s = socket(AI->ai_family, AI->ai_socktype, AI->ai_protocol)) < 0) {
144  perror("socket");
145  s = -1;
146  } else if (connect(s, AI->ai_addr, AI->ai_addrlen) < 0) {
147  close(s);
148  perror("connect");
149  s = -1;
150  }
151 
152  freeaddrinfo(AI);
153  return s;
154 }
155 
156 int
157 send_request(int fd, const char *data)
158 {
159  char msg[4096], buf[4096];
160  int len;
161  time_t w;
162  struct _r *r;
163  struct _r **R;
164  char *method, *url, *file, *size, *checksum;
165  char *tmp = xstrdup(data);
166  struct stat st;
167  int file_fd = -1;
168  method = strtok(tmp, " ");
169  url = strtok(NULL, " ");
170  file = strtok(NULL, " ");
171  size = strtok(NULL, " ");
172  checksum = strtok(NULL, " ");
173  if (!url) {
174  url = method;
175  method = "GET";
176  }
177  if (file && strcmp(file, "-") == 0)
178  file = NULL;
179  if (size && strcmp(size, "-") == 0)
180  size = NULL;
181  if (checksum && strcmp(checksum, "-") == 0)
182  checksum = NULL;
183  msg[0] = '\0';
184  snprintf(buf, sizeof(buf)-1, "%s %s HTTP/1.0\r\n", method, url);
185  strcat(msg, buf);
186  strcat(msg, "Accept: */*\r\n");
187  strcat(msg, "Proxy-Connection: Keep-Alive\r\n");
188  if (opt_ims && (lrand48() & 0x03) == 0) {
189  w = time(NULL) - (lrand48() & 0x3FFFF);
190  snprintf(buf, sizeof(buf)-1, "If-Modified-Since: %s\r\n", mkrfc850(&w));
191  strcat(msg, buf);
192  }
193  if (file) {
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  }
203  snprintf(buf, sizeof(buf)-1, "Content-length: %d\r\n", st.st_size);
204  strcat(msg, buf);
205  }
206  strcat(msg, "\r\n");
207  len = strlen(msg);
208  if (write(fd, msg, len) < 0) {
209  close(fd);
210  perror("request write");
211  close(file_fd);
212  return -1;
213  }
214  if (file) {
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);
229  }
230  r = calloc(1, sizeof(struct _r));
231  strcpy(r->url, url);
232  if (size)
233  r->validsize = atoi(size);
234  else
235  r->validsize = -1;
236  if (checksum)
237  r->validsum = atoi(checksum);
238  for (R = &Requests; *R; R = &(*R)->next);
239  *R = r;
240  /* fprintf(stderr, "REQUESTED %s\n", url); */
241  noutstanding++;
242  return 0;
243 }
244 
245 static int
246 get_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)) {
250  if (strncasecmp(t, hdr, strlen(hdr)) == 0) {
251  t += strlen(hdr);
252  while (xisspace(*t))
253  t++;
254  return atoi(t);
255  }
256  }
257  return -1;
258 }
259 
260 static const char *
261 get_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)) {
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  }
274  }
275  return NULL;
276 }
277 
278 void
279 request_done(struct _r *r)
280 {
281 #if 0
282  fprintf(stderr, "DONE: %s, (%d+%d)\n",
283  r->url,
284  r->hdr_length,
285  r->content_length);
286 #endif
287  if (r->content_length != r->bytes_read)
288  fprintf(stderr, "ERROR! Short reply, expected %d bytes got %d\n",
289  r->content_length, r->bytes_read);
290  else if (r->validsize >= 0) {
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);
297  }
298 }
299 int
300 handle_read(char *inbuf, int len)
301 {
302  struct _r *r = Requests;
303  const char *end;
304  const char *url;
305  static char buf[READ_BUF_SZ];
306  int hlen, blen;
307  if (len < 0) {
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;
316  }
318  memcpy(buf, inbuf, len);
319  if (len == 0) {
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;
330  }
331  if (trace_fd > 0)
332  write(trace_fd, buf, len);
333  while (len > 0) {
334  /* Build headers */
335  if (r->hdr_length == 0) {
336  hlen = min(len, REPLY_HDR_SZ - r->hdr_offset - 1);
337  memcpy(r->reply_hdrs + r->hdr_offset, buf, hlen);
338  r->hdr_offset += hlen;
339  r->reply_hdrs[r->hdr_offset] = '\0';
340  len -= hlen;
341  /* Save any remaining read data */
342  memmove(buf, buf + hlen, len);
343  }
344  /* Process headers */
345  if (r->hdr_length == 0 && (end = mime_headers_end(r->reply_hdrs)) != NULL) {
346 #if 0
347  fprintf(stderr, "FOUND EOH FOR %s\n", r->url);
348  */
349 #endif
350  r->hdr_length = end - r->reply_hdrs;
351 #if 0
352  fprintf(stderr, "HDR_LENGTH = %d\n", r->hdr_length);
353 #endif
354  /* "unread" any body contents received */
355  blen = r->hdr_offset - r->hdr_length;
356  assert(blen >= 0);
357  if (blen > 0) {
358  memmove(buf + blen, buf, len);
359  memcpy(buf, r->reply_hdrs + r->hdr_length, blen);
360  len += blen;
361  }
362  r->reply_hdrs[r->hdr_length] = '\0'; /* Null terminate headers */
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);
369 #if XREQUESTURI || 0
370  fprintf(stderr, "LOCATION = %s\n", get_header_string_value("X-Request-URI:", r->reply_hdrs, end));
371 #endif
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 {
386  bytes_left = len + 1; /* Unknown end... */
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) {
403  }
404  memmove(buf, buf + bytes_used, len);
405  }
406  }
407  return 0;
408 }
409 
410 int
411 read_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);
418  if (x < 0) {
419  perror("read reply");
420  close(fd);
421  }
422  return x;
423 }
424 
425 void
427 {
428  static int pconn_fd = -1;
429  static char buf[8192];
430  struct timeval to;
431  struct timeval now, last, start;
432  fd_set R;
433  struct _r *r;
434  struct _r *nextr;
435  int x;
436  int timeouts;
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) {
444  perror("socket");
445  exit(1);
446  }
447  while (!done_reading_urls || noutstanding) {
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  }
547  }
548 }
549 
550 void
551 usage(void)
552 {
553  fprintf(stderr, "usage: %s: -p port -h host -n max -t tracefile -i -c -l lifetime\n", progname);
554 }
555 
556 int
557 main(argc, argv)
558 int argc;
559 char *argv[];
560 {
561  int c;
562  setbuf(stdout, NULL);
563  setbuf(stderr, NULL);
564  progname = xstrdup(argv[0]);
565  while ((c = getopt(argc, argv, "p:h:n:t:icl:r")) != -1) {
566  switch (c) {
567  case 'p':
568  proxy_port = atoi(optarg);
569  break;
570  case 'h':
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':
590  break;
591  default:
592  usage();
593  return 1;
594  }
595  }
596  signal(SIGINT, sig_intr);
597  signal(SIGPIPE, SIG_IGN);
598  main_loop();
599  return 0;
600 }
601 
long validsum
Definition: pconn-banger.c:86
char * mkrfc850(time_t *t)
Definition: pconn-banger.c:92
#define assert(EX)
Definition: assert.h:17
int open_http_socket(void)
Definition: pconn-banger.c:130
static time_t lifetime
Definition: pconn-banger.c:69
static const char * get_header_string_value(const char *hdr, const char *buf, const char *end)
Definition: pconn-banger.c:261
int bytes_read
Definition: pconn-banger.c:81
char url[1024]
Definition: pconn-banger.c:77
static struct _r * Requests
Definition: pconn-banger.c:89
#define READ_BUF_SZ
Definition: pconn-banger.c:57
int i
Definition: membanger.c:49
#define xstrdup
int content_length
Definition: pconn-banger.c:78
static int trace_fd
Definition: pconn-banger.c:71
#define PROXY_PORT
Definition: pconn-banger.c:54
int read_reply(int fd)
Definition: pconn-banger.c:411
#define xisspace(x)
Definition: xis.h:17
void sig_intr(int sig)
Definition: pconn-banger.c:122
int timeouts
Definition: peer_select.cc:65
static int opt_ims
Definition: pconn-banger.c:65
void main_loop(void)
Definition: pconn-banger.c:426
static const char *const crlf
Definition: pconn-banger.c:70
char reply_hdrs[REPLY_HDR_SZ]
Definition: pconn-banger.c:82
void freeaddrinfo()
void const char HLPCB void * data
Definition: stub_helper.cc:16
#define PROXY_ADDR
Definition: pconn-banger.c:55
static int done_reading_urls
Definition: pconn-banger.c:64
int getopt(int nargc, char *const *nargv, const char *ostr)
Definition: getopt.c:62
int getaddrinfo()
static int max_outstanding
Definition: pconn-banger.c:68
#define AI_NUMERICSERV
static int nrequests
Definition: tcp-banger2.c:90
int hdr_length
Definition: pconn-banger.c:79
static char * proxy_addr
Definition: pconn-banger.c:61
int main(int argc, argv)
Definition: pconn-banger.c:557
struct _r * next
Definition: pconn-banger.c:83
#define REPLY_HDR_SZ
Definition: pconn-banger.c:74
int unsigned int const char *desc STUB void int len
Definition: stub_fd.cc:20
void const char * buf
Definition: stub_helper.cc:16
static int get_header_int_value(const char *hdr, const char *buf, const char *end)
Definition: pconn-banger.c:246
long validsize
Definition: pconn-banger.c:85
#define AI_NUMERICHOST
bool SIGHDLR int STUB void int
Definition: stub_tools.cc:68
int send_request(int fd, const char *data)
Definition: pconn-banger.c:157
int handle_read(char *inbuf, int len)
Definition: pconn-banger.c:300
long sum
Definition: pconn-banger.c:84
int hdr_offset
Definition: pconn-banger.c:80
static int opt_checksum
Definition: pconn-banger.c:66
socklen_t ai_addrlen
void usage(void)
Definition: pconn-banger.c:551
static char * progname
Definition: pconn-banger.c:62
struct sockaddr * ai_addr
static int reqpersec
Definition: tcp-banger2.c:89
size_t HttpReply *STUB StoreEntry const KeyScope scope const HttpRequestMethod & method
Definition: stub_store.cc:126
#define min(x, y)
Definition: pconn-banger.c:58
static int proxy_port
Definition: pconn-banger.c:60
static hrtime_t now
Definition: ProfStats.cc:256
void request_done(struct _r *r)
Definition: pconn-banger.c:279
static int total_bytes_read
Definition: pconn-banger.c:72
char * optarg
Definition: getopt.c:51
static int opt_reopen
Definition: pconn-banger.c:67
#define NULL
Definition: types.h:166
int size
Definition: ModDevPoll.cc:77
static int noutstanding
Definition: pconn-banger.c:63
char * mime_headers_end(const char *mime)
Definition: pconn-banger.c:103
#define free(a)
Definition: hash.c:31

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors