]>
Commit | Line | Data |
---|---|---|
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 | 43 | static int client_comm_bind(int, const char *); |
44 | static int client_comm_connect(int, const char *, u_short, struct timeval *); | |
f5b8bbc4 | 45 | static void usage(const char *progname); |
899bab3f | 46 | static int Now(struct timeval *); |
47 | static SIGHDLR catch; | |
54220df8 | 48 | static SIGHDLR pipe_handler; |
d6d09e02 | 49 | static void set_our_signal(void); |
20cbfe5a | 50 | static ssize_t myread(int fd, void *buf, size_t len); |
51 | static ssize_t mywrite(int fd, void *buf, size_t len); | |
cca89eeb | 52 | static int put_fd; |
53 | static char *put_file = NULL; | |
b6c6bcef | 54 | static struct stat sb; |
55 | int total_bytes = 0; | |
20cbfe5a | 56 | int io_timeout = 120; |
090089c4 | 57 | |
b8d8561b | 58 | static void |
0ee4272b | 59 | usage(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 | 83 | static int interrupted = 0; |
b8d8561b | 84 | int |
85 | main(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 | 360 | static int |
a2c963ae | 361 | client_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 | 379 | static int |
a2c963ae | 380 | client_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 | |
400 | static int | |
401 | Now(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 | ||
410 | static void | |
411 | catch(int sig) | |
412 | { | |
413 | interrupted = 1; | |
414 | fprintf(stderr, "Interrupted.\n"); | |
415 | } | |
b6c6bcef | 416 | |
5446b331 | 417 | static void |
54220df8 | 418 | pipe_handler(int sig) |
419 | { | |
b6c6bcef | 420 | fprintf(stderr, "SIGPIPE received.\n"); |
54220df8 | 421 | } |
422 | ||
423 | static void | |
9bc73deb | 424 | set_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 | |
441 | static ssize_t | |
442 | myread(int fd, void *buf, size_t len) | |
443 | { | |
444 | alarm(io_timeout); | |
445 | return read(fd, buf, len); | |
446 | } | |
447 | ||
448 | static ssize_t | |
449 | mywrite(int fd, void *buf, size_t len) | |
450 | { | |
451 | alarm(io_timeout); | |
452 | return write(fd, buf, len); | |
453 | } |