]> git.ipfire.org Git - thirdparty/squid.git/blob - src/client.cc
Need SquidTime for squid_curtime
[thirdparty/squid.git] / src / client.cc
1
2 /*
3 * $Id: client.cc,v 1.109 2005/05/26 09:44:30 hno Exp $
4 *
5 * DEBUG: section 0 WWW Client
6 * AUTHOR: Harvest Derived
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
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.
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
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36 #include "squid.h"
37
38 #ifndef BUFSIZ
39 #define BUFSIZ 8192
40 #endif
41
42 /* Local functions */
43 static int client_comm_bind(int, const char *);
44
45 static int client_comm_connect(int, const char *, u_short, struct timeval *);
46 static void usage(const char *progname);
47
48 static int Now(struct timeval *);
49 static SIGHDLR catchSignal;
50 static SIGHDLR pipe_handler;
51 static void set_our_signal(void);
52 #ifndef _SQUID_MSWIN_
53 static ssize_t myread(int fd, void *buf, size_t len);
54 static ssize_t mywrite(int fd, void *buf, size_t len);
55 #endif
56 static int put_fd;
57 static char *put_file = NULL;
58
59 static struct stat sb;
60 int total_bytes = 0;
61 int io_timeout = 120;
62
63 static void
64 usage(const char *progname)
65 {
66 fprintf(stderr,
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);
89 exit(1);
90 }
91
92 static int interrupted = 0;
93 int
94 main(int argc, char *argv[])
95 {
96 int conn, c, len, bytesWritten;
97 int port, to_stdout, reload;
98 int ping, pcount;
99 int keep_alive = 0;
100 int opt_noaccept = 0;
101 int opt_verbose = 0;
102 const char *hostname, *localhost;
103 char url[BUFSIZ], msg[49152], buf[BUFSIZ];
104 char extra_hdrs[32768];
105 const char *method = "GET";
106 extern char *optarg;
107 time_t ims = 0;
108 int max_forwards = -1;
109
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;
114 char *proxy_user = NULL;
115 char *proxy_password = NULL;
116 char *www_user = NULL;
117 char *www_password = NULL;
118
119 /* set the defaults */
120 hostname = "localhost";
121 localhost = NULL;
122 extra_hdrs[0] = '\0';
123 port = CACHE_HTTP_PORT;
124 to_stdout = 1;
125 reload = 0;
126 ping = 0;
127 pcount = 0;
128 ping_int = 1 * 1000;
129
130 if (argc < 2) {
131 usage(argv[0]); /* need URL */
132 } else if (argc >= 2) {
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 }
261 }
262
263 #ifdef _SQUID_MSWIN_
264 {
265 WSADATA wsaData;
266 WSAStartup(2, &wsaData);
267 }
268 #endif
269 /* Build the HTTP request */
270 if (strncmp(url, "mgr:", 4) == 0) {
271 char *t = xstrdup(url + 4);
272 snprintf(url, BUFSIZ, "cache_object://%s/%s", hostname, t);
273 xfree(t);
274 }
275
276 if (put_file) {
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
286 #ifdef _SQUID_WIN32_
287 setmode(put_fd, O_BINARY);
288
289 #endif
290
291 fstat(put_fd, &sb);
292 }
293
294 snprintf(msg, BUFSIZ, "%s %s HTTP/1.0\r\n", method, url);
295
296 if (reload) {
297 snprintf(buf, BUFSIZ, "Pragma: no-cache\r\n");
298 strcat(msg, buf);
299 }
300
301 if (put_fd > 0) {
302 snprintf(buf, BUFSIZ, "Content-length: %d\r\n", (int) sb.st_size);
303 strcat(msg, buf);
304 }
305
306 if (opt_noaccept == 0) {
307 snprintf(buf, BUFSIZ, "Accept: */*\r\n");
308 strcat(msg, buf);
309 }
310
311 if (ims) {
312 snprintf(buf, BUFSIZ, "If-Modified-Since: %s\r\n", mkrfc1123(ims));
313 strcat(msg, buf);
314 }
315
316 if (max_forwards > -1) {
317 snprintf(buf, BUFSIZ, "Max-Forwards: %d\r\n", max_forwards);
318 strcat(msg, buf);
319 }
320
321 if (proxy_user) {
322 char *user = proxy_user;
323 char *password = proxy_password;
324 #if HAVE_GETPASS
325
326 if (!password)
327 password = getpass("Proxy password: ");
328
329 #endif
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);
337 snprintf(buf, BUFSIZ, "Proxy-Authorization: Basic %s\r\n", base64_encode(buf));
338 strcat(msg, buf);
339 }
340
341 if (www_user) {
342 char *user = www_user;
343 char *password = www_password;
344 #if HAVE_GETPASS
345
346 if (!password)
347 password = getpass("WWW password: ");
348
349 #endif
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);
357 snprintf(buf, BUFSIZ, "Authorization: Basic %s\r\n", base64_encode(buf));
358 strcat(msg, buf);
359 }
360
361 if (keep_alive) {
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);
368 }
369
370 strcat(msg, extra_hdrs);
371 snprintf(buf, BUFSIZ, "\r\n");
372 strcat(msg, buf);
373
374 if (opt_verbose)
375 fprintf(stderr, "headers: '%s'\n", msg);
376
377 if (ping) {
378 #if HAVE_SIGACTION
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
389 #else
390 void (*osig) (int);
391
392 if ((osig = signal(SIGINT, catchSignal)) != SIG_DFL)
393 (void) signal(SIGINT, osig);
394
395 #endif
396
397 }
398
399 loops = ping ? pcount : 1;
400
401 for (i = 0; loops == 0 || i < loops; i++) {
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 */
429 #ifdef _SQUID_MSWIN_
430 bytesWritten = send(conn, (const void *) msg, strlen(msg), 0);
431
432 #else
433
434 bytesWritten = mywrite(conn, msg, strlen(msg));
435
436 #endif
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);
449 #ifdef _SQUID_MSWIN_
450
451 while ((x = read(put_fd, buf, sizeof(buf))) > 0) {
452 x = write(conn, buf, x);
453 #else
454
455 while ((x = myread(put_fd, buf, sizeof(buf))) > 0) {
456 x = mywrite(conn, buf, x);
457 #endif
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 */
470
471 #ifdef _SQUID_MSWIN_
472 setmode(1, O_BINARY);
473
474 while ((len = recv(conn, (void *) buf, sizeof(buf), 0)) > 0) {
475 #else
476
477 while ((len = myread(conn, buf, sizeof(buf))) > 0) {
478 #endif
479 fsize += len;
480
481 if (to_stdout)
482 fwrite(buf, len, 1, stdout);
483 }
484
485 #ifdef _SQUID_MSWIN_
486 setmode(1, O_TEXT);
487
488 #endif
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 }
530 }
531
532 if (ping && i) {
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);
538 }
539
540 exit(0);
541 /*NOTREACHED */
542 return 0;
543 }
544
545 static int
546 client_comm_bind(int sock, const char *local_host) {
547
548 static const struct hostent *hp = NULL;
549
550 static struct sockaddr_in from_addr;
551
552 /* Set up the source socket address from which to send. */
553
554 if (hp == NULL) {
555 from_addr.sin_family = AF_INET;
556
557 if ((hp = gethostbyname(local_host)) == 0) {
558 return (-1);
559 }
560
561 xmemcpy(&from_addr.sin_addr, hp->h_addr, hp->h_length);
562 from_addr.sin_port = 0;
563 }
564
565 return bind(sock, (struct sockaddr *) &from_addr, sizeof(struct sockaddr_in));
566 }
567
568 static int
569
570 client_comm_connect(int sock, const char *dest_host, u_short dest_port, struct timeval *tvp) {
571
572 static const struct hostent *hp = NULL;
573
574 static struct sockaddr_in to_addr;
575
576 /* Set up the destination socket address for message to send to. */
577
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);
588 }
589
590 if (tvp)
591 (void) Now(tvp);
592
593 return connect(sock, (struct sockaddr *) &to_addr, sizeof(struct sockaddr_in));
594 }
595
596 static int
597
598 Now(struct timeval *tp) {
599 #if GETTIMEOFDAY_NO_TZP
600 return gettimeofday(tp);
601 #else
602
603 return gettimeofday(tp, NULL);
604 #endif
605 } /* ARGSUSED */
606
607 static void
608 catchSignal(int sig) {
609 interrupted = 1;
610 fprintf(stderr, "Interrupted.\n");
611 }
612
613 static void
614 pipe_handler(int sig) {
615 fprintf(stderr, "SIGPIPE received.\n");
616 }
617
618 static void
619 set_our_signal(void) {
620 #if HAVE_SIGACTION
621
622 struct sigaction sa;
623 sa.sa_handler = pipe_handler;
624 sa.sa_flags = SA_RESTART;
625 sigemptyset(&sa.sa_mask);
626
627 if (sigaction(SIGPIPE, &sa, NULL) < 0) {
628 fprintf(stderr, "Cannot set PIPE signal.\n");
629 exit(-1);
630 }
631
632 #else
633 signal(SIGPIPE, pipe_handler);
634
635 #endif
636
637 }
638
639 #ifndef _SQUID_MSWIN_
640 static ssize_t
641 myread(int fd, void *buf, size_t len) {
642 alarm(io_timeout);
643 return read(fd, buf, len);
644 }
645
646 static ssize_t
647 mywrite(int fd, void *buf, size_t len) {
648 alarm(io_timeout);
649 return write(fd, buf, len);
650 }
651
652 #endif