]>
Commit | Line | Data |
---|---|---|
16300b58 | 1 | |
227e68b6 | 2 | |
e3dd531e | 3 | |
30a4f2a8 | 4 | /* |
e3dd531e | 5 | * $Id: client.cc,v 1.81 1999/01/24 02:26:22 wessels Exp $ |
30a4f2a8 | 6 | * |
7 | * DEBUG: section 0 WWW Client | |
8 | * AUTHOR: Harvest Derived | |
9 | * | |
42c04c16 | 10 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ |
e25c139f | 11 | * ---------------------------------------------------------- |
30a4f2a8 | 12 | * |
13 | * Squid is the result of efforts by numerous individuals from the | |
14 | * Internet community. Development is led by Duane Wessels of the | |
e25c139f | 15 | * National Laboratory for Applied Network Research and funded by the |
16 | * National Science Foundation. Squid is Copyrighted (C) 1998 by | |
17 | * Duane Wessels and the University of California San Diego. Please | |
18 | * see the COPYRIGHT file for full details. Squid incorporates | |
19 | * software developed and/or copyrighted by other sources. Please see | |
20 | * the CREDITS file for full details. | |
30a4f2a8 | 21 | * |
22 | * This program is free software; you can redistribute it and/or modify | |
23 | * it under the terms of the GNU General Public License as published by | |
24 | * the Free Software Foundation; either version 2 of the License, or | |
25 | * (at your option) any later version. | |
26 | * | |
27 | * This program is distributed in the hope that it will be useful, | |
28 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
29 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
30 | * GNU General Public License for more details. | |
31 | * | |
32 | * You should have received a copy of the GNU General Public License | |
33 | * along with this program; if not, write to the Free Software | |
cbdec147 | 34 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 35 | * |
30a4f2a8 | 36 | */ |
090089c4 | 37 | |
44a47c6e | 38 | #include "squid.h" |
090089c4 | 39 | |
40 | #ifndef BUFSIZ | |
41 | #define BUFSIZ 8192 | |
42 | #endif | |
43 | ||
44 | /* Local functions */ | |
899bab3f | 45 | static int client_comm_connect(int, char *, u_short, struct timeval *); |
f5b8bbc4 | 46 | static void usage(const char *progname); |
899bab3f | 47 | static int Now(struct timeval *); |
48 | static SIGHDLR catch; | |
54220df8 | 49 | static SIGHDLR pipe_handler; |
50 | static void set_our_signal(); | |
cca89eeb | 51 | static int put_fd; |
52 | static char *put_file = NULL; | |
b6c6bcef | 53 | static struct stat sb; |
54 | int total_bytes = 0; | |
090089c4 | 55 | |
b8d8561b | 56 | static void |
0ee4272b | 57 | usage(const char *progname) |
090089c4 | 58 | { |
0ee4272b | 59 | fprintf(stderr, |
63259c34 | 60 | "Usage: %s [-arsv] [-i IMS] [-h host] [-p port] [-m method] [-t count] [-I ping-interval] [-H 'strings'] url\n" |
fe4e214f | 61 | "Options:\n" |
cca89eeb | 62 | " -P file PUT request.\n" |
899bab3f | 63 | " -a Do NOT include Accept: header.\n" |
64 | " -r Force cache to reload URL.\n" | |
65 | " -s Silent. Do not print data to stdout.\n" | |
63259c34 | 66 | " -v Verbose. Print outgoing message to stderr.\n" |
899bab3f | 67 | " -i IMS If-Modified-Since time (in Epoch seconds).\n" |
68 | " -h host Retrieve URL from cache on hostname. Default is localhost.\n" | |
69 | " -p port Port number of cache. Default is %d.\n" | |
70 | " -m method Request method, default is GET.\n" | |
71 | " -t count Trace count cache-hops\n" | |
72 | " -g count Ping mode, \"count\" iterations (0 to loop until interrupted).\n" | |
63259c34 | 73 | " -I interval Ping interval in seconds (default 1 second).\n" |
99edd1c3 | 74 | " -H 'string' Extra headers to send. Use '\\n' for new lines.\n", |
fe4e214f | 75 | progname, CACHE_HTTP_PORT); |
090089c4 | 76 | exit(1); |
77 | } | |
78 | ||
899bab3f | 79 | static int interrupted = 0; |
b8d8561b | 80 | int |
81 | main(int argc, char *argv[]) | |
090089c4 | 82 | { |
83 | int conn, c, len, bytesWritten; | |
84 | int port, to_stdout, reload; | |
899bab3f | 85 | int ping, pcount; |
599eadbe | 86 | int keep_alive = 0; |
88738790 | 87 | int opt_noaccept = 0; |
cca89eeb | 88 | int opt_put = 0; |
63259c34 | 89 | int opt_verbose = 0; |
090089c4 | 90 | char url[BUFSIZ], msg[BUFSIZ], buf[BUFSIZ], hostname[BUFSIZ]; |
63259c34 | 91 | char extra_hdrs[BUFSIZ]; |
0ee4272b | 92 | const char *method = "GET"; |
090089c4 | 93 | extern char *optarg; |
234967c9 | 94 | time_t ims = 0; |
b3b64e58 | 95 | int max_forwards = -1; |
899bab3f | 96 | struct timeval tv1, tv2; |
97 | int i = 0, loops; | |
98 | long ping_int; | |
99 | long ping_min = 0, ping_max = 0, ping_sum = 0, ping_mean = 0; | |
090089c4 | 100 | |
101 | /* set the defaults */ | |
102 | strcpy(hostname, "localhost"); | |
63259c34 | 103 | extra_hdrs[0] = '\0'; |
090089c4 | 104 | port = CACHE_HTTP_PORT; |
105 | to_stdout = 1; | |
106 | reload = 0; | |
899bab3f | 107 | ping = 0; |
108 | pcount = 0; | |
109 | ping_int = 1 * 1000; | |
090089c4 | 110 | |
111 | if (argc < 2) { | |
112 | usage(argv[0]); /* need URL */ | |
113 | } else if (argc >= 2) { | |
114 | strcpy(url, argv[argc - 1]); | |
115 | if (url[0] == '-') | |
116 | usage(argv[0]); | |
63259c34 | 117 | while ((c = getopt(argc, argv, "ah:P:i:km:p:rsvt:g:p:I:H:?")) != -1) |
090089c4 | 118 | switch (c) { |
88738790 | 119 | case 'a': |
120 | opt_noaccept = 1; | |
121 | break; | |
090089c4 | 122 | case 'h': /* host:arg */ |
090089c4 | 123 | if (optarg != NULL) |
124 | strcpy(hostname, optarg); | |
125 | break; | |
126 | case 's': /* silent */ | |
090089c4 | 127 | to_stdout = 0; |
128 | break; | |
599eadbe | 129 | case 'k': /* backward compat */ |
130 | keep_alive = 1; | |
131 | break; | |
090089c4 | 132 | case 'r': /* reload */ |
133 | reload = 1; | |
134 | break; | |
135 | case 'p': /* port number */ | |
136 | sscanf(optarg, "%d", &port); | |
137 | if (port < 1) | |
138 | port = CACHE_HTTP_PORT; /* default */ | |
139 | break; | |
cca89eeb | 140 | case 'P': |
16300b58 | 141 | put_file = xstrdup(optarg); |
cca89eeb | 142 | break; |
234967c9 | 143 | case 'i': /* IMS */ |
144 | ims = (time_t) atoi(optarg); | |
145 | break; | |
146 | case 'm': | |
147 | method = xstrdup(optarg); | |
148 | break; | |
b3b64e58 | 149 | case 't': |
150 | method = xstrdup("TRACE"); | |
151 | max_forwards = atoi(optarg); | |
152 | break; | |
899bab3f | 153 | case 'g': |
154 | ping = 1; | |
155 | pcount = atoi(optarg); | |
156 | to_stdout = 0; | |
157 | break; | |
158 | case 'I': | |
159 | if ((ping_int = atoi(optarg) * 1000) <= 0) | |
160 | usage(argv[0]); | |
161 | break; | |
63259c34 | 162 | case 'H': |
163 | if (strlen(optarg)) { | |
4e41e277 | 164 | char *t; |
63259c34 | 165 | strncpy(extra_hdrs, optarg, sizeof(extra_hdrs)); |
4e41e277 | 166 | while ((t = strstr(extra_hdrs, "\\n"))) |
b644367b | 167 | *t = '\r', *(t + 1) = '\n'; |
63259c34 | 168 | } |
169 | break; | |
170 | case 'v': | |
171 | /* undocumented: may increase verb-level by giving more -v's */ | |
172 | opt_verbose++; | |
173 | break; | |
090089c4 | 174 | case '?': /* usage */ |
175 | default: | |
176 | usage(argv[0]); | |
177 | break; | |
178 | } | |
179 | } | |
090089c4 | 180 | /* Build the HTTP request */ |
8a9b6b94 | 181 | if (strncmp(url, "mgr:", 4) == 0) { |
182 | char *t = xstrdup(url + 4); | |
183 | snprintf(url, BUFSIZ, "cache_object://%s/%s", hostname, t); | |
e5f4e1b0 | 184 | xfree(t); |
8a9b6b94 | 185 | } |
cca89eeb | 186 | if (put_file) { |
16300b58 | 187 | opt_put = 1; |
b6c6bcef | 188 | /*method = xstrdup("PUT"); */ |
16300b58 | 189 | put_fd = open(put_file, O_RDONLY); |
54220df8 | 190 | set_our_signal(); |
16300b58 | 191 | if (put_fd < 0) { |
192 | fprintf(stderr, "%s: can't open file (%s)\n", argv[0], | |
193 | xstrerror()); | |
194 | exit(-1); | |
cca89eeb | 195 | } |
b6c6bcef | 196 | fstat(put_fd, &sb); |
cca89eeb | 197 | } |
042461c3 | 198 | snprintf(msg, BUFSIZ, "%s %s HTTP/1.0\r\n", method, url); |
090089c4 | 199 | if (reload) { |
042461c3 | 200 | snprintf(buf, BUFSIZ, "Pragma: no-cache\r\n"); |
234967c9 | 201 | strcat(msg, buf); |
202 | } | |
16300b58 | 203 | if (put_fd > 0) { |
5f6ac48b | 204 | snprintf(buf, BUFSIZ, "Content-length: %d\r\n", (int) sb.st_size); |
cca89eeb | 205 | strcat(msg, buf); |
206 | } | |
88738790 | 207 | if (opt_noaccept == 0) { |
042461c3 | 208 | snprintf(buf, BUFSIZ, "Accept: */*\r\n"); |
88738790 | 209 | strcat(msg, buf); |
210 | } | |
234967c9 | 211 | if (ims) { |
042461c3 | 212 | snprintf(buf, BUFSIZ, "If-Modified-Since: %s\r\n", mkrfc1123(ims)); |
234967c9 | 213 | strcat(msg, buf); |
090089c4 | 214 | } |
b3b64e58 | 215 | if (max_forwards > -1) { |
042461c3 | 216 | snprintf(buf, BUFSIZ, "Max-Forwards: %d\r\n", max_forwards); |
b3b64e58 | 217 | strcat(msg, buf); |
218 | } | |
599eadbe | 219 | if (keep_alive) { |
c62815be | 220 | if (port != 80) |
99edd1c3 | 221 | snprintf(buf, BUFSIZ, "Proxy-Connection: keep-alive\r\n"); |
c62815be | 222 | else |
99edd1c3 | 223 | snprintf(buf, BUFSIZ, "Connection: keep-alive\r\n"); |
599eadbe | 224 | strcat(msg, buf); |
225 | } | |
63259c34 | 226 | strcat(msg, extra_hdrs); |
042461c3 | 227 | snprintf(buf, BUFSIZ, "\r\n"); |
234967c9 | 228 | strcat(msg, buf); |
090089c4 | 229 | |
63259c34 | 230 | if (opt_verbose) |
231 | fprintf(stderr, "headers: '%s'\n", msg); | |
232 | ||
899bab3f | 233 | if (ping) { |
234 | #if HAVE_SIGACTION | |
235 | struct sigaction sa, osa; | |
236 | if (sigaction(SIGINT, NULL, &osa) == 0 && osa.sa_handler == SIG_DFL) { | |
237 | sa.sa_handler = catch; | |
238 | sa.sa_flags = 0; | |
239 | sigemptyset(&sa.sa_mask); | |
240 | (void) sigaction(SIGINT, &sa, NULL); | |
241 | } | |
242 | #else | |
243 | void (*osig) (); | |
244 | if ((osig = signal(SIGINT, catch)) != SIG_DFL) | |
245 | (void) signal(SIGINT, osig); | |
246 | #endif | |
247 | } | |
248 | loops = ping ? pcount : 1; | |
249 | for (i = 0; loops == 0 || i < loops; i++) { | |
250 | /* Connect to the server */ | |
251 | if ((conn = socket(PF_INET, SOCK_STREAM, 0)) < 0) { | |
252 | perror("client: socket"); | |
253 | exit(1); | |
254 | } | |
255 | if (client_comm_connect(conn, hostname, port, ping ? &tv1 : NULL) < 0) { | |
256 | if (errno == 0) { | |
257 | fprintf(stderr, "client: ERROR: Cannot connect to %s:%d: Host unknown.\n", hostname, port); | |
258 | } else { | |
259 | char tbuf[BUFSIZ]; | |
2bbd722b | 260 | snprintf(tbuf, BUFSIZ, "client: ERROR: Cannot connect to %s:%d", |
899bab3f | 261 | hostname, port); |
262 | perror(tbuf); | |
263 | } | |
264 | exit(1); | |
265 | } | |
266 | /* Send the HTTP request */ | |
267 | bytesWritten = write(conn, msg, strlen(msg)); | |
268 | if (bytesWritten < 0) { | |
269 | perror("client: ERROR: write"); | |
270 | exit(1); | |
271 | } else if (bytesWritten != strlen(msg)) { | |
272 | fprintf(stderr, "client: ERROR: Cannot send request?: %s\n", msg); | |
273 | exit(1); | |
274 | } | |
cca89eeb | 275 | if (put_file) { |
16300b58 | 276 | int x; |
9b7b8edd | 277 | lseek(put_fd, 0, SEEK_SET); |
419590f3 | 278 | while ((x = read(put_fd, buf, sizeof(buf))) > 0) { |
279 | x = write(conn, buf, x); | |
b6c6bcef | 280 | total_bytes += x; |
16300b58 | 281 | if (x <= 0) |
282 | break; | |
283 | } | |
b6c6bcef | 284 | if (x != 0) |
16300b58 | 285 | fprintf(stderr, "client: ERROR: Cannot send file.\n"); |
cca89eeb | 286 | } |
899bab3f | 287 | /* Read the data */ |
54220df8 | 288 | |
899bab3f | 289 | while ((len = read(conn, buf, sizeof(buf))) > 0) { |
290 | if (to_stdout) | |
291 | fwrite(buf, len, 1, stdout); | |
292 | } | |
293 | (void) close(conn); /* done with socket */ | |
294 | ||
295 | if (interrupted) | |
296 | break; | |
297 | ||
298 | if (ping) { | |
299 | struct tm *tmp; | |
300 | time_t t2s; | |
301 | long elapsed_msec; | |
302 | ||
303 | (void) Now(&tv2); | |
304 | elapsed_msec = tvSubMsec(tv1, tv2); | |
305 | t2s = tv2.tv_sec; | |
306 | tmp = localtime(&t2s); | |
307 | fprintf(stderr, "%d-%02d-%02d %02d:%02d:%02d [%d]: %ld.%03ld secs\n", | |
308 | tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday, | |
309 | tmp->tm_hour, tmp->tm_min, tmp->tm_sec, i + 1, | |
310 | elapsed_msec / 1000, elapsed_msec % 1000); | |
311 | if (i == 0 || elapsed_msec < ping_min) | |
312 | ping_min = elapsed_msec; | |
313 | if (i == 0 || elapsed_msec > ping_max) | |
314 | ping_max = elapsed_msec; | |
315 | ping_sum += elapsed_msec; | |
316 | /* Delay until next "ping_int" boundary */ | |
317 | if ((loops == 0 || i + 1 < loops) && elapsed_msec < ping_int) { | |
318 | struct timeval tvs; | |
319 | long msec_left = ping_int - elapsed_msec; | |
320 | ||
321 | tvs.tv_sec = msec_left / 1000; | |
322 | tvs.tv_usec = (msec_left % 1000) * 1000; | |
323 | select(0, NULL, NULL, NULL, &tvs); | |
324 | } | |
325 | } | |
090089c4 | 326 | } |
899bab3f | 327 | |
328 | if (ping && i) { | |
329 | ping_mean = ping_sum / i; | |
330 | fprintf(stderr, "%d requests, round-trip (secs) min/avg/max = " | |
331 | "%ld.%03ld/%ld.%03ld/%ld.%03ld\n", i, | |
332 | ping_min / 1000, ping_min % 1000, ping_mean / 1000, ping_mean % 1000, | |
333 | ping_max / 1000, ping_max % 1000); | |
090089c4 | 334 | } |
090089c4 | 335 | exit(0); |
336 | /*NOTREACHED */ | |
983061ed | 337 | return 0; |
090089c4 | 338 | } |
339 | ||
b8d8561b | 340 | static int |
899bab3f | 341 | client_comm_connect(int sock, char *dest_host, u_short dest_port, struct timeval *tvp) |
090089c4 | 342 | { |
899bab3f | 343 | static const struct hostent *hp = NULL; |
090089c4 | 344 | static struct sockaddr_in to_addr; |
345 | ||
346 | /* Set up the destination socket address for message to send to. */ | |
899bab3f | 347 | if (hp == NULL) { |
348 | to_addr.sin_family = AF_INET; | |
090089c4 | 349 | |
899bab3f | 350 | if ((hp = gethostbyname(dest_host)) == 0) { |
351 | return (-1); | |
352 | } | |
353 | xmemcpy(&to_addr.sin_addr, hp->h_addr, hp->h_length); | |
354 | to_addr.sin_port = htons(dest_port); | |
090089c4 | 355 | } |
899bab3f | 356 | if (tvp) |
357 | (void) Now(tvp); | |
090089c4 | 358 | return connect(sock, (struct sockaddr *) &to_addr, sizeof(struct sockaddr_in)); |
359 | } | |
899bab3f | 360 | |
361 | static int | |
362 | Now(struct timeval *tp) | |
363 | { | |
364 | #if GETTIMEOFDAY_NO_TZP | |
365 | return gettimeofday(tp); | |
366 | #else | |
367 | return gettimeofday(tp, NULL); | |
368 | #endif | |
369 | } /* ARGSUSED */ | |
370 | ||
371 | static void | |
372 | catch(int sig) | |
373 | { | |
374 | interrupted = 1; | |
375 | fprintf(stderr, "Interrupted.\n"); | |
376 | } | |
b6c6bcef | 377 | |
5446b331 | 378 | static void |
54220df8 | 379 | pipe_handler(int sig) |
380 | { | |
b6c6bcef | 381 | fprintf(stderr, "SIGPIPE received.\n"); |
54220df8 | 382 | } |
383 | ||
384 | static void | |
385 | set_our_signal() | |
386 | { | |
387 | #if HAVE_SIGACTION | |
388 | struct sigaction sa; | |
389 | sa.sa_handler = pipe_handler; | |
390 | sa.sa_flags = SA_RESTART; | |
391 | sigemptyset(&sa.sa_mask); | |
392 | if (sigaction(SIGPIPE, &sa, NULL) < 0) { | |
b6c6bcef | 393 | fprintf(stderr, "Cannot set PIPE signal.\n"); |
54220df8 | 394 | exit(-1); |
395 | } | |
396 | #else | |
397 | signal(SIGPIPE, pipe_handler); | |
398 | #endif | |
399 | ||
400 | } |