]> git.ipfire.org Git - thirdparty/squid.git/blame - src/client.cc
From Guido: The following patch adds the capability of to specify the
[thirdparty/squid.git] / src / client.cc
CommitLineData
16300b58 1
30a4f2a8 2/*
19c2646d 3 * $Id: client.cc,v 1.97 2002/06/14 17:51:39 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 *);
44static int client_comm_connect(int, const char *, u_short, struct timeval *);
f5b8bbc4 45static void usage(const char *progname);
899bab3f 46static int Now(struct timeval *);
47static SIGHDLR catch;
54220df8 48static SIGHDLR pipe_handler;
d6d09e02 49static void set_our_signal(void);
20cbfe5a 50static ssize_t myread(int fd, void *buf, size_t len);
51static ssize_t mywrite(int fd, void *buf, size_t len);
cca89eeb 52static int put_fd;
53static char *put_file = NULL;
b6c6bcef 54static struct stat sb;
55int total_bytes = 0;
20cbfe5a 56int io_timeout = 120;
090089c4 57
b8d8561b 58static void
0ee4272b 59usage(const char *progname)
090089c4 60{
0ee4272b 61 fprintf(stderr,
20cbfe5a 62 "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"
fe4e214f 63 "Options:\n"
cca89eeb 64 " -P file PUT request.\n"
899bab3f 65 " -a Do NOT include Accept: header.\n"
66 " -r Force cache to reload URL.\n"
67 " -s Silent. Do not print data to stdout.\n"
63259c34 68 " -v Verbose. Print outgoing message to stderr.\n"
899bab3f 69 " -i IMS If-Modified-Since time (in Epoch seconds).\n"
70 " -h host Retrieve URL from cache on hostname. Default is localhost.\n"
2c08acd9 71 " -l host Specify a local IP address to bind to. Default is none.\n"
899bab3f 72 " -p port Port number of cache. Default is %d.\n"
73 " -m method Request method, default is GET.\n"
74 " -t count Trace count cache-hops\n"
75 " -g count Ping mode, \"count\" iterations (0 to loop until interrupted).\n"
63259c34 76 " -I interval Ping interval in seconds (default 1 second).\n"
20cbfe5a 77 " -H 'string' Extra headers to send. Use '\\n' for new lines.\n"
78 " -T timeout Timeout value (seconds) for read/write operations.\n",
fe4e214f 79 progname, CACHE_HTTP_PORT);
090089c4 80 exit(1);
81}
82
899bab3f 83static int interrupted = 0;
b8d8561b 84int
85main(int argc, char *argv[])
090089c4 86{
87 int conn, c, len, bytesWritten;
88 int port, to_stdout, reload;
899bab3f 89 int ping, pcount;
599eadbe 90 int keep_alive = 0;
88738790 91 int opt_noaccept = 0;
63259c34 92 int opt_verbose = 0;
a2c963ae 93 const char *hostname, *localhost;
2c08acd9 94 char url[BUFSIZ], msg[BUFSIZ], buf[BUFSIZ];
63259c34 95 char extra_hdrs[BUFSIZ];
0ee4272b 96 const char *method = "GET";
090089c4 97 extern char *optarg;
234967c9 98 time_t ims = 0;
b3b64e58 99 int max_forwards = -1;
899bab3f 100 struct timeval tv1, tv2;
101 int i = 0, loops;
102 long ping_int;
103 long ping_min = 0, ping_max = 0, ping_sum = 0, ping_mean = 0;
090089c4 104
105 /* set the defaults */
2c08acd9 106 hostname = "localhost";
107 localhost = NULL;
63259c34 108 extra_hdrs[0] = '\0';
090089c4 109 port = CACHE_HTTP_PORT;
110 to_stdout = 1;
111 reload = 0;
899bab3f 112 ping = 0;
113 pcount = 0;
114 ping_int = 1 * 1000;
090089c4 115
116 if (argc < 2) {
117 usage(argv[0]); /* need URL */
118 } else if (argc >= 2) {
119 strcpy(url, argv[argc - 1]);
120 if (url[0] == '-')
121 usage(argv[0]);
338c5e76 122 while ((c = getopt(argc, argv, "ah:l:P:i:km:p:rsvt:g:p:I:H:T:?")) != -1)
090089c4 123 switch (c) {
88738790 124 case 'a':
125 opt_noaccept = 1;
126 break;
2c08acd9 127 case 'h': /* remote host */
090089c4 128 if (optarg != NULL)
2c08acd9 129 hostname = optarg;
130 break;
131 case 'l': /* local host */
132 if (optarg != NULL)
133 localhost = optarg;
090089c4 134 break;
135 case 's': /* silent */
090089c4 136 to_stdout = 0;
137 break;
599eadbe 138 case 'k': /* backward compat */
139 keep_alive = 1;
140 break;
090089c4 141 case 'r': /* reload */
142 reload = 1;
143 break;
144 case 'p': /* port number */
145 sscanf(optarg, "%d", &port);
146 if (port < 1)
147 port = CACHE_HTTP_PORT; /* default */
148 break;
cca89eeb 149 case 'P':
16300b58 150 put_file = xstrdup(optarg);
cca89eeb 151 break;
234967c9 152 case 'i': /* IMS */
153 ims = (time_t) atoi(optarg);
154 break;
155 case 'm':
156 method = xstrdup(optarg);
157 break;
b3b64e58 158 case 't':
159 method = xstrdup("TRACE");
160 max_forwards = atoi(optarg);
161 break;
899bab3f 162 case 'g':
163 ping = 1;
164 pcount = atoi(optarg);
165 to_stdout = 0;
166 break;
167 case 'I':
168 if ((ping_int = atoi(optarg) * 1000) <= 0)
169 usage(argv[0]);
170 break;
63259c34 171 case 'H':
172 if (strlen(optarg)) {
4e41e277 173 char *t;
63259c34 174 strncpy(extra_hdrs, optarg, sizeof(extra_hdrs));
4e41e277 175 while ((t = strstr(extra_hdrs, "\\n")))
b644367b 176 *t = '\r', *(t + 1) = '\n';
63259c34 177 }
178 break;
19c2646d 179 case 'T':
338c5e76 180 io_timeout = atoi(optarg);
181 break;
63259c34 182 case 'v':
183 /* undocumented: may increase verb-level by giving more -v's */
184 opt_verbose++;
185 break;
090089c4 186 case '?': /* usage */
187 default:
188 usage(argv[0]);
189 break;
190 }
191 }
090089c4 192 /* Build the HTTP request */
8a9b6b94 193 if (strncmp(url, "mgr:", 4) == 0) {
194 char *t = xstrdup(url + 4);
195 snprintf(url, BUFSIZ, "cache_object://%s/%s", hostname, t);
e5f4e1b0 196 xfree(t);
8a9b6b94 197 }
cca89eeb 198 if (put_file) {
16300b58 199 put_fd = open(put_file, O_RDONLY);
54220df8 200 set_our_signal();
16300b58 201 if (put_fd < 0) {
202 fprintf(stderr, "%s: can't open file (%s)\n", argv[0],
203 xstrerror());
204 exit(-1);
cca89eeb 205 }
c4aefe96 206#if defined(_SQUID_CYGWIN_)
207 setmode(put_fd, O_BINARY);
208#endif
b6c6bcef 209 fstat(put_fd, &sb);
cca89eeb 210 }
042461c3 211 snprintf(msg, BUFSIZ, "%s %s HTTP/1.0\r\n", method, url);
090089c4 212 if (reload) {
042461c3 213 snprintf(buf, BUFSIZ, "Pragma: no-cache\r\n");
234967c9 214 strcat(msg, buf);
215 }
16300b58 216 if (put_fd > 0) {
5f6ac48b 217 snprintf(buf, BUFSIZ, "Content-length: %d\r\n", (int) sb.st_size);
cca89eeb 218 strcat(msg, buf);
219 }
88738790 220 if (opt_noaccept == 0) {
042461c3 221 snprintf(buf, BUFSIZ, "Accept: */*\r\n");
88738790 222 strcat(msg, buf);
223 }
234967c9 224 if (ims) {
042461c3 225 snprintf(buf, BUFSIZ, "If-Modified-Since: %s\r\n", mkrfc1123(ims));
234967c9 226 strcat(msg, buf);
090089c4 227 }
b3b64e58 228 if (max_forwards > -1) {
042461c3 229 snprintf(buf, BUFSIZ, "Max-Forwards: %d\r\n", max_forwards);
b3b64e58 230 strcat(msg, buf);
231 }
599eadbe 232 if (keep_alive) {
c62815be 233 if (port != 80)
99edd1c3 234 snprintf(buf, BUFSIZ, "Proxy-Connection: keep-alive\r\n");
c62815be 235 else
99edd1c3 236 snprintf(buf, BUFSIZ, "Connection: keep-alive\r\n");
599eadbe 237 strcat(msg, buf);
238 }
63259c34 239 strcat(msg, extra_hdrs);
042461c3 240 snprintf(buf, BUFSIZ, "\r\n");
234967c9 241 strcat(msg, buf);
090089c4 242
63259c34 243 if (opt_verbose)
244 fprintf(stderr, "headers: '%s'\n", msg);
245
899bab3f 246 if (ping) {
247#if HAVE_SIGACTION
248 struct sigaction sa, osa;
249 if (sigaction(SIGINT, NULL, &osa) == 0 && osa.sa_handler == SIG_DFL) {
250 sa.sa_handler = catch;
251 sa.sa_flags = 0;
252 sigemptyset(&sa.sa_mask);
253 (void) sigaction(SIGINT, &sa, NULL);
254 }
255#else
256 void (*osig) ();
257 if ((osig = signal(SIGINT, catch)) != SIG_DFL)
258 (void) signal(SIGINT, osig);
259#endif
260 }
261 loops = ping ? pcount : 1;
262 for (i = 0; loops == 0 || i < loops; i++) {
d20b1cd0 263 int fsize = 0;
899bab3f 264 /* Connect to the server */
265 if ((conn = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
266 perror("client: socket");
267 exit(1);
268 }
2c08acd9 269 if (localhost && client_comm_bind(conn, localhost) < 0) {
270 perror("client: bind");
271 exit(1);
272 }
899bab3f 273 if (client_comm_connect(conn, hostname, port, ping ? &tv1 : NULL) < 0) {
274 if (errno == 0) {
275 fprintf(stderr, "client: ERROR: Cannot connect to %s:%d: Host unknown.\n", hostname, port);
276 } else {
277 char tbuf[BUFSIZ];
2bbd722b 278 snprintf(tbuf, BUFSIZ, "client: ERROR: Cannot connect to %s:%d",
899bab3f 279 hostname, port);
280 perror(tbuf);
281 }
282 exit(1);
283 }
284 /* Send the HTTP request */
20cbfe5a 285 bytesWritten = mywrite(conn, msg, strlen(msg));
899bab3f 286 if (bytesWritten < 0) {
287 perror("client: ERROR: write");
288 exit(1);
289 } else if (bytesWritten != strlen(msg)) {
290 fprintf(stderr, "client: ERROR: Cannot send request?: %s\n", msg);
291 exit(1);
292 }
cca89eeb 293 if (put_file) {
16300b58 294 int x;
9b7b8edd 295 lseek(put_fd, 0, SEEK_SET);
20cbfe5a 296 while ((x = myread(put_fd, buf, sizeof(buf))) > 0) {
297 x = mywrite(conn, buf, x);
b6c6bcef 298 total_bytes += x;
16300b58 299 if (x <= 0)
300 break;
301 }
b6c6bcef 302 if (x != 0)
16300b58 303 fprintf(stderr, "client: ERROR: Cannot send file.\n");
cca89eeb 304 }
899bab3f 305 /* Read the data */
54220df8 306
20cbfe5a 307 while ((len = myread(conn, buf, sizeof(buf))) > 0) {
d20b1cd0 308 fsize += len;
899bab3f 309 if (to_stdout)
310 fwrite(buf, len, 1, stdout);
311 }
312 (void) close(conn); /* done with socket */
313
314 if (interrupted)
315 break;
316
317 if (ping) {
318 struct tm *tmp;
319 time_t t2s;
320 long elapsed_msec;
321
322 (void) Now(&tv2);
323 elapsed_msec = tvSubMsec(tv1, tv2);
324 t2s = tv2.tv_sec;
325 tmp = localtime(&t2s);
d20b1cd0 326 fprintf(stderr, "%d-%02d-%02d %02d:%02d:%02d [%d]: %ld.%03ld secs, %f KB/s\n",
899bab3f 327 tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday,
328 tmp->tm_hour, tmp->tm_min, tmp->tm_sec, i + 1,
d20b1cd0 329 elapsed_msec / 1000, elapsed_msec % 1000,
330 elapsed_msec ? (double) fsize / elapsed_msec : -1.0);
899bab3f 331 if (i == 0 || elapsed_msec < ping_min)
332 ping_min = elapsed_msec;
333 if (i == 0 || elapsed_msec > ping_max)
334 ping_max = elapsed_msec;
335 ping_sum += elapsed_msec;
336 /* Delay until next "ping_int" boundary */
337 if ((loops == 0 || i + 1 < loops) && elapsed_msec < ping_int) {
338 struct timeval tvs;
339 long msec_left = ping_int - elapsed_msec;
340
341 tvs.tv_sec = msec_left / 1000;
342 tvs.tv_usec = (msec_left % 1000) * 1000;
343 select(0, NULL, NULL, NULL, &tvs);
344 }
345 }
090089c4 346 }
899bab3f 347
348 if (ping && i) {
349 ping_mean = ping_sum / i;
350 fprintf(stderr, "%d requests, round-trip (secs) min/avg/max = "
351 "%ld.%03ld/%ld.%03ld/%ld.%03ld\n", i,
352 ping_min / 1000, ping_min % 1000, ping_mean / 1000, ping_mean % 1000,
353 ping_max / 1000, ping_max % 1000);
090089c4 354 }
090089c4 355 exit(0);
356 /*NOTREACHED */
983061ed 357 return 0;
090089c4 358}
359
2c08acd9 360static int
a2c963ae 361client_comm_bind(int sock, const char *local_host)
2c08acd9 362{
363 static const struct hostent *hp = NULL;
364 static struct sockaddr_in from_addr;
365
366 /* Set up the source socket address from which to send. */
367 if (hp == NULL) {
368 from_addr.sin_family = AF_INET;
369
370 if ((hp = gethostbyname(local_host)) == 0) {
371 return (-1);
372 }
373 xmemcpy(&from_addr.sin_addr, hp->h_addr, hp->h_length);
374 from_addr.sin_port = 0;
375 }
376 return bind(sock, (struct sockaddr *) &from_addr, sizeof(struct sockaddr_in));
377}
378
b8d8561b 379static int
a2c963ae 380client_comm_connect(int sock, const char *dest_host, u_short dest_port, struct timeval *tvp)
090089c4 381{
899bab3f 382 static const struct hostent *hp = NULL;
090089c4 383 static struct sockaddr_in to_addr;
384
385 /* Set up the destination socket address for message to send to. */
899bab3f 386 if (hp == NULL) {
387 to_addr.sin_family = AF_INET;
090089c4 388
899bab3f 389 if ((hp = gethostbyname(dest_host)) == 0) {
390 return (-1);
391 }
392 xmemcpy(&to_addr.sin_addr, hp->h_addr, hp->h_length);
393 to_addr.sin_port = htons(dest_port);
090089c4 394 }
899bab3f 395 if (tvp)
396 (void) Now(tvp);
090089c4 397 return connect(sock, (struct sockaddr *) &to_addr, sizeof(struct sockaddr_in));
398}
899bab3f 399
400static int
401Now(struct timeval *tp)
402{
403#if GETTIMEOFDAY_NO_TZP
404 return gettimeofday(tp);
405#else
406 return gettimeofday(tp, NULL);
407#endif
408} /* ARGSUSED */
409
410static void
411catch(int sig)
412{
413 interrupted = 1;
414 fprintf(stderr, "Interrupted.\n");
415}
b6c6bcef 416
5446b331 417static void
54220df8 418pipe_handler(int sig)
419{
b6c6bcef 420 fprintf(stderr, "SIGPIPE received.\n");
54220df8 421}
422
423static void
9bc73deb 424set_our_signal(void)
54220df8 425{
426#if HAVE_SIGACTION
427 struct sigaction sa;
428 sa.sa_handler = pipe_handler;
429 sa.sa_flags = SA_RESTART;
430 sigemptyset(&sa.sa_mask);
431 if (sigaction(SIGPIPE, &sa, NULL) < 0) {
b6c6bcef 432 fprintf(stderr, "Cannot set PIPE signal.\n");
54220df8 433 exit(-1);
434 }
435#else
436 signal(SIGPIPE, pipe_handler);
437#endif
438
439}
20cbfe5a 440
441static ssize_t
442myread(int fd, void *buf, size_t len)
443{
444 alarm(io_timeout);
445 return read(fd, buf, len);
446}
447
448static ssize_t
449mywrite(int fd, void *buf, size_t len)
450{
451 alarm(io_timeout);
452 return write(fd, buf, len);
453}