]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client.cc
Need SquidTime for squid_curtime
[thirdparty/squid.git] / src / client.cc
CommitLineData
16300b58 1
30a4f2a8 2/*
f8230a8d 3 * $Id: client.cc,v 1.109 2005/05/26 09:44:30 hno Exp $
30a4f2a8 4 *
5 * DEBUG: section 0 WWW Client
6 * AUTHOR: Harvest Derived
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
30a4f2a8 10 *
2b6662ba 11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
30a4f2a8 19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
30a4f2a8 34 */
090089c4 35
44a47c6e 36#include "squid.h"
090089c4 37
38#ifndef BUFSIZ
39#define BUFSIZ 8192
40#endif
41
42/* Local functions */
a2c963ae 43static int client_comm_bind(int, const char *);
62e76326 44
a2c963ae 45static int client_comm_connect(int, const char *, u_short, struct timeval *);
f5b8bbc4 46static void usage(const char *progname);
62e76326 47
899bab3f 48static int Now(struct timeval *);
e6ccf245 49static SIGHDLR catchSignal;
54220df8 50static SIGHDLR pipe_handler;
d6d09e02 51static void set_our_signal(void);
0ef0f1de 52#ifndef _SQUID_MSWIN_
20cbfe5a 53static ssize_t myread(int fd, void *buf, size_t len);
54static ssize_t mywrite(int fd, void *buf, size_t len);
0ef0f1de 55#endif
cca89eeb 56static int put_fd;
57static char *put_file = NULL;
62e76326 58
b6c6bcef 59static struct stat sb;
60int total_bytes = 0;
20cbfe5a 61int io_timeout = 120;
090089c4 62
b8d8561b 63static void
0ee4272b 64usage(const char *progname)
090089c4 65{
0ee4272b 66 fprintf(stderr,
62e76326 67 "Usage: %s [-arsv] [-i IMS] [-h remote host] [-l local host] [-p port] [-m method] [-t count] [-I ping-interval] [-H 'strings'] [-T timeout] url\n"
68 "Options:\n"
69 " -P file PUT request.\n"
70 " -a Do NOT include Accept: header.\n"
71 " -r Force cache to reload URL.\n"
72 " -s Silent. Do not print data to stdout.\n"
73 " -v Verbose. Print outgoing message to stderr.\n"
74 " -i IMS If-Modified-Since time (in Epoch seconds).\n"
75 " -h host Retrieve URL from cache on hostname. Default is localhost.\n"
76 " -l host Specify a local IP address to bind to. Default is none.\n"
77 " -p port Port number of cache. Default is %d.\n"
78 " -m method Request method, default is GET.\n"
79 " -t count Trace count cache-hops\n"
80 " -g count Ping mode, \"count\" iterations (0 to loop until interrupted).\n"
81 " -I interval Ping interval in seconds (default 1 second).\n"
82 " -H 'string' Extra headers to send. Use '\\n' for new lines.\n"
83 " -T timeout Timeout value (seconds) for read/write operations.\n"
84 " -u user Proxy authentication username\n"
85 " -w password Proxy authentication password\n"
86 " -U user WWW authentication username\n"
87 " -W password WWW authentication password\n",
88 progname, CACHE_HTTP_PORT);
090089c4 89 exit(1);
90}
91
899bab3f 92static int interrupted = 0;
b8d8561b 93int
94main(int argc, char *argv[])
090089c4 95{
96 int conn, c, len, bytesWritten;
97 int port, to_stdout, reload;
899bab3f 98 int ping, pcount;
599eadbe 99 int keep_alive = 0;
88738790 100 int opt_noaccept = 0;
63259c34 101 int opt_verbose = 0;
a2c963ae 102 const char *hostname, *localhost;
f8230a8d 103 char url[BUFSIZ], msg[49152], buf[BUFSIZ];
104 char extra_hdrs[32768];
0ee4272b 105 const char *method = "GET";
090089c4 106 extern char *optarg;
234967c9 107 time_t ims = 0;
b3b64e58 108 int max_forwards = -1;
62e76326 109
899bab3f 110 struct timeval tv1, tv2;
111 int i = 0, loops;
112 long ping_int;
113 long ping_min = 0, ping_max = 0, ping_sum = 0, ping_mean = 0;
a78886fc 114 char *proxy_user = NULL;
115 char *proxy_password = NULL;
116 char *www_user = NULL;
117 char *www_password = NULL;
090089c4 118
119 /* set the defaults */
2c08acd9 120 hostname = "localhost";
121 localhost = NULL;
63259c34 122 extra_hdrs[0] = '\0';
090089c4 123 port = CACHE_HTTP_PORT;
124 to_stdout = 1;
125 reload = 0;
899bab3f 126 ping = 0;
127 pcount = 0;
128 ping_int = 1 * 1000;
090089c4 129
130 if (argc < 2) {
62e76326 131 usage(argv[0]); /* need URL */
090089c4 132 } else if (argc >= 2) {
62e76326 133 strncpy(url, argv[argc - 1], BUFSIZ);
134 url[BUFSIZ - 1] = '\0';
135
136 if (url[0] == '-')
137 usage(argv[0]);
138
139 while ((c = getopt(argc, argv, "ah:l:P:i:km:p:rsvt:g:p:I:H:T:u:U:w:W:?")) != -1)
140 switch (c) {
141
142 case 'a':
143 opt_noaccept = 1;
144 break;
145
146 case 'h': /* remote host */
147
148 if (optarg != NULL)
149 hostname = optarg;
150
151 break;
152
153 case 'l': /* local host */
154 if (optarg != NULL)
155 localhost = optarg;
156
157 break;
158
159 case 's': /* silent */
160 to_stdout = 0;
161
162 break;
163
164 case 'k': /* backward compat */
165 keep_alive = 1;
166
167 break;
168
169 case 'r': /* reload */
170 reload = 1;
171
172 break;
173
174 case 'p': /* port number */
175 sscanf(optarg, "%d", &port);
176
177 if (port < 1)
178 port = CACHE_HTTP_PORT; /* default */
179
180 break;
181
182 case 'P':
183 put_file = xstrdup(optarg);
184
185 break;
186
187 case 'i': /* IMS */
188 ims = (time_t) atoi(optarg);
189
190 break;
191
192 case 'm':
193 method = xstrdup(optarg);
194
195 break;
196
197 case 't':
198 method = xstrdup("TRACE");
199
200 max_forwards = atoi(optarg);
201
202 break;
203
204 case 'g':
205 ping = 1;
206
207 pcount = atoi(optarg);
208
209 to_stdout = 0;
210
211 break;
212
213 case 'I':
214 if ((ping_int = atoi(optarg) * 1000) <= 0)
215 usage(argv[0]);
216
217 break;
218
219 case 'H':
220 if (strlen(optarg)) {
221 char *t;
222 strncpy(extra_hdrs, optarg, sizeof(extra_hdrs));
223
224 while ((t = strstr(extra_hdrs, "\\n")))
225 *t = '\r', *(t + 1) = '\n';
226 }
227
228 break;
229
230 case 'T':
231 io_timeout = atoi(optarg);
232 break;
233
234 case 'u':
235 proxy_user = optarg;
236 break;
237
238 case 'w':
239 proxy_password = optarg;
240 break;
241
242 case 'U':
243 www_user = optarg;
244 break;
245
246 case 'W':
247 www_password = optarg;
248 break;
249
250 case 'v':
251 /* undocumented: may increase verb-level by giving more -v's */
252 opt_verbose++;
253 break;
254
255 case '?': /* usage */
256
257 default:
258 usage(argv[0]);
259 break;
260 }
090089c4 261 }
62e76326 262
0ef0f1de 263#ifdef _SQUID_MSWIN_
264 {
62e76326 265 WSADATA wsaData;
266 WSAStartup(2, &wsaData);
0ef0f1de 267 }
268#endif
090089c4 269 /* Build the HTTP request */
8a9b6b94 270 if (strncmp(url, "mgr:", 4) == 0) {
62e76326 271 char *t = xstrdup(url + 4);
272 snprintf(url, BUFSIZ, "cache_object://%s/%s", hostname, t);
273 xfree(t);
8a9b6b94 274 }
62e76326 275
cca89eeb 276 if (put_file) {
62e76326 277 put_fd = open(put_file, O_RDONLY);
278 set_our_signal();
279
280 if (put_fd < 0) {
281 fprintf(stderr, "%s: can't open file (%s)\n", argv[0],
282 xstrerror());
283 exit(-1);
284 }
285
ec4daaa5 286#ifdef _SQUID_WIN32_
62e76326 287 setmode(put_fd, O_BINARY);
288
c4aefe96 289#endif
62e76326 290
291 fstat(put_fd, &sb);
cca89eeb 292 }
62e76326 293
042461c3 294 snprintf(msg, BUFSIZ, "%s %s HTTP/1.0\r\n", method, url);
62e76326 295
090089c4 296 if (reload) {
62e76326 297 snprintf(buf, BUFSIZ, "Pragma: no-cache\r\n");
298 strcat(msg, buf);
234967c9 299 }
62e76326 300
16300b58 301 if (put_fd > 0) {
62e76326 302 snprintf(buf, BUFSIZ, "Content-length: %d\r\n", (int) sb.st_size);
303 strcat(msg, buf);
cca89eeb 304 }
62e76326 305
88738790 306 if (opt_noaccept == 0) {
62e76326 307 snprintf(buf, BUFSIZ, "Accept: */*\r\n");
308 strcat(msg, buf);
88738790 309 }
62e76326 310
234967c9 311 if (ims) {
62e76326 312 snprintf(buf, BUFSIZ, "If-Modified-Since: %s\r\n", mkrfc1123(ims));
313 strcat(msg, buf);
090089c4 314 }
62e76326 315
b3b64e58 316 if (max_forwards > -1) {
62e76326 317 snprintf(buf, BUFSIZ, "Max-Forwards: %d\r\n", max_forwards);
318 strcat(msg, buf);
b3b64e58 319 }
62e76326 320
a78886fc 321 if (proxy_user) {
62e76326 322 char *user = proxy_user;
323 char *password = proxy_password;
230c091c 324#if HAVE_GETPASS
62e76326 325
326 if (!password)
327 password = getpass("Proxy password: ");
328
230c091c 329#endif
62e76326 330
331 if (!password) {
332 fprintf(stderr, "ERROR: Proxy password missing\n");
333 exit(1);
334 }
335
336 snprintf(buf, BUFSIZ, "%s:%s", user, password);
a394841f 337 snprintf(buf, BUFSIZ, "Proxy-Authorization: Basic %s\r\n", base64_encode(buf));
62e76326 338 strcat(msg, buf);
a78886fc 339 }
62e76326 340
a78886fc 341 if (www_user) {
62e76326 342 char *user = www_user;
343 char *password = www_password;
230c091c 344#if HAVE_GETPASS
62e76326 345
346 if (!password)
347 password = getpass("WWW password: ");
348
230c091c 349#endif
62e76326 350
351 if (!password) {
352 fprintf(stderr, "ERROR: WWW password missing\n");
353 exit(1);
354 }
355
356 snprintf(buf, BUFSIZ, "%s:%s", user, password);
a394841f 357 snprintf(buf, BUFSIZ, "Authorization: Basic %s\r\n", base64_encode(buf));
62e76326 358 strcat(msg, buf);
a78886fc 359 }
62e76326 360
599eadbe 361 if (keep_alive) {
62e76326 362 if (port != 80)
363 snprintf(buf, BUFSIZ, "Proxy-Connection: keep-alive\r\n");
364 else
365 snprintf(buf, BUFSIZ, "Connection: keep-alive\r\n");
366
367 strcat(msg, buf);
599eadbe 368 }
62e76326 369
63259c34 370 strcat(msg, extra_hdrs);
042461c3 371 snprintf(buf, BUFSIZ, "\r\n");
234967c9 372 strcat(msg, buf);
090089c4 373
63259c34 374 if (opt_verbose)
62e76326 375 fprintf(stderr, "headers: '%s'\n", msg);
63259c34 376
899bab3f 377 if (ping) {
378#if HAVE_SIGACTION
62e76326 379
380 struct sigaction sa, osa;
381
382 if (sigaction(SIGINT, NULL, &osa) == 0 && osa.sa_handler == SIG_DFL) {
383 sa.sa_handler = catchSignal;
384 sa.sa_flags = 0;
385 sigemptyset(&sa.sa_mask);
386 (void) sigaction(SIGINT, &sa, NULL);
387 }
388
899bab3f 389#else
62e76326 390 void (*osig) (int);
391
392 if ((osig = signal(SIGINT, catchSignal)) != SIG_DFL)
393 (void) signal(SIGINT, osig);
394
899bab3f 395#endif
62e76326 396
899bab3f 397 }
62e76326 398
899bab3f 399 loops = ping ? pcount : 1;
62e76326 400
899bab3f 401 for (i = 0; loops == 0 || i < loops; i++) {
62e76326 402 int fsize = 0;
403 /* Connect to the server */
404
405 if ((conn = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
406 perror("client: socket");
407 exit(1);
408 }
409
410 if (localhost && client_comm_bind(conn, localhost) < 0) {
411 perror("client: bind");
412 exit(1);
413 }
414
415 if (client_comm_connect(conn, hostname, port, ping ? &tv1 : NULL) < 0) {
416 if (errno == 0) {
417 fprintf(stderr, "client: ERROR: Cannot connect to %s:%d: Host unknown.\n", hostname, port);
418 } else {
419 char tbuf[BUFSIZ];
420 snprintf(tbuf, BUFSIZ, "client: ERROR: Cannot connect to %s:%d",
421 hostname, port);
422 perror(tbuf);
423 }
424
425 exit(1);
426 }
427
428 /* Send the HTTP request */
0ef0f1de 429#ifdef _SQUID_MSWIN_
411c6ea3 430 bytesWritten = send(conn, (const void *) msg, strlen(msg), 0);
62e76326 431
0ef0f1de 432#else
62e76326 433
434 bytesWritten = mywrite(conn, msg, strlen(msg));
435
0ef0f1de 436#endif
62e76326 437
438 if (bytesWritten < 0) {
439 perror("client: ERROR: write");
440 exit(1);
441 } else if ((unsigned) bytesWritten != strlen(msg)) {
442 fprintf(stderr, "client: ERROR: Cannot send request?: %s\n", msg);
443 exit(1);
444 }
445
446 if (put_file) {
447 int x;
448 lseek(put_fd, 0, SEEK_SET);
0ef0f1de 449#ifdef _SQUID_MSWIN_
62e76326 450
451 while ((x = read(put_fd, buf, sizeof(buf))) > 0) {
452 x = write(conn, buf, x);
0ef0f1de 453#else
62e76326 454
455 while ((x = myread(put_fd, buf, sizeof(buf))) > 0) {
456 x = mywrite(conn, buf, x);
0ef0f1de 457#endif
62e76326 458
459 total_bytes += x;
460
461 if (x <= 0)
462 break;
463 }
464
465 if (x != 0)
466 fprintf(stderr, "client: ERROR: Cannot send file.\n");
467 }
468
469 /* Read the data */
54220df8 470
0ef0f1de 471#ifdef _SQUID_MSWIN_
62e76326 472 setmode(1, O_BINARY);
473
411c6ea3 474 while ((len = recv(conn, (void *) buf, sizeof(buf), 0)) > 0) {
0ef0f1de 475#else
62e76326 476
477 while ((len = myread(conn, buf, sizeof(buf))) > 0) {
0ef0f1de 478#endif
62e76326 479 fsize += len;
480
481 if (to_stdout)
482 fwrite(buf, len, 1, stdout);
483 }
484
0ef0f1de 485#ifdef _SQUID_MSWIN_
62e76326 486 setmode(1, O_TEXT);
487
0ef0f1de 488#endif
62e76326 489
490 (void) close(conn); /* done with socket */
491
492 if (interrupted)
493 break;
494
495 if (ping) {
496
497 struct tm *tmp;
498 time_t t2s;
499 long elapsed_msec;
500
501 (void) Now(&tv2);
502 elapsed_msec = tvSubMsec(tv1, tv2);
503 t2s = tv2.tv_sec;
504 tmp = localtime(&t2s);
505 fprintf(stderr, "%d-%02d-%02d %02d:%02d:%02d [%d]: %ld.%03ld secs, %f KB/s\n",
506 tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
507 tmp->tm_hour, tmp->tm_min, tmp->tm_sec, i + 1,
508 elapsed_msec / 1000, elapsed_msec % 1000,
509 elapsed_msec ? (double) fsize / elapsed_msec : -1.0);
510
511 if (i == 0 || elapsed_msec < ping_min)
512 ping_min = elapsed_msec;
513
514 if (i == 0 || elapsed_msec > ping_max)
515 ping_max = elapsed_msec;
516
517 ping_sum += elapsed_msec;
518
519 /* Delay until next "ping_int" boundary */
520 if ((loops == 0 || i + 1 < loops) && elapsed_msec < ping_int) {
521
522 struct timeval tvs;
523 long msec_left = ping_int - elapsed_msec;
524
525 tvs.tv_sec = msec_left / 1000;
526 tvs.tv_usec = (msec_left % 1000) * 1000;
527 select(0, NULL, NULL, NULL, &tvs);
528 }
529 }
090089c4 530 }
899bab3f 531
532 if (ping && i) {
62e76326 533 ping_mean = ping_sum / i;
534 fprintf(stderr, "%d requests, round-trip (secs) min/avg/max = "
535 "%ld.%03ld/%ld.%03ld/%ld.%03ld\n", i,
536 ping_min / 1000, ping_min % 1000, ping_mean / 1000, ping_mean % 1000,
537 ping_max / 1000, ping_max % 1000);
090089c4 538 }
62e76326 539
090089c4 540 exit(0);
541 /*NOTREACHED */
983061ed 542 return 0;
090089c4 543}
544
2c08acd9 545static int
62e76326 546client_comm_bind(int sock, const char *local_host) {
547
2c08acd9 548 static const struct hostent *hp = NULL;
62e76326 549
2c08acd9 550 static struct sockaddr_in from_addr;
551
552 /* Set up the source socket address from which to send. */
62e76326 553
2c08acd9 554 if (hp == NULL) {
62e76326 555 from_addr.sin_family = AF_INET;
556
557 if ((hp = gethostbyname(local_host)) == 0) {
558 return (-1);
559 }
2c08acd9 560
62e76326 561 xmemcpy(&from_addr.sin_addr, hp->h_addr, hp->h_length);
562 from_addr.sin_port = 0;
2c08acd9 563 }
62e76326 564
2c08acd9 565 return bind(sock, (struct sockaddr *) &from_addr, sizeof(struct sockaddr_in));
566}
567
b8d8561b 568static int
62e76326 569
570client_comm_connect(int sock, const char *dest_host, u_short dest_port, struct timeval *tvp) {
571
899bab3f 572 static const struct hostent *hp = NULL;
62e76326 573
090089c4 574 static struct sockaddr_in to_addr;
575
576 /* Set up the destination socket address for message to send to. */
090089c4 577
62e76326 578 if (hp == NULL)
579 {
580 to_addr.sin_family = AF_INET;
581
582 if ((hp = gethostbyname(dest_host)) == 0) {
583 return (-1);
584 }
585
586 xmemcpy(&to_addr.sin_addr, hp->h_addr, hp->h_length);
587 to_addr.sin_port = htons(dest_port);
090089c4 588 }
62e76326 589
899bab3f 590 if (tvp)
62e76326 591 (void) Now(tvp);
592
090089c4 593 return connect(sock, (struct sockaddr *) &to_addr, sizeof(struct sockaddr_in));
594}
899bab3f 595
596static int
62e76326 597
598Now(struct timeval *tp) {
899bab3f 599#if GETTIMEOFDAY_NO_TZP
600 return gettimeofday(tp);
601#else
62e76326 602
899bab3f 603 return gettimeofday(tp, NULL);
604#endif
605} /* ARGSUSED */
606
607static void
62e76326 608catchSignal(int sig) {
899bab3f 609 interrupted = 1;
610 fprintf(stderr, "Interrupted.\n");
611}
b6c6bcef 612
5446b331 613static void
62e76326 614pipe_handler(int sig) {
b6c6bcef 615 fprintf(stderr, "SIGPIPE received.\n");
54220df8 616}
617
618static void
62e76326 619set_our_signal(void) {
54220df8 620#if HAVE_SIGACTION
62e76326 621
54220df8 622 struct sigaction sa;
623 sa.sa_handler = pipe_handler;
624 sa.sa_flags = SA_RESTART;
625 sigemptyset(&sa.sa_mask);
62e76326 626
54220df8 627 if (sigaction(SIGPIPE, &sa, NULL) < 0) {
62e76326 628 fprintf(stderr, "Cannot set PIPE signal.\n");
629 exit(-1);
54220df8 630 }
62e76326 631
54220df8 632#else
633 signal(SIGPIPE, pipe_handler);
62e76326 634
54220df8 635#endif
636
637}
20cbfe5a 638
0ef0f1de 639#ifndef _SQUID_MSWIN_
20cbfe5a 640static ssize_t
62e76326 641myread(int fd, void *buf, size_t len) {
20cbfe5a 642 alarm(io_timeout);
643 return read(fd, buf, len);
644}
645
646static ssize_t
62e76326 647mywrite(int fd, void *buf, size_t len) {
20cbfe5a 648 alarm(io_timeout);
649 return write(fd, buf, len);
650}
62e76326 651
0ef0f1de 652#endif