]> git.ipfire.org Git - thirdparty/squid.git/blame - src/http.cc
add magic marker stuff, remove ftpget thread stuff
[thirdparty/squid.git] / src / http.cc
CommitLineData
b367f261 1/* $Id: http.cc,v 1.7 1996/03/25 19:05:50 wessels Exp $ */
ed43818f 2
090089c4 3#include "config.h"
4#include <sys/errno.h>
5#include <stdlib.h>
6#include <string.h>
7#include <unistd.h>
8
9#include "ansihelp.h"
10#include "comm.h"
11#include "store.h"
12#include "stat.h"
13#include "url.h"
14#include "ipcache.h"
15#include "cache_cf.h"
16#include "ttl.h"
17#include "icp.h"
18#include "util.h"
d1cfbef7 19#include "cached_error.h"
090089c4 20
21#define HTTP_PORT 80
22#define HTTP_DELETE_GAP (64*1024)
b367f261 23#define READBUFSIZ 4096
090089c4 24
25extern int errno;
26extern char *dns_error_message;
27extern time_t cached_curtime;
28
29typedef struct _httpdata {
30 StoreEntry *entry;
ed43818f 31 char host[SQUIDHOSTNAMELEN + 1];
090089c4 32 int port;
33 char *type;
34 char *mime_hdr;
35 char type_id;
36 char request[MAX_URL + 1];
37 char *icp_page_ptr; /* Used to send proxy-http request:
38 * put_free_8k_page(me) if the lifetime
39 * expires */
40 char *icp_rwd_ptr; /* When a lifetime expires during the
41 * middle of an icpwrite, don't lose the
42 * icpReadWriteData */
43} HttpData;
44
45extern char *tmp_error_buf;
46
47char *HTTP_OPS[] =
48{"GET", "POST", "HEAD", ""};
49
50int http_url_parser(url, host, port, request)
51 char *url;
52 char *host;
53 int *port;
54 char *request;
55{
56 static char hostbuf[MAX_URL];
57 static char atypebuf[MAX_URL];
58 int t;
59
60 /* initialize everything */
61 (*port) = 0;
62 atypebuf[0] = hostbuf[0] = request[0] = host[0] = '\0';
63
64 t = sscanf(url, "%[a-zA-Z]://%[^/]%s", atypebuf, hostbuf, request);
65 if ((t < 2) || (strcasecmp(atypebuf, "http") != 0)) {
66 return -1;
67 } else if (t == 2) {
68 strcpy(request, "/");
69 }
70 if (sscanf(hostbuf, "%[^:]:%d", host, port) < 2)
71 (*port) = HTTP_PORT;
72 return 0;
73}
74
75int httpCachable(url, type, mime_hdr)
76 char *url;
77 char *type;
78 char *mime_hdr;
79{
80 stoplist *p;
81
82 /* GET and HEAD are cachable. Others are not. */
83 if (((strncasecmp(type, "GET", 3) != 0)) &&
84 (strncasecmp(type, "HEAD", 4) != 0))
85 return 0;
86
87 /* url's requiring authentication are uncachable */
88 if (mime_hdr && (strstr(mime_hdr, "Authorization")))
89 return 0;
90
91 /* scan stop list */
92 p = http_stoplist;
93 while (p) {
94 if (strstr(url, p->key))
95 return 0;
96 p = p->next;
97 }
98
99 /* else cachable */
100 return 1;
101}
102
103/* This will be called when timeout on read. */
104void httpReadReplyTimeout(fd, data)
105 int fd;
106 HttpData *data;
107{
108 StoreEntry *entry = NULL;
109
110 entry = data->entry;
111 debug(4, "httpReadReplyTimeout: FD %d: <URL:%s>\n", fd, entry->url);
b367f261 112 cached_error_entry(entry, ERR_READ_TIMEOUT, NULL);
090089c4 113 if (data->icp_rwd_ptr)
114 safe_free(data->icp_rwd_ptr);
115 if (data->icp_page_ptr) {
116 put_free_8k_page(data->icp_page_ptr);
117 data->icp_page_ptr = NULL;
118 }
090089c4 119 comm_set_select_handler(fd, COMM_SELECT_READ, 0, 0);
120 comm_close(fd);
090089c4 121 safe_free(data);
122}
123
124/* This will be called when socket lifetime is expired. */
125void httpLifetimeExpire(fd, data)
126 int fd;
127 HttpData *data;
128{
129 StoreEntry *entry = NULL;
130
131 entry = data->entry;
132 debug(4, "httpLifeTimeExpire: FD %d: <URL:%s>\n", fd, entry->url);
133
b367f261 134 cached_error_entry(entry, ERR_LIFETIME_EXP, NULL);
090089c4 135 if (data->icp_page_ptr) {
136 put_free_8k_page(data->icp_page_ptr);
137 data->icp_page_ptr = NULL;
138 }
139 if (data->icp_rwd_ptr)
140 safe_free(data->icp_rwd_ptr);
090089c4 141 comm_set_select_handler(fd, COMM_SELECT_READ | COMM_SELECT_WRITE, 0, 0);
142 comm_close(fd);
090089c4 143 safe_free(data);
144}
145
146
147
148/* This will be called when data is ready to be read from fd. Read until
149 * error or connection closed. */
150void httpReadReply(fd, data)
151 int fd;
152 HttpData *data;
153{
b367f261 154 static char buf[READBUFSIZ];
090089c4 155 int len;
156 int clen;
157 int off;
158 StoreEntry *entry = NULL;
159
160 entry = data->entry;
161 if (entry->flag & DELETE_BEHIND) {
162 if (storeClientWaiting(entry)) {
163 /* check if we want to defer reading */
22e4fa85 164 clen = entry->mem_obj->e_current_len;
165 off = entry->mem_obj->e_lowest_offset;
090089c4 166 if ((clen - off) > HTTP_DELETE_GAP) {
167 debug(3, "httpReadReply: Read deferred for Object: %s\n",
168 entry->key);
169 debug(3, " Current Gap: %d bytes\n",
170 clen - off);
171
172 /* reschedule, so it will be automatically reactivated
173 * when Gap is big enough. */
174 comm_set_select_handler(fd,
175 COMM_SELECT_READ,
176 (PF) httpReadReply,
177 (caddr_t) data);
178
179/* don't install read timeout until we are below the GAP */
180#ifdef INSTALL_READ_TIMEOUT_ABOVE_GAP
181 comm_set_select_handler_plus_timeout(fd,
182 COMM_SELECT_TIMEOUT,
183 (PF) httpReadReplyTimeout,
184 (caddr_t) data,
185 getReadTimeout());
186#else
187 comm_set_select_handler_plus_timeout(fd,
188 COMM_SELECT_TIMEOUT,
189 (PF) NULL,
190 (caddr_t) NULL,
191 (time_t) 0);
192#endif
193 return;
194 }
195 } else {
196 /* we can terminate connection right now */
b367f261 197 cached_error_entry(entry, ERR_NO_CLIENTS_BIG_OBJ, NULL);
090089c4 198 comm_close(fd);
090089c4 199 safe_free(data);
200 return;
201 }
202 }
b367f261 203 len = read(fd, buf, READBUFSIZ);
090089c4 204 debug(5, "httpReadReply: FD %d: len %d.\n", fd, len);
205
22e4fa85 206 if (len < 0 || ((len == 0) && (entry->mem_obj->e_current_len == 0))) {
090089c4 207 /* XXX we we should log when len==0 and current_len==0 */
208 debug(2, "httpReadReply: FD %d: read failure: %s.\n",
209 fd, xstrerror());
210 if (errno == ECONNRESET) {
211 /* Connection reset by peer */
212 /* consider it as a EOF */
213 if (!(entry->flag & DELETE_BEHIND))
214 entry->expires = cached_curtime + ttlSet(entry);
215 sprintf(tmp_error_buf, "\n<p>Warning: The Remote Server sent RESET at the end of transmission.\n");
216 storeAppend(entry, tmp_error_buf, strlen(tmp_error_buf));
217 storeComplete(entry);
218 } else {
b367f261 219 cached_error_entry(entry, ERR_READ_ERROR, xstrerror());
090089c4 220 }
221 comm_close(fd);
090089c4 222 safe_free(data);
223 } else if (len == 0) {
224 /* Connection closed; retrieval done. */
225 if (!(entry->flag & DELETE_BEHIND))
226 entry->expires = cached_curtime + ttlSet(entry);
227 storeComplete(entry);
228 comm_close(fd);
229 safe_free(data);
22e4fa85 230 } else if (((entry->mem_obj->e_current_len + len) > getHttpMax()) &&
090089c4 231 !(entry->flag & DELETE_BEHIND)) {
232 /* accept data, but start to delete behind it */
233 storeStartDeleteBehind(entry);
234
235 storeAppend(entry, buf, len);
236 comm_set_select_handler(fd, COMM_SELECT_READ,
237 (PF) httpReadReply, (caddr_t) data);
238 comm_set_select_handler_plus_timeout(fd, COMM_SELECT_TIMEOUT,
239 (PF) httpReadReplyTimeout, (caddr_t) data, getReadTimeout());
240
241 } else if (entry->flag & CLIENT_ABORT_REQUEST) {
242 /* append the last bit of info we get */
243 storeAppend(entry, buf, len);
b367f261 244 cached_error_entry(entry, ERR_CLIENT_ABORT, NULL);
090089c4 245 comm_close(fd);
090089c4 246 safe_free(data);
247 } else {
248 storeAppend(entry, buf, len);
249 comm_set_select_handler(fd, COMM_SELECT_READ,
250 (PF) httpReadReply, (caddr_t) data);
251 comm_set_select_handler_plus_timeout(fd, COMM_SELECT_TIMEOUT,
252 (PF) httpReadReplyTimeout, (caddr_t) data, getReadTimeout());
253 }
254}
255
256/* This will be called when request write is complete. Schedule read of
257 * reply. */
258void httpSendComplete(fd, buf, size, errflag, data)
259 int fd;
260 char *buf;
261 int size;
262 int errflag;
263 HttpData *data;
264{
265 StoreEntry *entry = NULL;
266
267 entry = data->entry;
268 debug(5, "httpSendComplete: FD %d: size %d: errflag %d.\n",
269 fd, size, errflag);
270
271 if (buf) {
272 put_free_8k_page(buf); /* Allocated by httpSendRequest. */
273 buf = NULL;
274 }
275 data->icp_page_ptr = NULL; /* So lifetime expire doesn't re-free */
276 data->icp_rwd_ptr = NULL; /* Don't double free in lifetimeexpire */
277
278 if (errflag) {
b367f261 279 cached_error_entry(entry, ERR_CONNECT_FAIL, xstrerror());
090089c4 280 comm_close(fd);
090089c4 281 safe_free(data);
282 return;
283 } else {
284 /* Schedule read reply. */
285 comm_set_select_handler(fd, COMM_SELECT_READ,
286 (PF) httpReadReply, (caddr_t) data);
287 comm_set_select_handler_plus_timeout(fd, COMM_SELECT_TIMEOUT,
288 (PF) httpReadReplyTimeout, (caddr_t) data, getReadTimeout());
289 comm_set_fd_lifetime(fd, -1); /* disable lifetime DPW */
290
291 }
292}
293
294/* This will be called when connect completes. Write request. */
295void httpSendRequest(fd, data)
296 int fd;
297 HttpData *data;
298{
299 char *xbuf = NULL;
300 char *ybuf = NULL;
301 char *buf = NULL;
302 char *t = NULL;
303 char *post_buf = NULL;
304 static char *crlf = "\r\n";
305 static char *HARVEST_PROXY_TEXT = "via Harvest Cache version";
306 int len = 0;
307 int buflen;
308
309 debug(5, "httpSendRequest: FD %d: data %p.\n", fd, data);
310 buflen = strlen(data->type) + strlen(data->request);
311 if (data->mime_hdr)
312 buflen += strlen(data->mime_hdr);
313 buflen += 512; /* lots of extra */
314
315 if (!strcasecmp(data->type, "POST") && data->mime_hdr) {
316 if ((t = strstr(data->mime_hdr, "\r\n\r\n"))) {
317 post_buf = xstrdup(t + 4);
318 *(t + 4) = '\0';
319 }
320 }
321 /* Since we limit the URL read to a 4K page, I doubt that the
322 * mime header could be longer than an 8K page */
323 buf = (char *) get_free_8k_page();
324 data->icp_page_ptr = buf;
325 if (buflen > DISK_PAGE_SIZE) {
326 debug(0, "Mime header length %d is breaking ICP code\n", buflen);
327 }
328 memset(buf, '\0', buflen);
329
330 sprintf(buf, "%s %s ", data->type, data->request);
331 len = strlen(buf);
332 if (data->mime_hdr) { /* we have to parse the MIME header */
333 xbuf = xstrdup(data->mime_hdr);
334 for (t = strtok(xbuf, crlf); t; t = strtok(NULL, crlf)) {
335 if (strncasecmp(t, "User-Agent:", 11) == 0) {
336 ybuf = (char *) get_free_4k_page();
337 memset(ybuf, '\0', SM_PAGE_SIZE);
73a5465f 338 sprintf(ybuf, "%s %s %s", t, HARVEST_PROXY_TEXT, SQUID_VERSION);
090089c4 339 t = ybuf;
340 }
341 if (strncasecmp(t, "If-Modified-Since:", 18) == 0)
342 continue;
343 if (len + (int) strlen(t) > buflen - 10)
344 continue;
345 strcat(buf, t);
346 strcat(buf, crlf);
347 len += strlen(t) + 2;
348 }
349 xfree(xbuf);
350 if (ybuf) {
351 put_free_4k_page(ybuf);
352 ybuf = NULL;
353 }
354 }
355 strcat(buf, crlf);
356 len += 2;
357 if (post_buf) {
358 strcat(buf, post_buf);
359 len += strlen(post_buf);
360 xfree(post_buf);
361 }
362 debug(6, "httpSendRequest: FD %d: buf '%s'\n", fd, buf);
363 data->icp_rwd_ptr = icpWrite(fd, buf, len, 30, httpSendComplete, data);
364}
365
366void httpConnInProgress(fd, data)
367 int fd;
368 HttpData *data;
369{
370 StoreEntry *entry = data->entry;
371
372 if (comm_connect(fd, data->host, data->port) != COMM_OK)
373 switch (errno) {
374 case EINPROGRESS:
375 case EALREADY:
376 /* schedule this handler again */
377 comm_set_select_handler(fd,
378 COMM_SELECT_WRITE,
379 (PF) httpConnInProgress,
380 (caddr_t) data);
381 return;
382 case EISCONN:
383 break; /* cool, we're connected */
384 default:
385 comm_close(fd);
b367f261 386 cached_error_entry(entry, ERR_CONNECT_FAIL, xstrerror());
090089c4 387 safe_free(data);
388 return;
389 }
390 /* Call the real write handler, now that we're fully connected */
391 comm_set_select_handler(fd, COMM_SELECT_WRITE,
392 (PF) httpSendRequest, (caddr_t) data);
393}
394
395int proxyhttpStart(e, url, entry)
396 edge *e;
397 char *url;
398 StoreEntry *entry;
399{
400
401 /* Create state structure. */
402 int sock, status;
403 HttpData *data = (HttpData *) xmalloc(sizeof(HttpData));
404
405 debug(3, "proxyhttpStart: <URL:%s>\n", url);
406 debug(10, "proxyhttpStart: HTTP request header:\n%s\n",
22e4fa85 407 entry->mem_obj->mime_hdr);
090089c4 408
409 memset(data, '\0', sizeof(HttpData));
410 data->entry = entry;
411
412 strncpy(data->request, url, sizeof(data->request) - 1);
413 data->type = HTTP_OPS[entry->type_id];
414 data->port = e->ascii_port;
22e4fa85 415 data->mime_hdr = entry->mem_obj->mime_hdr;
090089c4 416 strncpy(data->host, e->host, sizeof(data->host) - 1);
417
418 if (e->proxy_only)
419 storeStartDeleteBehind(entry);
420
421 /* Create socket. */
422 sock = comm_open(COMM_NONBLOCKING, 0, 0, url);
423 if (sock == COMM_ERROR) {
424 debug(4, "proxyhttpStart: Failed because we're out of sockets.\n");
b367f261 425 cached_error_entry(entry, ERR_NO_FDS, xstrerror());
090089c4 426 safe_free(data);
427 return COMM_ERROR;
428 }
429 /* check if IP is already in cache. It must be.
430 * It should be done before this route is called.
431 * Otherwise, we cannot check return code for connect. */
432 if (!ipcache_gethostbyname(data->host)) {
433 debug(4, "proxyhttpstart: Called without IP entry in ipcache. OR lookup failed.\n");
434 comm_close(sock);
b367f261 435 cached_error_entry(entry, ERR_DNS_FAIL, dns_error_message);
090089c4 436 safe_free(data);
437 return COMM_ERROR;
438 }
439 /* Open connection. */
440 if ((status = comm_connect(sock, data->host, data->port))) {
441 if (status != EINPROGRESS) {
442 comm_close(sock);
b367f261 443 cached_error_entry(entry, ERR_CONNECT_FAIL, xstrerror());
090089c4 444 safe_free(data);
445 e->last_fail_time = cached_curtime;
446 e->neighbor_up = 0;
447 return COMM_ERROR;
448 } else {
449 debug(5, "proxyhttpStart: FD %d: EINPROGRESS.\n", sock);
450 comm_set_select_handler(sock, COMM_SELECT_LIFETIME,
451 (PF) httpLifetimeExpire, (caddr_t) data);
452 comm_set_select_handler(sock, COMM_SELECT_WRITE,
453 (PF) httpConnInProgress, (caddr_t) data);
454 return COMM_OK;
455 }
456 }
457 /* Install connection complete handler. */
458 fd_note(sock, entry->url);
459 comm_set_select_handler(sock, COMM_SELECT_LIFETIME,
460 (PF) httpLifetimeExpire, (caddr_t) data);
461 comm_set_select_handler(sock, COMM_SELECT_WRITE,
462 (PF) httpSendRequest, (caddr_t) data);
463 return COMM_OK;
464
465}
466
467int httpStart(unusedfd, url, type, mime_hdr, entry)
468 int unusedfd;
469 char *url;
470 char *type;
471 char *mime_hdr;
472 StoreEntry *entry;
473{
474 /* Create state structure. */
475 int sock, status;
476 HttpData *data = (HttpData *) xmalloc(sizeof(HttpData));
477
478 debug(3, "httpStart: %s <URL:%s>\n", type, url);
479 debug(10, "httpStart: mime_hdr '%s'\n", mime_hdr);
480
481 memset(data, '\0', sizeof(HttpData));
482 data->entry = entry;
483 data->type = type;
484 data->mime_hdr = mime_hdr;
485
486 /* Parse url. */
487 if (http_url_parser(url, data->host, &data->port, data->request)) {
b367f261 488 cached_error_entry(entry, ERR_INVALID_URL, NULL);
090089c4 489 safe_free(data);
490 return COMM_ERROR;
491 }
492 /* Create socket. */
493 sock = comm_open(COMM_NONBLOCKING, 0, 0, url);
494 if (sock == COMM_ERROR) {
495 debug(4, "httpStart: Failed because we're out of sockets.\n");
b367f261 496 cached_error_entry(entry, ERR_NO_FDS, xstrerror());
090089c4 497 safe_free(data);
498 return COMM_ERROR;
499 }
500 /* check if IP is already in cache. It must be.
501 * It should be done before this route is called.
502 * Otherwise, we cannot check return code for connect. */
503 if (!ipcache_gethostbyname(data->host)) {
504 debug(4, "httpstart: Called without IP entry in ipcache. OR lookup failed.\n");
505 comm_close(sock);
b367f261 506 cached_error_entry(entry, ERR_DNS_FAIL, dns_error_message);
090089c4 507 safe_free(data);
508 return COMM_ERROR;
509 }
510 /* Open connection. */
511 if ((status = comm_connect(sock, data->host, data->port))) {
512 if (status != EINPROGRESS) {
513 comm_close(sock);
b367f261 514 cached_error_entry(entry, ERR_CONNECT_FAIL, xstrerror());
090089c4 515 safe_free(data);
516 return COMM_ERROR;
517 } else {
518 debug(5, "httpStart: FD %d: EINPROGRESS.\n", sock);
519 comm_set_select_handler(sock, COMM_SELECT_LIFETIME,
520 (PF) httpLifetimeExpire, (caddr_t) data);
521 comm_set_select_handler(sock, COMM_SELECT_WRITE,
522 (PF) httpConnInProgress, (caddr_t) data);
523 return COMM_OK;
524 }
525 }
526 /* Install connection complete handler. */
527 fd_note(sock, entry->url);
528 comm_set_select_handler(sock, COMM_SELECT_LIFETIME,
529 (PF) httpLifetimeExpire, (caddr_t) data);
530 comm_set_select_handler(sock, COMM_SELECT_WRITE,
531 (PF) httpSendRequest, (caddr_t) data);
532 return COMM_OK;
533}