]> git.ipfire.org Git - thirdparty/squid.git/blob - src/client_side.cc
2.3 branch merge
[thirdparty/squid.git] / src / client_side.cc
1
2 /*
3 * $Id: client_side.cc,v 1.461 1999/08/02 06:18:32 wessels Exp $
4 *
5 * DEBUG: section 33 Client-side Routines
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * Duane Wessels and the University of California San Diego. Please
16 * see the COPYRIGHT file for full details. Squid incorporates
17 * software developed and/or copyrighted by other sources. Please see
18 * 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 #if IPF_TRANSPARENT
39 #if HAVE_SYS_IOCTL_H
40 #include <sys/ioctl.h>
41 #endif
42 #include <netinet/tcp.h>
43 #include <net/if.h>
44 #include <ip_compat.h>
45 #include <ip_fil.h>
46 #include <ip_nat.h>
47 #endif
48
49
50
51 #if LINGERING_CLOSE
52 #define comm_close comm_lingering_close
53 #endif
54
55 static const char *const crlf = "\r\n";
56
57 #define REQUEST_BUF_SIZE 4096
58 #define FAILURE_MODE_TIME 300
59
60 /* Local functions */
61
62 static CWCB clientWriteComplete;
63 static PF clientReadRequest;
64 static PF connStateFree;
65 static PF requestTimeout;
66 static int clientCheckTransferDone(clientHttpRequest *);
67 static int clientGotNotEnough(clientHttpRequest *);
68 static void checkFailureRatio(err_type, hier_code);
69 static void clientProcessMiss(clientHttpRequest *);
70 static void clientBuildReplyHeader(clientHttpRequest * http, HttpReply * rep);
71 static clientHttpRequest *parseHttpRequestAbort(ConnStateData * conn, const char *uri);
72 static clientHttpRequest *parseHttpRequest(ConnStateData *, method_t *, int *, char **, size_t *);
73 static RH clientRedirectDone;
74 static STCB clientHandleIMSReply;
75 static int clientGetsOldEntry(StoreEntry * new, StoreEntry * old, request_t * request);
76 static int checkAccelOnly(clientHttpRequest *);
77 #if USE_IDENT
78 static IDCB clientIdentDone;
79 #endif
80 static int clientOnlyIfCached(clientHttpRequest * http);
81 static STCB clientSendMoreData;
82 static STCB clientCacheHit;
83 static void clientSetKeepaliveFlag(clientHttpRequest *);
84 static void clientPackRangeHdr(const HttpReply * rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb);
85 static void clientPackTermBound(String boundary, MemBuf * mb);
86 static void clientInterpretRequestHeaders(clientHttpRequest *);
87 static void clientProcessRequest(clientHttpRequest *);
88 static void clientProcessExpired(void *data);
89 static void clientProcessOnlyIfCachedMiss(clientHttpRequest * http);
90 static int clientCachable(clientHttpRequest * http);
91 static int clientHierarchical(clientHttpRequest * http);
92 static int clientCheckContentLength(request_t * r);
93 static int httpAcceptDefer(void);
94 static log_type clientProcessRequest2(clientHttpRequest * http);
95 static int clientReplyBodyTooLarge(int clen);
96
97 static int
98 checkAccelOnly(clientHttpRequest * http)
99 {
100 /* return TRUE if someone makes a proxy request to us and
101 * we are in httpd-accel only mode */
102 if (!Config2.Accel.on)
103 return 0;
104 if (Config.onoff.accel_with_proxy)
105 return 0;
106 if (http->request->protocol == PROTO_CACHEOBJ)
107 return 0;
108 if (http->flags.accel)
109 return 0;
110 return 1;
111 }
112
113 #if USE_IDENT
114 void
115 clientIdentDone(const char *ident, void *data)
116 {
117 ConnStateData *conn = data;
118 if (ident)
119 xstrncpy(conn->ident, ident, sizeof(conn->ident));
120 else
121 xstrncpy(conn->ident, "-", sizeof(conn->ident));
122 }
123 #endif
124
125 void
126 clientAccessCheck(void *data)
127 {
128 clientHttpRequest *http = data;
129 ConnStateData *conn = http->conn;
130 const char *browser;
131 if (checkAccelOnly(http)) {
132 clientAccessCheckDone(0, http);
133 return;
134 }
135 browser = httpHeaderGetStr(&http->request->header, HDR_USER_AGENT);
136 http->acl_checklist = aclChecklistCreate(Config.accessList.http,
137 http->request,
138 conn->peer.sin_addr,
139 conn->me.sin_addr,
140 browser,
141 conn->ident);
142 #if USE_IDENT
143 /*
144 * hack for ident ACL. It needs to get full addresses, and a
145 * place to store the ident result on persistent connections...
146 */
147 http->acl_checklist->conn = conn;
148 cbdataLock(http->acl_checklist->conn);
149 #endif
150 aclNBCheck(http->acl_checklist, clientAccessCheckDone, http);
151 }
152
153 /*
154 * returns true if client specified that the object must come from the cache
155 * witout contacting origin server
156 */
157 static int
158 clientOnlyIfCached(clientHttpRequest * http)
159 {
160 const request_t *r = http->request;
161 assert(r);
162 return r->cache_control &&
163 EBIT_TEST(r->cache_control->mask, CC_ONLY_IF_CACHED);
164 }
165
166 StoreEntry *
167 clientCreateStoreEntry(clientHttpRequest * h, method_t m, request_flags flags)
168 {
169 StoreEntry *e;
170 /*
171 * For erroneous requests, we might not have a h->request,
172 * so make a fake one.
173 */
174 if (h->request == NULL)
175 h->request = requestLink(requestCreate(m, PROTO_NONE, null_string));
176 e = storeCreateEntry(h->uri, h->log_uri, flags, m);
177 storeClientListAdd(e, h);
178 #if DELAY_POOLS
179 delaySetStoreClient(e, h, delayClient(h->request));
180 #endif
181 storeClientCopy(e, 0, 0, CLIENT_SOCK_SZ,
182 memAllocate(MEM_CLIENT_SOCK_BUF), clientSendMoreData, h);
183 return e;
184 }
185
186 void
187 clientAccessCheckDone(int answer, void *data)
188 {
189 clientHttpRequest *http = data;
190 int page_id = -1;
191 http_status status;
192 ErrorState *err = NULL;
193 debug(33, 2) ("The request %s %s is %s, because it matched '%s'\n",
194 RequestMethodStr[http->request->method], http->uri,
195 answer ? "ALLOWED" : "DENIED",
196 AclMatchedName ? AclMatchedName : "NO ACL's");
197 http->acl_checklist = NULL;
198 if (answer == ACCESS_ALLOWED) {
199 safe_free(http->uri);
200 http->uri = xstrdup(urlCanonical(http->request));
201 assert(http->redirect_state == REDIRECT_NONE);
202 http->redirect_state = REDIRECT_PENDING;
203 redirectStart(http, clientRedirectDone, http);
204 } else {
205 debug(33, 5) ("Access Denied: %s\n", http->uri);
206 debug(33, 5) ("AclMatchedName = %s\n",
207 AclMatchedName ? AclMatchedName : "<null>");
208 /*
209 * NOTE: get page_id here, based on AclMatchedName because
210 * if USE_DELAY_POOLS is enabled, then AclMatchedName gets
211 * clobbered in the clientCreateStoreEntry() call
212 * just below. Pedro Ribeiro <pribeiro@isel.pt>
213 */
214 page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName);
215 http->log_type = LOG_TCP_DENIED;
216 http->entry = clientCreateStoreEntry(http, http->request->method,
217 null_request_flags);
218 if (answer == ACCESS_REQ_PROXY_AUTH || aclIsProxyAuth(AclMatchedName)) {
219 if (!http->flags.accel) {
220 /* Proxy authorisation needed */
221 status = HTTP_PROXY_AUTHENTICATION_REQUIRED;
222 } else {
223 /* WWW authorisation needed */
224 status = HTTP_UNAUTHORIZED;
225 }
226 if (page_id <= 0)
227 page_id = ERR_CACHE_ACCESS_DENIED;
228 } else {
229 status = HTTP_FORBIDDEN;
230 if (page_id <= 0)
231 page_id = ERR_ACCESS_DENIED;
232 }
233 err = errorCon(page_id, status);
234 err->request = requestLink(http->request);
235 err->src_addr = http->conn->peer.sin_addr;
236 errorAppendEntry(http->entry, err);
237 }
238 }
239
240 static void
241 clientRedirectDone(void *data, char *result)
242 {
243 clientHttpRequest *http = data;
244 request_t *new_request = NULL;
245 request_t *old_request = http->request;
246 debug(33, 5) ("clientRedirectDone: '%s' result=%s\n", http->uri,
247 result ? result : "NULL");
248 assert(http->redirect_state == REDIRECT_PENDING);
249 http->redirect_state = REDIRECT_DONE;
250 if (result) {
251 http_status status = atoi(result);
252 if (status == 301 || status == 302) {
253 char *t = result;
254 if ((t = strchr(result, ':')) != NULL) {
255 http->redirect.status = status;
256 http->redirect.location = xstrdup(t + 1);
257 } else {
258 debug(33, 1) ("clientRedirectDone: bad input: %s\n", result);
259 }
260 }
261 if (strcmp(result, http->uri))
262 new_request = urlParse(old_request->method, result);
263 }
264 if (new_request) {
265 safe_free(http->uri);
266 http->uri = xstrdup(urlCanonical(new_request));
267 new_request->http_ver = old_request->http_ver;
268 httpHeaderAppend(&new_request->header, &old_request->header);
269 new_request->client_addr = old_request->client_addr;
270 new_request->my_addr = old_request->my_addr;
271 new_request->flags.redirected = 1;
272 if (old_request->body) {
273 new_request->body = xmalloc(old_request->body_sz);
274 xmemcpy(new_request->body, old_request->body, old_request->body_sz);
275 new_request->body_sz = old_request->body_sz;
276 }
277 requestUnlink(old_request);
278 http->request = requestLink(new_request);
279 }
280 clientInterpretRequestHeaders(http);
281 fd_note(http->conn->fd, http->uri);
282 clientProcessRequest(http);
283 }
284
285 static void
286 clientProcessExpired(void *data)
287 {
288 clientHttpRequest *http = data;
289 char *url = http->uri;
290 StoreEntry *entry = NULL;
291 debug(33, 3) ("clientProcessExpired: '%s'\n", http->uri);
292 assert(http->entry->lastmod >= 0);
293 /*
294 * check if we are allowed to contact other servers
295 * @?@: Instead of a 504 (Gateway Timeout) reply, we may want to return
296 * a stale entry *if* it matches client requirements
297 */
298 if (clientOnlyIfCached(http)) {
299 clientProcessOnlyIfCachedMiss(http);
300 return;
301 }
302 http->request->flags.refresh = 1;
303 http->old_entry = http->entry;
304 /*
305 * Assert that 'http' is already a client of old_entry. If
306 * it is not, then the beginning of the object data might get
307 * freed from memory before we need to access it.
308 */
309 assert(storeClientListSearch(http->old_entry->mem_obj, http));
310 entry = storeCreateEntry(url,
311 http->log_uri,
312 http->request->flags,
313 http->request->method);
314 /* NOTE, don't call storeLockObject(), storeCreateEntry() does it */
315 storeClientListAdd(entry, http);
316 #if DELAY_POOLS
317 /* delay_id is already set on original store client */
318 delaySetStoreClient(entry, http, delayClient(http->request));
319 #endif
320 entry->lastmod = http->old_entry->lastmod;
321 debug(33, 5) ("clientProcessExpired: lastmod %d\n", (int) entry->lastmod);
322 entry->refcount++; /* EXPIRED CASE */
323 #if HEAP_REPLACEMENT
324 storeHeapPositionUpdate(entry);
325 #endif
326 http->entry = entry;
327 http->out.offset = 0;
328 fwdStart(http->conn->fd, http->entry, http->request,
329 http->conn->peer.sin_addr, http->conn->me.sin_addr);
330 /* Register with storage manager to receive updates when data comes in. */
331 if (EBIT_TEST(entry->flags, ENTRY_ABORTED))
332 debug(33, 0) ("clientProcessExpired: found ENTRY_ABORTED object\n");
333 storeClientCopy(entry,
334 http->out.offset,
335 http->out.offset,
336 CLIENT_SOCK_SZ,
337 memAllocate(MEM_CLIENT_SOCK_BUF),
338 clientHandleIMSReply,
339 http);
340 }
341
342 static int
343 clientGetsOldEntry(StoreEntry * new_entry, StoreEntry * old_entry, request_t * request)
344 {
345 const http_status status = new_entry->mem_obj->reply->sline.status;
346 if (0 == status) {
347 debug(33, 5) ("clientGetsOldEntry: YES, broken HTTP reply\n");
348 return 1;
349 }
350 /* If the reply is a failure then send the old object as a last
351 * resort */
352 if (status >= 500 && status < 600) {
353 debug(33, 3) ("clientGetsOldEntry: YES, failure reply=%d\n", status);
354 return 1;
355 }
356 /* If the reply is anything but "Not Modified" then
357 * we must forward it to the client */
358 if (HTTP_NOT_MODIFIED != status) {
359 debug(33, 5) ("clientGetsOldEntry: NO, reply=%d\n", status);
360 return 0;
361 }
362 /* If the client did not send IMS in the request, then it
363 * must get the old object, not this "Not Modified" reply */
364 if (!request->flags.ims) {
365 debug(33, 5) ("clientGetsOldEntry: YES, no client IMS\n");
366 return 1;
367 }
368 /* If the client IMS time is prior to the entry LASTMOD time we
369 * need to send the old object */
370 if (modifiedSince(old_entry, request)) {
371 debug(33, 5) ("clientGetsOldEntry: YES, modified since %d\n",
372 (int) request->ims);
373 return 1;
374 }
375 debug(33, 5) ("clientGetsOldEntry: NO, new one is fine\n");
376 return 0;
377 }
378
379
380 static void
381 clientHandleIMSReply(void *data, char *buf, ssize_t size)
382 {
383 clientHttpRequest *http = data;
384 StoreEntry *entry = http->entry;
385 MemObject *mem;
386 const char *url = storeUrl(entry);
387 int unlink_request = 0;
388 StoreEntry *oldentry;
389 int recopy = 1;
390 http_status status;
391 debug(33, 3) ("clientHandleIMSReply: %s, %d bytes\n", url, (int) size);
392 if (entry == NULL) {
393 memFree(buf, MEM_CLIENT_SOCK_BUF);
394 return;
395 }
396 if (size < 0 && !EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
397 memFree(buf, MEM_CLIENT_SOCK_BUF);
398 return;
399 }
400 mem = entry->mem_obj;
401 status = mem->reply->sline.status;
402 if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
403 debug(33, 3) ("clientHandleIMSReply: ABORTED '%s'\n", url);
404 /* We have an existing entry, but failed to validate it */
405 /* Its okay to send the old one anyway */
406 http->log_type = LOG_TCP_REFRESH_FAIL_HIT;
407 storeUnregister(entry, http);
408 storeUnlockObject(entry);
409 entry = http->entry = http->old_entry;
410 entry->refcount++;
411 #if HEAP_REPLACEMENT
412 storeHeapPositionUpdate(entry);
413 #endif
414 } else if (STORE_PENDING == entry->store_status && 0 == status) {
415 debug(33, 3) ("clientHandleIMSReply: Incomplete headers for '%s'\n", url);
416 if (size >= CLIENT_SOCK_SZ) {
417 /* will not get any bigger than that */
418 debug(33, 3) ("clientHandleIMSReply: Reply is too large '%s', using old entry\n", url);
419 /* use old entry, this repeats the code abovez */
420 http->log_type = LOG_TCP_REFRESH_FAIL_HIT;
421 storeUnregister(entry, http);
422 storeUnlockObject(entry);
423 entry = http->entry = http->old_entry;
424 entry->refcount++;
425 #if HEAP_REPLACEMENT
426 storeHeapPositionUpdate(entry);
427 #endif
428 /* continue */
429 } else {
430 storeClientCopy(entry,
431 http->out.offset + size,
432 http->out.offset,
433 CLIENT_SOCK_SZ,
434 buf,
435 clientHandleIMSReply,
436 http);
437 return;
438 }
439 } else if (clientGetsOldEntry(entry, http->old_entry, http->request)) {
440 /* We initiated the IMS request, the client is not expecting
441 * 304, so put the good one back. First, make sure the old entry
442 * headers have been loaded from disk. */
443 oldentry = http->old_entry;
444 http->log_type = LOG_TCP_REFRESH_HIT;
445 if (oldentry->mem_obj->request == NULL) {
446 oldentry->mem_obj->request = requestLink(mem->request);
447 unlink_request = 1;
448 }
449 /* Don't memcpy() the whole reply structure here. For example,
450 * www.thegist.com (Netscape/1.13) returns a content-length for
451 * 304's which seems to be the length of the 304 HEADERS!!! and
452 * not the body they refer to. */
453 httpReplyUpdateOnNotModified(oldentry->mem_obj->reply, mem->reply);
454 storeTimestampsSet(oldentry);
455 storeUnregister(entry, http);
456 storeUnlockObject(entry);
457 entry = http->entry = oldentry;
458 entry->timestamp = squid_curtime;
459 if (unlink_request) {
460 requestUnlink(entry->mem_obj->request);
461 entry->mem_obj->request = NULL;
462 }
463 } else {
464 /* the client can handle this reply, whatever it is */
465 http->log_type = LOG_TCP_REFRESH_MISS;
466 if (HTTP_NOT_MODIFIED == mem->reply->sline.status) {
467 httpReplyUpdateOnNotModified(http->old_entry->mem_obj->reply,
468 mem->reply);
469 storeTimestampsSet(http->old_entry);
470 http->old_entry->refcount++;
471 #if HEAP_REPLACEMENT
472 storeHeapPositionUpdate(http->old_entry);
473 #endif
474 http->log_type = LOG_TCP_REFRESH_HIT;
475 }
476 storeUnregister(http->old_entry, http);
477 storeUnlockObject(http->old_entry);
478 recopy = 0;
479 }
480 http->old_entry = NULL; /* done with old_entry */
481 assert(!EBIT_TEST(entry->flags, ENTRY_ABORTED));
482 if (recopy) {
483 storeClientCopy(entry,
484 http->out.offset,
485 http->out.offset,
486 CLIENT_SOCK_SZ,
487 buf,
488 clientSendMoreData,
489 http);
490 } else {
491 clientSendMoreData(data, buf, size);
492 }
493 }
494
495 int
496 modifiedSince(StoreEntry * entry, request_t * request)
497 {
498 int object_length;
499 MemObject *mem = entry->mem_obj;
500 time_t mod_time = entry->lastmod;
501 debug(33, 3) ("modifiedSince: '%s'\n", storeUrl(entry));
502 if (mod_time < 0)
503 mod_time = entry->timestamp;
504 debug(33, 3) ("modifiedSince: mod_time = %d\n", (int) mod_time);
505 if (mod_time < 0)
506 return 1;
507 /* Find size of the object */
508 object_length = mem->reply->content_length;
509 if (object_length < 0)
510 object_length = contentLen(entry);
511 if (mod_time > request->ims) {
512 debug(33, 3) ("--> YES: entry newer than client\n");
513 return 1;
514 } else if (mod_time < request->ims) {
515 debug(33, 3) ("--> NO: entry older than client\n");
516 return 0;
517 } else if (request->imslen < 0) {
518 debug(33, 3) ("--> NO: same LMT, no client length\n");
519 return 0;
520 } else if (request->imslen == object_length) {
521 debug(33, 3) ("--> NO: same LMT, same length\n");
522 return 0;
523 } else {
524 debug(33, 3) ("--> YES: same LMT, different length\n");
525 return 1;
526 }
527 }
528
529 void
530 clientPurgeRequest(clientHttpRequest * http)
531 {
532 StoreEntry *entry;
533 ErrorState *err = NULL;
534 HttpReply *r;
535 debug(33, 3) ("Config.onoff.enable_purge = %d\n", Config.onoff.enable_purge);
536 if (!Config.onoff.enable_purge) {
537 http->log_type = LOG_TCP_DENIED;
538 err = errorCon(ERR_ACCESS_DENIED, HTTP_FORBIDDEN);
539 err->request = requestLink(http->request);
540 err->src_addr = http->conn->peer.sin_addr;
541 http->entry = clientCreateStoreEntry(http, http->request->method, null_request_flags);
542 errorAppendEntry(http->entry, err);
543 return;
544 }
545 http->log_type = LOG_TCP_MISS;
546 if ((entry = storeGetPublic(http->uri, METHOD_GET)) == NULL) {
547 http->http_code = HTTP_NOT_FOUND;
548 } else {
549 storeRelease(entry);
550 http->http_code = HTTP_OK;
551 }
552 debug(33, 4) ("clientPurgeRequest: Not modified '%s'\n",
553 storeUrl(entry));
554 /*
555 * Make a new entry to hold the reply to be written
556 * to the client.
557 */
558 http->entry = clientCreateStoreEntry(http, http->request->method, null_request_flags);
559 httpReplyReset(r = http->entry->mem_obj->reply);
560 httpReplySetHeaders(r, 1.0, http->http_code, NULL, NULL, 0, 0, -1);
561 httpReplySwapOut(r, http->entry);
562 storeComplete(http->entry);
563 }
564
565 int
566 checkNegativeHit(StoreEntry * e)
567 {
568 if (!EBIT_TEST(e->flags, ENTRY_NEGCACHED))
569 return 0;
570 if (e->expires <= squid_curtime)
571 return 0;
572 if (e->store_status != STORE_OK)
573 return 0;
574 return 1;
575 }
576
577 void
578 clientUpdateCounters(clientHttpRequest * http)
579 {
580 int svc_time = tvSubMsec(http->start, current_time);
581 ping_data *i;
582 HierarchyLogEntry *H;
583 Counter.client_http.requests++;
584 if (isTcpHit(http->log_type))
585 Counter.client_http.hits++;
586 if (http->request->err_type != ERR_NONE)
587 Counter.client_http.errors++;
588 statHistCount(&Counter.client_http.all_svc_time, svc_time);
589 /*
590 * The idea here is not to be complete, but to get service times
591 * for only well-defined types. For example, we don't include
592 * LOG_TCP_REFRESH_FAIL_HIT because its not really a cache hit
593 * (we *tried* to validate it, but failed).
594 */
595 switch (http->log_type) {
596 case LOG_TCP_REFRESH_HIT:
597 statHistCount(&Counter.client_http.nh_svc_time, svc_time);
598 break;
599 case LOG_TCP_IMS_HIT:
600 statHistCount(&Counter.client_http.nm_svc_time, svc_time);
601 break;
602 case LOG_TCP_HIT:
603 case LOG_TCP_MEM_HIT:
604 case LOG_TCP_OFFLINE_HIT:
605 statHistCount(&Counter.client_http.hit_svc_time, svc_time);
606 break;
607 case LOG_TCP_MISS:
608 case LOG_TCP_CLIENT_REFRESH_MISS:
609 statHistCount(&Counter.client_http.miss_svc_time, svc_time);
610 break;
611 default:
612 /* make compiler warnings go away */
613 break;
614 }
615 H = &http->request->hier;
616 switch (H->alg) {
617 case PEER_SA_DIGEST:
618 Counter.cd.times_used++;
619 break;
620 case PEER_SA_ICP:
621 Counter.icp.times_used++;
622 i = &H->ping;
623 if (0 != i->stop.tv_sec && 0 != i->start.tv_sec)
624 statHistCount(&Counter.icp.query_svc_time,
625 tvSubUsec(i->start, i->stop));
626 if (i->timeout)
627 Counter.icp.query_timeouts++;
628 break;
629 case PEER_SA_NETDB:
630 Counter.netdb.times_used++;
631 break;
632 default:
633 break;
634 }
635 }
636
637 static void
638 httpRequestFree(void *data)
639 {
640 clientHttpRequest *http = data;
641 clientHttpRequest **H;
642 ConnStateData *conn = http->conn;
643 StoreEntry *e;
644 request_t *request = http->request;
645 MemObject *mem = NULL;
646 debug(33, 3) ("httpRequestFree: %s\n", storeUrl(http->entry));
647 if (!clientCheckTransferDone(http)) {
648 if ((e = http->entry)) {
649 http->entry = NULL;
650 storeUnregister(e, http);
651 storeUnlockObject(e);
652 }
653 if (http->entry && http->entry->ping_status == PING_WAITING)
654 storeReleaseRequest(http->entry);
655 }
656 assert(http->log_type < LOG_TYPE_MAX);
657 if (http->entry)
658 mem = http->entry->mem_obj;
659 if (http->out.size || http->log_type) {
660 http->al.icp.opcode = ICP_INVALID;
661 http->al.url = http->log_uri;
662 debug(33, 9) ("httpRequestFree: al.url='%s'\n", http->al.url);
663 if (mem) {
664 http->al.http.code = mem->reply->sline.status;
665 http->al.http.content_type = strBuf(mem->reply->content_type);
666 }
667 http->al.cache.caddr = conn->log_addr;
668 http->al.cache.size = http->out.size;
669 http->al.cache.code = http->log_type;
670 http->al.cache.msec = tvSubMsec(http->start, current_time);
671 if (request) {
672 Packer p;
673 MemBuf mb;
674 memBufDefInit(&mb);
675 packerToMemInit(&p, &mb);
676 httpHeaderPackInto(&request->header, &p);
677 http->al.http.method = request->method;
678 http->al.http.version = request->http_ver;
679 http->al.headers.request = xstrdup(mb.buf);
680 http->al.hier = request->hier;
681 if (request->user_ident[0])
682 http->al.cache.ident = request->user_ident;
683 else
684 http->al.cache.ident = conn->ident;
685 packerClean(&p);
686 memBufClean(&mb);
687 }
688 accessLogLog(&http->al);
689 clientUpdateCounters(http);
690 clientdbUpdate(conn->peer.sin_addr, http->log_type, PROTO_HTTP, http->out.size);
691 }
692 if (http->acl_checklist)
693 aclChecklistFree(http->acl_checklist);
694 if (request)
695 checkFailureRatio(request->err_type, http->al.hier.code);
696 safe_free(http->uri);
697 safe_free(http->log_uri);
698 safe_free(http->al.headers.request);
699 safe_free(http->al.headers.reply);
700 safe_free(http->redirect.location);
701 stringClean(&http->range_iter.boundary);
702 if ((e = http->entry)) {
703 http->entry = NULL;
704 storeUnregister(e, http);
705 storeUnlockObject(e);
706 }
707 /* old_entry might still be set if we didn't yet get the reply
708 * code in clientHandleIMSReply() */
709 if ((e = http->old_entry)) {
710 http->old_entry = NULL;
711 storeUnregister(e, http);
712 storeUnlockObject(e);
713 }
714 requestUnlink(http->request);
715 assert(http != http->next);
716 assert(http->conn->chr != NULL);
717 H = &http->conn->chr;
718 while (*H) {
719 if (*H == http)
720 break;
721 H = &(*H)->next;
722 }
723 assert(*H != NULL);
724 *H = http->next;
725 http->next = NULL;
726 dlinkDelete(&http->active, &ClientActiveRequests);
727 cbdataFree(http);
728 }
729
730 /* This is a handler normally called by comm_close() */
731 static void
732 connStateFree(int fd, void *data)
733 {
734 ConnStateData *connState = data;
735 clientHttpRequest *http;
736 debug(33, 3) ("connStateFree: FD %d\n", fd);
737 assert(connState != NULL);
738 while ((http = connState->chr) != NULL) {
739 assert(http->conn == connState);
740 assert(connState->chr != connState->chr->next);
741 httpRequestFree(http);
742 }
743 safe_free(connState->in.buf);
744 /* XXX account connState->in.buf */
745 pconnHistCount(0, connState->nrequests);
746 cbdataFree(connState);
747 #ifdef _SQUID_LINUX_
748 /* prevent those nasty RST packets */
749 {
750 char buf[SQUID_TCP_SO_RCVBUF];
751 while (read(fd, buf, SQUID_TCP_SO_RCVBUF) > 0);
752 }
753 #endif
754 }
755
756 static void
757 clientInterpretRequestHeaders(clientHttpRequest * http)
758 {
759 request_t *request = http->request;
760 const HttpHeader *req_hdr = &request->header;
761 int no_cache = 0;
762 #if USE_USERAGENT_LOG
763 const char *str;
764 #endif
765 request->imslen = -1;
766 request->ims = httpHeaderGetTime(req_hdr, HDR_IF_MODIFIED_SINCE);
767 if (request->ims > 0)
768 request->flags.ims = 1;
769 if (httpHeaderHas(req_hdr, HDR_PRAGMA)) {
770 String s = httpHeaderGetList(req_hdr, HDR_PRAGMA);
771 if (strListIsMember(&s, "no-cache", ','))
772 no_cache++;
773 stringClean(&s);
774 }
775 if (request->cache_control)
776 if (EBIT_TEST(request->cache_control->mask, CC_NO_CACHE))
777 no_cache++;
778 if (no_cache) {
779 #if HTTP_VIOLATIONS
780 if (Config.onoff.reload_into_ims)
781 request->flags.nocache_hack = 1;
782 else if (refresh_nocache_hack)
783 request->flags.nocache_hack = 1;
784 else
785 #endif
786 request->flags.nocache = 1;
787 }
788 /* ignore range header in non-GETs */
789 if (request->method == METHOD_GET) {
790 request->range = httpHeaderGetRange(req_hdr);
791 if (request->range)
792 request->flags.range = 1;
793 }
794 if (httpHeaderHas(req_hdr, HDR_AUTHORIZATION))
795 request->flags.auth = 1;
796 if (request->login[0] != '\0')
797 request->flags.auth = 1;
798 if (httpHeaderHas(req_hdr, HDR_VIA)) {
799 String s = httpHeaderGetList(req_hdr, HDR_VIA);
800 /*
801 * ThisCache cannot be a member of Via header, "1.0 ThisCache" can.
802 * Note ThisCache2 has a space prepended to the hostname so we don't
803 * accidentally match super-domains.
804 */
805 if (strListIsSubstr(&s, ThisCache2, ',')) {
806 debugObj(33, 1, "WARNING: Forwarding loop detected for:\n",
807 request, (ObjPackMethod) & httpRequestPack);
808 request->flags.loopdetect = 1;
809 }
810 #if FORW_VIA_DB
811 fvdbCountVia(strBuf(s));
812 #endif
813 stringClean(&s);
814 }
815 #if USE_USERAGENT_LOG
816 if ((str = httpHeaderGetStr(req_hdr, HDR_USER_AGENT)))
817 logUserAgent(fqdnFromAddr(http->conn->peer.sin_addr), str);
818 #endif
819 #if FORW_VIA_DB
820 if (httpHeaderHas(req_hdr, HDR_X_FORWARDED_FOR)) {
821 String s = httpHeaderGetList(req_hdr, HDR_X_FORWARDED_FOR);
822 fvdbCountForw(strBuf(s));
823 stringClean(&s);
824 }
825 #endif
826 request->cache_control = httpHeaderGetCc(req_hdr);
827 if (request->method == METHOD_TRACE) {
828 request->max_forwards = httpHeaderGetInt(req_hdr, HDR_MAX_FORWARDS);
829 }
830 if (clientCachable(http))
831 request->flags.cachable = 1;
832 if (clientHierarchical(http))
833 request->flags.hierarchical = 1;
834 debug(33, 5) ("clientInterpretRequestHeaders: REQ_NOCACHE = %s\n",
835 request->flags.nocache ? "SET" : "NOT SET");
836 debug(33, 5) ("clientInterpretRequestHeaders: REQ_CACHABLE = %s\n",
837 request->flags.cachable ? "SET" : "NOT SET");
838 debug(33, 5) ("clientInterpretRequestHeaders: REQ_HIERARCHICAL = %s\n",
839 request->flags.hierarchical ? "SET" : "NOT SET");
840 }
841
842 /*
843 * clientSetKeepaliveFlag() sets request->flags.proxy_keepalive.
844 * This is the client-side persistent connection flag. We need
845 * to set this relatively early in the request processing
846 * to handle hacks for broken servers and clients.
847 */
848 static void
849 clientSetKeepaliveFlag(clientHttpRequest * http)
850 {
851 request_t *request = http->request;
852 const HttpHeader *req_hdr = &request->header;
853 debug(33, 3) ("clientSetKeepaliveFlag: http_ver = %3.1f\n",
854 request->http_ver);
855 debug(33, 3) ("clientSetKeepaliveFlag: method = %s\n",
856 RequestMethodStr[request->method]);
857 if (httpMsgIsPersistent(request->http_ver, req_hdr))
858 request->flags.proxy_keepalive = 1;
859 }
860
861 static int
862 clientCheckContentLength(request_t * r)
863 {
864 int has_cont_len = (r->content_length >= 0);
865 switch (r->method) {
866 case METHOD_PUT:
867 case METHOD_POST:
868 /* PUT/POST requires a request entity */
869 return has_cont_len;
870 case METHOD_GET:
871 case METHOD_HEAD:
872 /* We do not want to see a request entity on GET/HEAD requests */
873 return !has_cont_len;
874 default:
875 /* For other types of requests we don't care */
876 return 1;
877 }
878 /* NOT REACHED */
879 }
880
881 static int
882 clientCachable(clientHttpRequest * http)
883 {
884 const char *url = http->uri;
885 request_t *req = http->request;
886 method_t method = req->method;
887 aclCheck_t ch;
888 memset(&ch, '\0', sizeof(ch));
889 /*
890 * Hopefully, nobody really wants 'no_cache' by client's IP
891 * address, but if they do, this should work if they use IP
892 * addresses in their ACLs, or if the client's address is in
893 * the FQDN cache.
894 *
895 * This may not work yet for 'dst' and 'dst_domain' ACLs.
896 */
897 ch.src_addr = http->conn->peer.sin_addr;
898 ch.my_addr = http->conn->me.sin_addr;
899 ch.request = http->request;
900 /*
901 * aclCheckFast returns 1 for ALLOW and 0 for DENY. The default
902 * is ALLOW, so we require 'no_cache DENY foo' in squid.conf
903 * to indicate uncachable objects.
904 */
905 if (!aclCheckFast(Config.accessList.noCache, &ch))
906 return 0;
907 if (req->protocol == PROTO_HTTP)
908 return httpCachable(method);
909 /* FTP is always cachable */
910 if (req->protocol == PROTO_GOPHER)
911 return gopherCachable(url);
912 if (req->protocol == PROTO_WAIS)
913 return 0;
914 if (method == METHOD_CONNECT)
915 return 0;
916 if (method == METHOD_TRACE)
917 return 0;
918 if (req->protocol == PROTO_CACHEOBJ)
919 return 0;
920 return 1;
921 }
922
923 /* Return true if we can query our neighbors for this object */
924 static int
925 clientHierarchical(clientHttpRequest * http)
926 {
927 const char *url = http->uri;
928 request_t *request = http->request;
929 method_t method = request->method;
930 const wordlist *p = NULL;
931
932 /* IMS needs a private key, so we can use the hierarchy for IMS only
933 * if our neighbors support private keys */
934 if (request->flags.ims && !neighbors_do_private_keys)
935 return 0;
936 if (request->flags.auth)
937 return 0;
938 if (method == METHOD_TRACE)
939 return 1;
940 if (method != METHOD_GET)
941 return 0;
942 /* scan hierarchy_stoplist */
943 for (p = Config.hierarchy_stoplist; p; p = p->next)
944 if (strstr(url, p->key))
945 return 0;
946 if (request->flags.loopdetect)
947 return 0;
948 if (request->protocol == PROTO_HTTP)
949 return httpCachable(method);
950 if (request->protocol == PROTO_GOPHER)
951 return gopherCachable(url);
952 if (request->protocol == PROTO_WAIS)
953 return 0;
954 if (request->protocol == PROTO_CACHEOBJ)
955 return 0;
956 return 1;
957 }
958
959 int
960 isTcpHit(log_type code)
961 {
962 /* this should be a bitmap for better optimization */
963 if (code == LOG_TCP_HIT)
964 return 1;
965 if (code == LOG_TCP_IMS_HIT)
966 return 1;
967 if (code == LOG_TCP_REFRESH_FAIL_HIT)
968 return 1;
969 if (code == LOG_TCP_REFRESH_HIT)
970 return 1;
971 if (code == LOG_TCP_NEGATIVE_HIT)
972 return 1;
973 if (code == LOG_TCP_MEM_HIT)
974 return 1;
975 if (code == LOG_TCP_OFFLINE_HIT)
976 return 1;
977 return 0;
978 }
979
980 /*
981 * returns true if If-Range specs match reply, false otherwise
982 */
983 static int
984 clientIfRangeMatch(clientHttpRequest * http, HttpReply * rep)
985 {
986 const TimeOrTag spec = httpHeaderGetTimeOrTag(&http->request->header, HDR_IF_RANGE);
987 /* check for parsing falure */
988 if (!spec.valid)
989 return 0;
990 /* got an ETag? */
991 if (spec.tag.str) {
992 ETag rep_tag = httpHeaderGetETag(&rep->header, HDR_ETAG);
993 debug(33, 3) ("clientIfRangeMatch: ETags: %s and %s\n",
994 spec.tag.str, rep_tag.str ? rep_tag.str : "<none>");
995 if (!rep_tag.str)
996 return 0; /* entity has no etag to compare with! */
997 if (spec.tag.weak || rep_tag.weak) {
998 debug(33, 1) ("clientIfRangeMatch: Weak ETags are not allowed in If-Range: %s ? %s\n",
999 spec.tag.str, rep_tag.str);
1000 return 0; /* must use strong validator for sub-range requests */
1001 }
1002 return etagIsEqual(&rep_tag, &spec.tag);
1003 }
1004 /* got modification time? */
1005 if (spec.time >= 0) {
1006 return http->entry->lastmod <= spec.time;
1007 }
1008 assert(0); /* should not happen */
1009 return 0;
1010 }
1011
1012 /* returns expected content length for multi-range replies
1013 * note: assumes that httpHdrRangeCanonize has already been called
1014 * warning: assumes that HTTP headers for individual ranges at the
1015 * time of the actuall assembly will be exactly the same as
1016 * the headers when clientMRangeCLen() is called */
1017 static int
1018 clientMRangeCLen(clientHttpRequest * http)
1019 {
1020 int clen = 0;
1021 HttpHdrRangePos pos = HttpHdrRangeInitPos;
1022 const HttpHdrRangeSpec *spec;
1023 MemBuf mb;
1024
1025 assert(http->entry->mem_obj);
1026
1027 memBufDefInit(&mb);
1028 while ((spec = httpHdrRangeGetSpec(http->request->range, &pos))) {
1029
1030 /* account for headers for this range */
1031 memBufReset(&mb);
1032 clientPackRangeHdr(http->entry->mem_obj->reply,
1033 spec, http->range_iter.boundary, &mb);
1034 clen += mb.size;
1035
1036 /* account for range content */
1037 clen += spec->length;
1038
1039 debug(33, 6) ("clientMRangeCLen: (clen += %d + %d) == %d\n",
1040 mb.size, spec->length, clen);
1041 }
1042 /* account for the terminating boundary */
1043 memBufReset(&mb);
1044 clientPackTermBound(http->range_iter.boundary, &mb);
1045 clen += mb.size;
1046
1047 memBufClean(&mb);
1048 return clen;
1049 }
1050
1051 /* adds appropriate Range headers if needed */
1052 static void
1053 clientBuildRangeHeader(clientHttpRequest * http, HttpReply * rep)
1054 {
1055 HttpHeader *hdr = rep ? &rep->header : 0;
1056 const char *range_err = NULL;
1057 assert(http->request->range);
1058 /* check if we still want to do ranges */
1059 if (!rep)
1060 range_err = "no [parse-able] reply";
1061 else if (rep->sline.status != HTTP_OK)
1062 range_err = "wrong status code";
1063 else if (httpHeaderHas(hdr, HDR_CONTENT_RANGE))
1064 range_err = "origin server does ranges";
1065 else if (rep->content_length < 0)
1066 range_err = "unknown length";
1067 else if (rep->content_length != http->entry->mem_obj->reply->content_length)
1068 range_err = "INCONSISTENT length"; /* a bug? */
1069 else if (httpHeaderHas(&http->request->header, HDR_IF_RANGE) && !clientIfRangeMatch(http, rep))
1070 range_err = "If-Range match failed";
1071 else if (!httpHdrRangeCanonize(http->request->range, rep->content_length))
1072 range_err = "canonization failed";
1073 else if (httpHdrRangeIsComplex(http->request->range))
1074 range_err = "too complex range header";
1075 /* get rid of our range specs on error */
1076 if (range_err) {
1077 debug(33, 3) ("clientBuildRangeHeader: will not do ranges: %s.\n", range_err);
1078 httpHdrRangeDestroy(http->request->range);
1079 http->request->range = NULL;
1080 } else {
1081 const int spec_count = http->request->range->specs.count;
1082 int actual_clen = -1;
1083
1084 debug(33, 3) ("clientBuildRangeHeader: range spec count: %d virgin clen: %d\n",
1085 spec_count, rep->content_length);
1086 assert(spec_count > 0);
1087 /* ETags should not be returned with Partial Content replies? */
1088 httpHeaderDelById(hdr, HDR_ETAG);
1089 /* append appropriate header(s) */
1090 if (spec_count == 1) {
1091 HttpHdrRangePos pos = HttpHdrRangeInitPos;
1092 const HttpHdrRangeSpec *spec = httpHdrRangeGetSpec(http->request->range, &pos);
1093 assert(spec);
1094 /* append Content-Range */
1095 httpHeaderAddContRange(hdr, *spec, rep->content_length);
1096 /* set new Content-Length to the actual number of bytes
1097 * transmitted in the message-body */
1098 actual_clen = spec->length;
1099 } else {
1100 /* multipart! */
1101 /* generate boundary string */
1102 http->range_iter.boundary = httpHdrRangeBoundaryStr(http);
1103 /* delete old Content-Type, add ours */
1104 httpHeaderDelById(hdr, HDR_CONTENT_TYPE);
1105 httpHeaderPutStrf(hdr, HDR_CONTENT_TYPE,
1106 "multipart/byteranges; boundary=\"%s\"",
1107 strBuf(http->range_iter.boundary));
1108 /* Content-Length is not required in multipart responses
1109 * but it is always nice to have one */
1110 actual_clen = clientMRangeCLen(http);
1111 }
1112
1113 /* replace Content-Length header */
1114 assert(actual_clen >= 0);
1115 httpHeaderDelById(hdr, HDR_CONTENT_LENGTH);
1116 httpHeaderPutInt(hdr, HDR_CONTENT_LENGTH, actual_clen);
1117 debug(33, 3) ("clientBuildRangeHeader: actual content length: %d\n", actual_clen);
1118 }
1119 }
1120
1121 /*
1122 * filters out unwanted entries from original reply header
1123 * adds extra entries if we have more info than origin server
1124 * adds Squid specific entries
1125 */
1126 static void
1127 clientBuildReplyHeader(clientHttpRequest * http, HttpReply * rep)
1128 {
1129 HttpHeader *hdr = &rep->header;
1130 int is_hit = isTcpHit(http->log_type);
1131 request_t *request = http->request;
1132 #if DONT_FILTER_THESE
1133 /* but you might want to if you run Squid as an HTTP accelerator */
1134 /* httpHeaderDelById(hdr, HDR_ACCEPT_RANGES); */
1135 httpHeaderDelById(hdr, HDR_ETAG);
1136 #endif
1137 httpHeaderDelById(hdr, HDR_PROXY_CONNECTION);
1138 /* here: Keep-Alive is a field-name, not a connection directive! */
1139 httpHeaderDelByName(hdr, "Keep-Alive");
1140 /* remove Set-Cookie if a hit */
1141 if (is_hit)
1142 httpHeaderDelById(hdr, HDR_SET_COOKIE);
1143 /* handle Connection header */
1144 if (httpHeaderHas(hdr, HDR_CONNECTION)) {
1145 /* anything that matches Connection list member will be deleted */
1146 String strConnection = httpHeaderGetList(hdr, HDR_CONNECTION);
1147 const HttpHeaderEntry *e;
1148 HttpHeaderPos pos = HttpHeaderInitPos;
1149 /*
1150 * think: on-average-best nesting of the two loops (hdrEntry
1151 * and strListItem) @?@
1152 */
1153 /*
1154 * maybe we should delete standard stuff ("keep-alive","close")
1155 * from strConnection first?
1156 */
1157 while ((e = httpHeaderGetEntry(hdr, &pos))) {
1158 if (strListIsMember(&strConnection, strBuf(e->name), ','))
1159 httpHeaderDelAt(hdr, pos);
1160 }
1161 httpHeaderDelById(hdr, HDR_CONNECTION);
1162 stringClean(&strConnection);
1163 }
1164 /* Handle Ranges */
1165 if (request->range)
1166 clientBuildRangeHeader(http, rep);
1167 /*
1168 * Add Age header, not that our header must replace Age headers
1169 * from other caches if any
1170 */
1171 if (http->entry->timestamp > 0) {
1172 httpHeaderDelById(hdr, HDR_AGE);
1173 /*
1174 * we do not follow HTTP/1.1 precisely here becuase we rely
1175 * on Date header when computing entry->timestamp; we should
1176 * be using _request_ time if Date header is not available
1177 * or if it is out of sync
1178 */
1179 httpHeaderPutInt(hdr, HDR_AGE,
1180 http->entry->timestamp <= squid_curtime ?
1181 squid_curtime - http->entry->timestamp : 0);
1182 }
1183 /* Append X-Cache */
1184 httpHeaderPutStrf(hdr, HDR_X_CACHE, "%s from %s",
1185 is_hit ? "HIT" : "MISS", getMyHostname());
1186 #if USE_CACHE_DIGESTS
1187 /* Append X-Cache-Lookup: -- temporary hack, to be removed @?@ @?@ */
1188 httpHeaderPutStrf(hdr, HDR_X_CACHE_LOOKUP, "%s from %s:%d",
1189 http->lookup_type ? http->lookup_type : "NONE",
1190 getMyHostname(), Config.Port.http->i);
1191 #endif
1192 if (httpReplyBodySize(request->method, http->entry->mem_obj->reply) < 0) {
1193 debug(33, 3) ("clientBuildReplyHeader: can't keep-alive, unknown body size\n");
1194 request->flags.proxy_keepalive = 0;
1195 }
1196 /* Signal keep-alive if needed */
1197 httpHeaderPutStr(hdr,
1198 http->flags.accel ? HDR_CONNECTION : HDR_PROXY_CONNECTION,
1199 request->flags.proxy_keepalive ? "keep-alive" : "close");
1200 #if ADD_X_REQUEST_URI
1201 /*
1202 * Knowing the URI of the request is useful when debugging persistent
1203 * connections in a client; we cannot guarantee the order of http headers,
1204 * but X-Request-URI is likely to be the very last header to ease use from a
1205 * debugger [hdr->entries.count-1].
1206 */
1207 httpHeaderPutStr(hdr, HDR_X_REQUEST_URI,
1208 http->entry->mem_obj->url ? http->entry->mem_obj->url : http->uri);
1209 #endif
1210 }
1211
1212 static HttpReply *
1213 clientBuildReply(clientHttpRequest * http, const char *buf, size_t size)
1214 {
1215 HttpReply *rep = httpReplyCreate();
1216 if (httpReplyParse(rep, buf)) {
1217 /* enforce 1.0 reply version */
1218 rep->sline.version = 1.0;
1219 /* do header conversions */
1220 clientBuildReplyHeader(http, rep);
1221 /* if we do ranges, change status to "Partial Content" */
1222 if (http->request->range)
1223 httpStatusLineSet(&rep->sline, rep->sline.version, HTTP_PARTIAL_CONTENT, NULL);
1224 } else {
1225 /* parsing failure, get rid of the invalid reply */
1226 httpReplyDestroy(rep);
1227 rep = NULL;
1228 /* if we were going to do ranges, backoff */
1229 if (http->request->range)
1230 clientBuildRangeHeader(http, rep); /* will fail and destroy request->range */
1231 }
1232 return rep;
1233 }
1234
1235 /*
1236 * clientCacheHit should only be called until the HTTP reply headers
1237 * have been parsed. Normally this should be a single call, but
1238 * it might take more than one. As soon as we have the headers,
1239 * we hand off to clientSendMoreData, clientProcessExpired, or
1240 * clientProcessMiss.
1241 */
1242 static void
1243 clientCacheHit(void *data, char *buf, ssize_t size)
1244 {
1245 clientHttpRequest *http = data;
1246 StoreEntry *e = http->entry;
1247 MemObject *mem;
1248 request_t *r = http->request;
1249 debug(33, 3) ("clientCacheHit: %s, %d bytes\n", http->uri, (int) size);
1250 if (http->entry == NULL) {
1251 memFree(buf, MEM_CLIENT_SOCK_BUF);
1252 debug(33, 3) ("clientCacheHit: request aborted\n");
1253 return;
1254 } else if (size < 0) {
1255 /* swap in failure */
1256 memFree(buf, MEM_CLIENT_SOCK_BUF);
1257 debug(33, 3) ("clientCacheHit: swapin failure for %s\n", http->uri);
1258 http->log_type = LOG_TCP_SWAPFAIL_MISS;
1259 if ((e = http->entry)) {
1260 http->entry = NULL;
1261 storeUnregister(e, http);
1262 storeUnlockObject(e);
1263 }
1264 clientProcessMiss(http);
1265 return;
1266 }
1267 assert(size > 0);
1268 mem = e->mem_obj;
1269 assert(!EBIT_TEST(e->flags, ENTRY_ABORTED));
1270 if (mem->reply->sline.status == 0) {
1271 /*
1272 * we don't have full reply headers yet; either wait for more or
1273 * punt to clientProcessMiss.
1274 */
1275 if (e->mem_status == IN_MEMORY || e->store_status == STORE_OK) {
1276 memFree(buf, MEM_CLIENT_SOCK_BUF);
1277 clientProcessMiss(http);
1278 } else if (size == CLIENT_SOCK_SZ && http->out.offset == 0) {
1279 memFree(buf, MEM_CLIENT_SOCK_BUF);
1280 clientProcessMiss(http);
1281 } else {
1282 debug(33, 3) ("clientCacheHit: waiting for HTTP reply headers\n");
1283 storeClientCopy(e,
1284 http->out.offset + size,
1285 http->out.offset,
1286 CLIENT_SOCK_SZ,
1287 buf,
1288 clientCacheHit,
1289 http);
1290 }
1291 return;
1292 }
1293 /*
1294 * Got the headers, now grok them
1295 */
1296 assert(http->log_type == LOG_TCP_HIT);
1297 if (checkNegativeHit(e)) {
1298 http->log_type = LOG_TCP_NEGATIVE_HIT;
1299 clientSendMoreData(data, buf, size);
1300 } else if (r->method == METHOD_HEAD) {
1301 /*
1302 * RFC 2068 seems to indicate there is no "conditional HEAD"
1303 * request. We cannot validate a cached object for a HEAD
1304 * request, nor can we return 304.
1305 */
1306 if (e->mem_status == IN_MEMORY)
1307 http->log_type = LOG_TCP_MEM_HIT;
1308 clientSendMoreData(data, buf, size);
1309 } else if (refreshCheckHTTP(e, r) && !http->flags.internal) {
1310 debug(33, 5) ("clientCacheHit: in refreshCheck() block\n");
1311 /*
1312 * We hold a stale copy; it needs to be validated
1313 */
1314 /*
1315 * The 'need_validation' flag is used to prevent forwarding
1316 * loops between siblings. If our copy of the object is stale,
1317 * then we should probably only use parents for the validation
1318 * request. Otherwise two siblings could generate a loop if
1319 * both have a stale version of the object.
1320 */
1321 r->flags.need_validation = 1;
1322 if (e->lastmod < 0) {
1323 /*
1324 * Previous reply didn't have a Last-Modified header,
1325 * we cannot revalidate it.
1326 */
1327 http->log_type = LOG_TCP_MISS;
1328 clientProcessMiss(http);
1329 } else if (r->flags.nocache) {
1330 /*
1331 * This did not match a refresh pattern that overrides no-cache
1332 * we should honour the client no-cache header.
1333 */
1334 http->log_type = LOG_TCP_CLIENT_REFRESH_MISS;
1335 clientProcessMiss(http);
1336 } else if (r->protocol == PROTO_HTTP) {
1337 /*
1338 * Object needs to be revalidated
1339 * XXX This could apply to FTP as well, if Last-Modified is known.
1340 */
1341 http->log_type = LOG_TCP_REFRESH_MISS;
1342 clientProcessExpired(http);
1343 } else {
1344 /*
1345 * We don't know how to re-validate other protocols. Handle
1346 * them as if the object has expired.
1347 */
1348 http->log_type = LOG_TCP_MISS;
1349 clientProcessMiss(http);
1350 }
1351 memFree(buf, MEM_CLIENT_SOCK_BUF);
1352 } else if (r->flags.ims) {
1353 /*
1354 * Handle If-Modified-Since requests from the client
1355 */
1356 if (mem->reply->sline.status != HTTP_OK) {
1357 debug(33, 4) ("clientCacheHit: Reply code %d != 200\n",
1358 mem->reply->sline.status);
1359 memFree(buf, MEM_CLIENT_SOCK_BUF);
1360 http->log_type = LOG_TCP_MISS;
1361 clientProcessMiss(http);
1362 } else if (modifiedSince(e, http->request)) {
1363 http->log_type = LOG_TCP_IMS_HIT;
1364 clientSendMoreData(data, buf, size);
1365 } else {
1366 MemBuf mb = httpPacked304Reply(e->mem_obj->reply);
1367 http->log_type = LOG_TCP_IMS_HIT;
1368 memFree(buf, MEM_CLIENT_SOCK_BUF);
1369 storeUnregister(e, http);
1370 storeUnlockObject(e);
1371 e = clientCreateStoreEntry(http, http->request->method, null_request_flags);
1372 http->entry = e;
1373 httpReplyParse(e->mem_obj->reply, mb.buf);
1374 storeAppend(e, mb.buf, mb.size);
1375 memBufClean(&mb);
1376 storeComplete(e);
1377 }
1378 } else {
1379 /*
1380 * plain ol' cache hit
1381 */
1382 if (e->mem_status == IN_MEMORY)
1383 http->log_type = LOG_TCP_MEM_HIT;
1384 else if (Config.onoff.offline)
1385 http->log_type = LOG_TCP_OFFLINE_HIT;
1386 clientSendMoreData(data, buf, size);
1387 }
1388 }
1389
1390 /* put terminating boundary for multiparts */
1391 static void
1392 clientPackTermBound(String boundary, MemBuf * mb)
1393 {
1394 memBufPrintf(mb, "\r\n--%s--\r\n", strBuf(boundary));
1395 debug(33, 6) ("clientPackTermBound: buf offset: %d\n", mb->size);
1396 }
1397
1398 /* appends a "part" HTTP header (as in a multi-part/range reply) to the buffer */
1399 static void
1400 clientPackRangeHdr(const HttpReply * rep, const HttpHdrRangeSpec * spec, String boundary, MemBuf * mb)
1401 {
1402 HttpHeader hdr;
1403 Packer p;
1404 assert(rep);
1405 assert(spec);
1406
1407 /* put boundary */
1408 debug(33, 5) ("clientPackRangeHdr: appending boundary: %s\n", strBuf(boundary));
1409 /* rfc2046 requires to _prepend_ boundary with <crlf>! */
1410 memBufPrintf(mb, "\r\n--%s\r\n", strBuf(boundary));
1411
1412 /* stuff the header with required entries and pack it */
1413 httpHeaderInit(&hdr, hoReply);
1414 if (httpHeaderHas(&rep->header, HDR_CONTENT_TYPE))
1415 httpHeaderPutStr(&hdr, HDR_CONTENT_TYPE, httpHeaderGetStr(&rep->header, HDR_CONTENT_TYPE));
1416 httpHeaderAddContRange(&hdr, *spec, rep->content_length);
1417 packerToMemInit(&p, mb);
1418 httpHeaderPackInto(&hdr, &p);
1419 packerClean(&p);
1420 httpHeaderClean(&hdr);
1421
1422 /* append <crlf> (we packed a header, not a reply) */
1423 memBufPrintf(mb, crlf);
1424 }
1425
1426 /* extracts a "range" from *buf and appends them to mb, updating all offsets and such */
1427 static void
1428 clientPackRange(clientHttpRequest * http, HttpHdrRangeIter * i, const char **buf, ssize_t * size, MemBuf * mb)
1429 {
1430 const size_t copy_sz = i->debt_size <= *size ? i->debt_size : *size;
1431 off_t body_off = http->out.offset - i->prefix_size;
1432 assert(*size > 0);
1433 assert(i->spec);
1434
1435 /* intersection of "have" and "need" ranges must not be empty */
1436 assert(body_off < i->spec->offset + i->spec->length);
1437 assert(body_off + *size > i->spec->offset);
1438
1439 /* put boundary and headers at the beginning of a range in a multi-range */
1440 if (http->request->range->specs.count > 1 && i->debt_size == i->spec->length) {
1441 assert(http->entry->mem_obj);
1442 clientPackRangeHdr(
1443 http->entry->mem_obj->reply, /* original reply */
1444 i->spec, /* current range */
1445 i->boundary, /* boundary, the same for all */
1446 mb
1447 );
1448 }
1449 /* append content */
1450 debug(33, 3) ("clientPackRange: appending %d bytes\n", copy_sz);
1451 memBufAppend(mb, *buf, copy_sz);
1452
1453 /* update offsets */
1454 *size -= copy_sz;
1455 i->debt_size -= copy_sz;
1456 body_off += copy_sz;
1457 *buf += copy_sz;
1458 http->out.offset = body_off + i->prefix_size; /* sync */
1459
1460 /* paranoid check */
1461 assert(*size >= 0 && i->debt_size >= 0);
1462 }
1463
1464 /* returns true if there is still data available to pack more ranges
1465 * increments iterator "i"
1466 * used by clientPackMoreRanges */
1467 static int
1468 clientCanPackMoreRanges(const clientHttpRequest * http, HttpHdrRangeIter * i, ssize_t size)
1469 {
1470 /* first update "i" if needed */
1471 if (!i->debt_size) {
1472 if ((i->spec = httpHdrRangeGetSpec(http->request->range, &i->pos)))
1473 i->debt_size = i->spec->length;
1474 }
1475 assert(!i->debt_size == !i->spec); /* paranoid sync condition */
1476 /* continue condition: need_more_data && have_more_data */
1477 return i->spec && size > 0;
1478 }
1479
1480 /* extracts "ranges" from buf and appends them to mb, updating all offsets and such */
1481 /* returns true if we need more data */
1482 static int
1483 clientPackMoreRanges(clientHttpRequest * http, const char *buf, ssize_t size, MemBuf * mb)
1484 {
1485 HttpHdrRangeIter *i = &http->range_iter;
1486 /* offset in range specs does not count the prefix of an http msg */
1487 off_t body_off = http->out.offset - i->prefix_size;
1488 assert(size >= 0);
1489 /* check: reply was parsed and range iterator was initialized */
1490 assert(i->prefix_size > 0);
1491 /* filter out data according to range specs */
1492 while (clientCanPackMoreRanges(http, i, size)) {
1493 off_t start; /* offset of still missing data */
1494 assert(i->spec);
1495 start = i->spec->offset + i->spec->length - i->debt_size;
1496 debug(33, 3) ("clientPackMoreRanges: in: offset: %d size: %d\n",
1497 (int) body_off, size);
1498 debug(33, 3) ("clientPackMoreRanges: out: start: %d spec[%d]: [%d, %d), len: %d debt: %d\n",
1499 (int) start, (int) i->pos, i->spec->offset, (int) (i->spec->offset + i->spec->length), i->spec->length, i->debt_size);
1500 assert(body_off <= start); /* we did not miss it */
1501 /* skip up to start */
1502 if (body_off + size > start) {
1503 const size_t skip_size = start - body_off;
1504 body_off = start;
1505 size -= skip_size;
1506 buf += skip_size;
1507 } else {
1508 /* has not reached start yet */
1509 body_off += size;
1510 size = 0;
1511 buf = NULL;
1512 }
1513 /* put next chunk if any */
1514 if (size) {
1515 http->out.offset = body_off + i->prefix_size; /* sync */
1516 clientPackRange(http, i, &buf, &size, mb);
1517 body_off = http->out.offset - i->prefix_size; /* sync */
1518 }
1519 }
1520 assert(!i->debt_size == !i->spec); /* paranoid sync condition */
1521 debug(33, 3) ("clientPackMoreRanges: buf exhausted: in: offset: %d size: %d need_more: %d\n",
1522 (int) body_off, size, i->debt_size);
1523 if (i->debt_size) {
1524 debug(33, 3) ("clientPackMoreRanges: need more: spec[%d]: [%d, %d), len: %d\n",
1525 (int) i->pos, i->spec->offset, (int) (i->spec->offset + i->spec->length), i->spec->length);
1526 /* skip the data we do not need if possible */
1527 if (i->debt_size == i->spec->length) /* at the start of the cur. spec */
1528 body_off = i->spec->offset;
1529 else
1530 assert(body_off == i->spec->offset + i->spec->length - i->debt_size);
1531 } else if (http->request->range->specs.count > 1) {
1532 /* put terminating boundary for multiparts */
1533 clientPackTermBound(i->boundary, mb);
1534 }
1535 http->out.offset = body_off + i->prefix_size; /* sync */
1536 return i->debt_size > 0;
1537 }
1538
1539 static int
1540 clientReplyBodyTooLarge(int clen)
1541 {
1542 if (0 == Config.maxReplyBodySize)
1543 return 0; /* disabled */
1544 if (clen < 0)
1545 return 0; /* unknown */
1546 if (clen > Config.maxReplyBodySize)
1547 return 1; /* too large */
1548 return 0;
1549 }
1550
1551 /*
1552 * accepts chunk of a http message in buf, parses prefix, filters headers and
1553 * such, writes processed message to the client's socket
1554 */
1555 static void
1556 clientSendMoreData(void *data, char *buf, ssize_t size)
1557 {
1558 clientHttpRequest *http = data;
1559 StoreEntry *entry = http->entry;
1560 ConnStateData *conn = http->conn;
1561 int fd = conn->fd;
1562 HttpReply *rep = NULL;
1563 const char *body_buf = buf;
1564 ssize_t body_size = size;
1565 MemBuf mb;
1566 ssize_t check_size = 0;
1567 debug(33, 5) ("clientSendMoreData: %s, %d bytes\n", http->uri, (int) size);
1568 assert(size <= CLIENT_SOCK_SZ);
1569 assert(http->request != NULL);
1570 dlinkDelete(&http->active, &ClientActiveRequests);
1571 dlinkAdd(http, &http->active, &ClientActiveRequests);
1572 debug(33, 5) ("clientSendMoreData: FD %d '%s', out.offset=%d \n",
1573 fd, storeUrl(entry), (int) http->out.offset);
1574 if (conn->chr != http) {
1575 /* there is another object in progress, defer this one */
1576 debug(33, 1) ("clientSendMoreData: Deferring %s\n", storeUrl(entry));
1577 memFree(buf, MEM_CLIENT_SOCK_BUF);
1578 return;
1579 } else if (entry && EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
1580 /* call clientWriteComplete so the client socket gets closed */
1581 clientWriteComplete(fd, NULL, 0, COMM_OK, http);
1582 memFree(buf, MEM_CLIENT_SOCK_BUF);
1583 return;
1584 } else if (size < 0) {
1585 /* call clientWriteComplete so the client socket gets closed */
1586 clientWriteComplete(fd, NULL, 0, COMM_OK, http);
1587 memFree(buf, MEM_CLIENT_SOCK_BUF);
1588 return;
1589 } else if (size == 0) {
1590 /* call clientWriteComplete so the client socket gets closed */
1591 clientWriteComplete(fd, NULL, 0, COMM_OK, http);
1592 memFree(buf, MEM_CLIENT_SOCK_BUF);
1593 return;
1594 }
1595 if (http->out.offset == 0) {
1596 if (Config.onoff.log_mime_hdrs) {
1597 size_t k;
1598 if ((k = headersEnd(buf, size))) {
1599 safe_free(http->al.headers.reply);
1600 http->al.headers.reply = xcalloc(k + 1, 1);
1601 xstrncpy(http->al.headers.reply, buf, k);
1602 }
1603 }
1604 rep = clientBuildReply(http, buf, size);
1605 if (rep && clientReplyBodyTooLarge(rep->content_length)) {
1606 ErrorState *err = errorCon(ERR_TOO_BIG, HTTP_FORBIDDEN);
1607 err->request = requestLink(http->request);
1608 storeUnregister(http->entry, http);
1609 storeUnlockObject(http->entry);
1610 http->entry = clientCreateStoreEntry(http, http->request->method,
1611 null_request_flags);
1612 errorAppendEntry(http->entry, err);
1613 return;
1614 } else if (rep) {
1615 body_size = size - rep->hdr_sz;
1616 assert(body_size >= 0);
1617 body_buf = buf + rep->hdr_sz;
1618 http->range_iter.prefix_size = rep->hdr_sz;
1619 debug(33, 3) ("clientSendMoreData: Appending %d bytes after %d bytes of headers\n",
1620 body_size, rep->hdr_sz);
1621 } else if (size < CLIENT_SOCK_SZ && entry->store_status == STORE_PENDING) {
1622 /* wait for more to arrive */
1623 storeClientCopy(entry,
1624 http->out.offset + size,
1625 http->out.offset,
1626 CLIENT_SOCK_SZ,
1627 buf,
1628 clientSendMoreData,
1629 http);
1630 return;
1631 }
1632 /* reset range iterator */
1633 http->range_iter.pos = HttpHdrRangeInitPos;
1634 }
1635 if (http->request->method == METHOD_HEAD) {
1636 if (rep) {
1637 /* do not forward body for HEAD replies */
1638 body_size = 0;
1639 http->flags.done_copying = 1;
1640 } else {
1641 /*
1642 * If we are here, then store_status == STORE_OK and it
1643 * seems we have a HEAD repsponse which is missing the
1644 * empty end-of-headers line (home.mira.net, phttpd/0.99.72
1645 * does this). Because clientBuildReply() fails we just
1646 * call this reply a body, set the done_copying flag and
1647 * continue...
1648 */
1649 http->flags.done_copying = 1;
1650 }
1651 }
1652 /* write headers and/or body if any */
1653 assert(rep || (body_buf && body_size));
1654 /* init mb; put status line and headers if any */
1655 if (rep) {
1656 mb = httpReplyPack(rep);
1657 http->out.offset += rep->hdr_sz;
1658 check_size += rep->hdr_sz;
1659 httpReplyDestroy(rep);
1660 rep = NULL;
1661 } else {
1662 memBufDefInit(&mb);
1663 }
1664 /* append body if any */
1665 if (http->request->range) {
1666 /* Only GET requests should have ranges */
1667 assert(http->request->method == METHOD_GET);
1668 /* clientPackMoreRanges() updates http->out.offset */
1669 /* force the end of the transfer if we are done */
1670 if (!clientPackMoreRanges(http, body_buf, body_size, &mb))
1671 http->flags.done_copying = 1;
1672 } else if (body_buf && body_size) {
1673 http->out.offset += body_size;
1674 check_size += body_size;
1675 memBufAppend(&mb, body_buf, body_size);
1676 }
1677 if (!http->request->range && http->request->method == METHOD_GET)
1678 assert(check_size == size);
1679 /* write */
1680 comm_write_mbuf(fd, mb, clientWriteComplete, http);
1681 /* if we don't do it, who will? */
1682 memFree(buf, MEM_CLIENT_SOCK_BUF);
1683 }
1684
1685 static void
1686 clientKeepaliveNextRequest(clientHttpRequest * http)
1687 {
1688 ConnStateData *conn = http->conn;
1689 StoreEntry *entry;
1690 debug(33, 3) ("clientKeepaliveNextRequest: FD %d\n", conn->fd);
1691 conn->defer.until = 0; /* Kick it to read a new request */
1692 httpRequestFree(http);
1693 if ((http = conn->chr) == NULL) {
1694 debug(33, 5) ("clientKeepaliveNextRequest: FD %d reading next req\n",
1695 conn->fd);
1696 fd_note(conn->fd, "Reading next request");
1697 /*
1698 * Set the timeout BEFORE calling clientReadRequest().
1699 */
1700 commSetTimeout(conn->fd, 15, requestTimeout, conn);
1701 clientReadRequest(conn->fd, conn); /* Read next request */
1702 /*
1703 * Note, the FD may be closed at this point.
1704 */
1705 } else if ((entry = http->entry) == NULL) {
1706 /*
1707 * this request is in progress, maybe doing an ACL or a redirect,
1708 * execution will resume after the operation completes.
1709 */
1710 } else {
1711 debug(33, 1) ("clientKeepaliveNextRequest: FD %d Sending next\n",
1712 conn->fd);
1713 assert(entry);
1714 if (0 == storeClientCopyPending(entry, http)) {
1715 if (EBIT_TEST(entry->flags, ENTRY_ABORTED))
1716 debug(33, 0) ("clientKeepaliveNextRequest: ENTRY_ABORTED\n");
1717 storeClientCopy(entry,
1718 http->out.offset,
1719 http->out.offset,
1720 CLIENT_SOCK_SZ,
1721 memAllocate(MEM_CLIENT_SOCK_BUF),
1722 clientSendMoreData,
1723 http);
1724 }
1725 }
1726 }
1727
1728 static void
1729 clientWriteComplete(int fd, char *bufnotused, size_t size, int errflag, void *data)
1730 {
1731 clientHttpRequest *http = data;
1732 StoreEntry *entry = http->entry;
1733 int done;
1734 http->out.size += size;
1735 debug(33, 5) ("clientWriteComplete: FD %d, sz %d, err %d, off %d, len %d\n",
1736 fd, size, errflag, (int) http->out.offset, entry ? objectLen(entry) : 0);
1737 if (size > 0) {
1738 kb_incr(&Counter.client_http.kbytes_out, size);
1739 if (isTcpHit(http->log_type))
1740 kb_incr(&Counter.client_http.hit_kbytes_out, size);
1741 }
1742 if (errflag) {
1743 /*
1744 * just close the socket, httpRequestFree will abort if needed
1745 */
1746 comm_close(fd);
1747 } else if (NULL == entry) {
1748 comm_close(fd); /* yuk */
1749 } else if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
1750 comm_close(fd);
1751 } else if ((done = clientCheckTransferDone(http)) != 0 || size == 0) {
1752 debug(33, 5) ("clientWriteComplete: FD %d transfer is DONE\n", fd);
1753 /* We're finished case */
1754 if (http->entry->mem_obj->reply->content_length < 0) {
1755 debug(33, 5) ("clientWriteComplete: closing, content_length < 0\n");
1756 comm_close(fd);
1757 } else if (!done) {
1758 debug(33, 5) ("clientWriteComplete: closing, !done\n");
1759 comm_close(fd);
1760 } else if (clientGotNotEnough(http)) {
1761 debug(33, 5) ("clientWriteComplete: client didn't get all it expected\n");
1762 comm_close(fd);
1763 } else if (http->request->flags.proxy_keepalive) {
1764 debug(33, 5) ("clientWriteComplete: FD %d Keeping Alive\n", fd);
1765 clientKeepaliveNextRequest(http);
1766 } else {
1767 comm_close(fd);
1768 }
1769 } else if (clientReplyBodyTooLarge((int) http->out.offset)) {
1770 comm_close(fd);
1771 } else {
1772 /* More data will be coming from primary server; register with
1773 * storage manager. */
1774 if (EBIT_TEST(entry->flags, ENTRY_ABORTED))
1775 debug(33, 0) ("clientWriteComplete 2: ENTRY_ABORTED\n");
1776 storeClientCopy(entry,
1777 http->out.offset,
1778 http->out.offset,
1779 CLIENT_SOCK_SZ,
1780 memAllocate(MEM_CLIENT_SOCK_BUF),
1781 clientSendMoreData,
1782 http);
1783 }
1784 }
1785
1786 /*
1787 * client issued a request with an only-if-cached cache-control directive;
1788 * we did not find a cached object that can be returned without
1789 * contacting other servers;
1790 * respond with a 504 (Gateway Timeout) as suggested in [RFC 2068]
1791 */
1792 static void
1793 clientProcessOnlyIfCachedMiss(clientHttpRequest * http)
1794 {
1795 char *url = http->uri;
1796 request_t *r = http->request;
1797 ErrorState *err = NULL;
1798 debug(33, 4) ("clientProcessOnlyIfCachedMiss: '%s %s'\n",
1799 RequestMethodStr[r->method], url);
1800 http->al.http.code = HTTP_GATEWAY_TIMEOUT;
1801 err = errorCon(ERR_ONLY_IF_CACHED_MISS, HTTP_GATEWAY_TIMEOUT);
1802 err->request = requestLink(r);
1803 err->src_addr = http->conn->peer.sin_addr;
1804 if (http->entry) {
1805 storeUnregister(http->entry, http);
1806 storeUnlockObject(http->entry);
1807 }
1808 http->entry = clientCreateStoreEntry(http, r->method, null_request_flags);
1809 errorAppendEntry(http->entry, err);
1810 }
1811
1812 static log_type
1813 clientProcessRequest2(clientHttpRequest * http)
1814 {
1815 request_t *r = http->request;
1816 StoreEntry *e;
1817 e = http->entry = storeGetPublic(http->uri, r->method);
1818 if (r->method == METHOD_HEAD && e == NULL) {
1819 /* We can generate a HEAD reply from a cached GET object */
1820 e = http->entry = storeGetPublic(http->uri, METHOD_GET);
1821 }
1822 #if USE_CACHE_DIGESTS
1823 http->lookup_type = e ? "HIT" : "MISS";
1824 #endif
1825 if (NULL == e) {
1826 /* this object isn't in the cache */
1827 debug(33, 3) ("clientProcessRequest2: storeGet() MISS\n");
1828 return LOG_TCP_MISS;
1829 }
1830 if (Config.onoff.offline) {
1831 debug(33, 3) ("clientProcessRequest2: offline HIT\n");
1832 http->entry = e;
1833 return LOG_TCP_HIT;
1834 }
1835 if (!storeEntryValidToSend(e)) {
1836 debug(33, 3) ("clientProcessRequest2: !storeEntryValidToSend MISS\n");
1837 http->entry = NULL;
1838 return LOG_TCP_MISS;
1839 }
1840 if (EBIT_TEST(e->flags, ENTRY_SPECIAL)) {
1841 /* Special entries are always hits, no matter what the client says */
1842 debug(33, 3) ("clientProcessRequest2: ENTRY_SPECIAL HIT\n");
1843 http->entry = e;
1844 return LOG_TCP_HIT;
1845 }
1846 #if HTTP_VIOLATIONS
1847 if (r->flags.nocache_hack) {
1848 /* if nocache_hack is set, nocache should always be clear, right? */
1849 assert(!r->flags.nocache);
1850 ipcacheReleaseInvalid(r->host);
1851 /* continue! */
1852 }
1853 if (e->store_status == STORE_PENDING) {
1854 if (r->flags.nocache || r->flags.nocache_hack) {
1855 debug(33, 3) ("Clearing no-cache for STORE_PENDING request\n\t%s\n",
1856 storeUrl(e));
1857 r->flags.nocache = 0;
1858 r->flags.nocache_hack = 0;
1859 }
1860 }
1861 #endif
1862 if (r->flags.nocache) {
1863 debug(33, 3) ("clientProcessRequest2: no-cache REFRESH MISS\n");
1864 http->entry = NULL;
1865 ipcacheReleaseInvalid(r->host);
1866 return LOG_TCP_CLIENT_REFRESH_MISS;
1867 }
1868 if (r->range && httpHdrRangeWillBeComplex(r->range)) {
1869 /*
1870 * Some clients break if we return "200 OK" for a Range
1871 * request. We would have to return "200 OK" for a _complex_
1872 * Range request that is also a HIT. Thus, let's prevent HITs
1873 * on complex Range requests
1874 */
1875 debug(33, 3) ("clientProcessRequest2: complex range MISS\n");
1876 http->entry = NULL;
1877 return LOG_TCP_MISS;
1878 }
1879 debug(33, 3) ("clientProcessRequest2: default HIT\n");
1880 http->entry = e;
1881 return LOG_TCP_HIT;
1882 }
1883
1884 static void
1885 clientProcessRequest(clientHttpRequest * http)
1886 {
1887 char *url = http->uri;
1888 request_t *r = http->request;
1889 int fd = http->conn->fd;
1890 HttpReply *rep;
1891 debug(33, 4) ("clientProcessRequest: %s '%s'\n",
1892 RequestMethodStr[r->method],
1893 url);
1894 if (r->method == METHOD_CONNECT) {
1895 http->log_type = LOG_TCP_MISS;
1896 sslStart(fd, url, r, &http->out.size);
1897 return;
1898 } else if (r->method == METHOD_PURGE) {
1899 clientPurgeRequest(http);
1900 return;
1901 } else if (r->method == METHOD_TRACE) {
1902 if (r->max_forwards == 0) {
1903 http->entry = clientCreateStoreEntry(http, r->method, null_request_flags);
1904 storeReleaseRequest(http->entry);
1905 storeBuffer(http->entry);
1906 rep = httpReplyCreate();
1907 httpReplySetHeaders(rep, 1.0, HTTP_OK, NULL, "text/plain",
1908 httpRequestPrefixLen(r), 0, squid_curtime);
1909 httpReplySwapOut(rep, http->entry);
1910 httpReplyDestroy(rep);
1911 httpRequestSwapOut(r, http->entry);
1912 storeComplete(http->entry);
1913 return;
1914 }
1915 /* yes, continue */
1916 http->log_type = LOG_TCP_MISS;
1917 } else if (r->content_length > 0) {
1918 http->log_type = LOG_TCP_MISS;
1919 /* XXX oof, POST can be cached! */
1920 pumpInit(fd, r, http->uri);
1921 } else {
1922 http->log_type = clientProcessRequest2(http);
1923 }
1924 debug(33, 4) ("clientProcessRequest: %s for '%s'\n",
1925 log_tags[http->log_type],
1926 http->uri);
1927 http->out.offset = 0;
1928 if (NULL != http->entry) {
1929 storeLockObject(http->entry);
1930 storeCreateMemObject(http->entry, http->uri, http->log_uri);
1931 http->entry->mem_obj->method = r->method;
1932 storeClientListAdd(http->entry, http);
1933 #if DELAY_POOLS
1934 delaySetStoreClient(http->entry, http, delayClient(r));
1935 #endif
1936 http->entry->refcount++;
1937 #if HEAP_REPLACEMENT
1938 storeHeapPositionUpdate(http->entry);
1939 #endif
1940 storeClientCopy(http->entry,
1941 http->out.offset,
1942 http->out.offset,
1943 CLIENT_SOCK_SZ,
1944 memAllocate(MEM_CLIENT_SOCK_BUF),
1945 clientCacheHit,
1946 http);
1947 } else {
1948 /* MISS CASE */
1949 http->log_type = LOG_TCP_MISS;
1950 clientProcessMiss(http);
1951 }
1952 }
1953
1954 /*
1955 * Prepare to fetch the object as it's a cache miss of some kind.
1956 */
1957 static void
1958 clientProcessMiss(clientHttpRequest * http)
1959 {
1960 char *url = http->uri;
1961 request_t *r = http->request;
1962 ErrorState *err = NULL;
1963 debug(33, 4) ("clientProcessMiss: '%s %s'\n",
1964 RequestMethodStr[r->method], url);
1965 /*
1966 * We might have a left-over StoreEntry from a failed cache hit
1967 * or IMS request.
1968 */
1969 if (http->entry) {
1970 if (EBIT_TEST(http->entry->flags, ENTRY_SPECIAL))
1971 debug(33, 0) ("clientProcessMiss: miss on a special object (%s).\n", url);
1972 storeUnregister(http->entry, http);
1973 storeUnlockObject(http->entry);
1974 http->entry = NULL;
1975 }
1976 if (clientOnlyIfCached(http)) {
1977 clientProcessOnlyIfCachedMiss(http);
1978 return;
1979 }
1980 /*
1981 * Deny loops when running in accelerator/transproxy mode.
1982 */
1983 if (http->flags.accel && r->flags.loopdetect) {
1984 http->al.http.code = HTTP_FORBIDDEN;
1985 err = errorCon(ERR_ACCESS_DENIED, HTTP_FORBIDDEN);
1986 err->request = requestLink(r);
1987 err->src_addr = http->conn->peer.sin_addr;
1988 http->entry = clientCreateStoreEntry(http, r->method, null_request_flags);
1989 errorAppendEntry(http->entry, err);
1990 return;
1991 }
1992 assert(http->out.offset == 0);
1993 http->entry = clientCreateStoreEntry(http, r->method, r->flags);
1994 http->entry->refcount++;
1995 #if HEAP_REPLACEMENT
1996 storeHeapPositionUpdate(http->entry);
1997 #endif
1998 if (http->redirect.status) {
1999 HttpReply *rep = httpReplyCreate();
2000 storeReleaseRequest(http->entry);
2001 httpRedirectReply(rep, http->redirect.status, http->redirect.location);
2002 httpReplySwapOut(rep, http->entry);
2003 httpReplyDestroy(rep);
2004 storeComplete(http->entry);
2005 return;
2006 }
2007 if (http->flags.internal)
2008 r->protocol = PROTO_INTERNAL;
2009 fwdStart(http->conn->fd, http->entry, r,
2010 http->conn->peer.sin_addr, http->conn->me.sin_addr);
2011 }
2012
2013 static clientHttpRequest *
2014 parseHttpRequestAbort(ConnStateData * conn, const char *uri)
2015 {
2016 clientHttpRequest *http = xcalloc(1, sizeof(clientHttpRequest));
2017 cbdataAdd(http, cbdataXfree, 0);
2018 http->conn = conn;
2019 http->start = current_time;
2020 http->req_sz = conn->in.offset;
2021 http->uri = xstrdup(uri);
2022 http->log_uri = xstrndup(uri, MAX_URL);
2023 http->range_iter.boundary = StringNull;
2024 dlinkAdd(http, &http->active, &ClientActiveRequests);
2025 return http;
2026 }
2027
2028 /*
2029 * parseHttpRequest()
2030 *
2031 * Returns
2032 * NULL on error or incomplete request
2033 * a clientHttpRequest structure on success
2034 */
2035 static clientHttpRequest *
2036 parseHttpRequest(ConnStateData * conn, method_t * method_p, int *status,
2037 char **prefix_p, size_t * req_line_sz_p)
2038 {
2039 char *inbuf = NULL;
2040 char *mstr = NULL;
2041 char *url = NULL;
2042 char *req_hdr = NULL;
2043 float http_ver;
2044 char *token = NULL;
2045 char *t = NULL;
2046 char *end;
2047 int free_request = 0;
2048 size_t header_sz; /* size of headers, not including first line */
2049 size_t prefix_sz; /* size of whole request (req-line + headers) */
2050 size_t url_sz;
2051 size_t req_sz;
2052 method_t method;
2053 clientHttpRequest *http = NULL;
2054 #if IPF_TRANSPARENT
2055 struct natlookup natLookup;
2056 static int natfd = -1;
2057 #endif
2058
2059 if ((req_sz = headersEnd(conn->in.buf, conn->in.offset)) == 0) {
2060 debug(33, 5) ("Incomplete request, waiting for end of headers\n");
2061 *status = 0;
2062 *prefix_p = NULL;
2063 *method_p = METHOD_NONE;
2064 return NULL;
2065 }
2066 assert(req_sz <= conn->in.offset);
2067 /* Use memcpy, not strdup! */
2068 inbuf = xmalloc(req_sz + 1);
2069 xmemcpy(inbuf, conn->in.buf, req_sz);
2070 *(inbuf + req_sz) = '\0';
2071
2072 /* pre-set these values to make aborting simpler */
2073 *prefix_p = inbuf;
2074 *method_p = METHOD_NONE;
2075 *status = -1;
2076
2077 /* Barf on NULL characters in the headers */
2078 if (strlen(inbuf) != req_sz) {
2079 debug(33, 1) ("parseHttpRequest: Requestheader contains NULL characters\n");
2080 return parseHttpRequestAbort(conn, "error:invalid-request");
2081 }
2082 /* Look for request method */
2083 if ((mstr = strtok(inbuf, "\t ")) == NULL) {
2084 debug(33, 1) ("parseHttpRequest: Can't get request method\n");
2085 return parseHttpRequestAbort(conn, "error:invalid-request-method");
2086 }
2087 method = urlParseMethod(mstr);
2088 if (method == METHOD_NONE) {
2089 debug(33, 1) ("parseHttpRequest: Unsupported method '%s'\n", mstr);
2090 return parseHttpRequestAbort(conn, "error:unsupported-request-method");
2091 }
2092 debug(33, 5) ("parseHttpRequest: Method is '%s'\n", mstr);
2093 *method_p = method;
2094
2095 /* look for URL+HTTP/x.x */
2096 if ((url = strtok(NULL, "\n")) == NULL) {
2097 debug(33, 1) ("parseHttpRequest: Missing URL\n");
2098 return parseHttpRequestAbort(conn, "error:missing-url");
2099 }
2100 while (xisspace(*url))
2101 url++;
2102 t = url + strlen(url);
2103 assert(*t == '\0');
2104 token = NULL;
2105 while (t > url) {
2106 t--;
2107 if (xisspace(*t) && !strncmp(t + 1, "HTTP/", 5)) {
2108 token = t + 1;
2109 break;
2110 }
2111 }
2112 while (t > url && xisspace(*t))
2113 *(t--) = '\0';
2114 debug(33, 5) ("parseHttpRequest: URI is '%s'\n", url);
2115 if (token == NULL) {
2116 debug(33, 3) ("parseHttpRequest: Missing HTTP identifier\n");
2117 #if RELAXED_HTTP_PARSER
2118 http_ver = (float) 0.9; /* wild guess */
2119 #else
2120 return parseHttpRequestAbort(conn, "error:missing-http-ident");
2121 #endif
2122 } else {
2123 http_ver = (float) atof(token + 5);
2124 }
2125
2126 /*
2127 * Process headers after request line
2128 */
2129 req_hdr = strtok(NULL, null_string);
2130 header_sz = req_sz - (req_hdr - inbuf);
2131 if (0 == header_sz) {
2132 debug(33, 3) ("parseHttpRequest: header_sz == 0\n");
2133 *status = 0;
2134 return NULL;
2135 }
2136 assert(header_sz > 0);
2137 debug(33, 3) ("parseHttpRequest: req_hdr = {%s}\n", req_hdr);
2138 end = req_hdr + header_sz;
2139 debug(33, 3) ("parseHttpRequest: end = {%s}\n", end);
2140
2141 prefix_sz = end - inbuf;
2142 *req_line_sz_p = req_hdr - inbuf;
2143 debug(33, 3) ("parseHttpRequest: prefix_sz = %d, req_line_sz = %d\n",
2144 (int) prefix_sz, (int) *req_line_sz_p);
2145 assert(prefix_sz <= conn->in.offset);
2146
2147 /* Ok, all headers are received */
2148 http = xcalloc(1, sizeof(clientHttpRequest));
2149 cbdataAdd(http, cbdataXfree, 0);
2150 http->http_ver = http_ver;
2151 http->conn = conn;
2152 http->start = current_time;
2153 http->req_sz = prefix_sz;
2154 http->range_iter.boundary = StringNull;
2155 *prefix_p = xmalloc(prefix_sz + 1);
2156 xmemcpy(*prefix_p, conn->in.buf, prefix_sz);
2157 *(*prefix_p + prefix_sz) = '\0';
2158 dlinkAdd(http, &http->active, &ClientActiveRequests);
2159
2160 debug(33, 5) ("parseHttpRequest: Request Header is\n%s\n", (*prefix_p) + *req_line_sz_p);
2161 if ((t = strchr(url, '#'))) /* remove HTML anchors */
2162 *t = '\0';
2163
2164 /* handle internal objects */
2165 if (internalCheck(url)) {
2166 /* prepend our name & port */
2167 http->uri = xstrdup(internalLocalUri(NULL, url));
2168 http->flags.internal = 1;
2169 http->flags.accel = 1;
2170 }
2171 /* see if we running in Config2.Accel.on, if so got to convert it to URL */
2172 else if (Config2.Accel.on && *url == '/') {
2173 /* prepend the accel prefix */
2174 if (opt_accel_uses_host && (t = mime_get_header(req_hdr, "Host"))) {
2175 /* If a Host: header was specified, use it to build the URL
2176 * instead of the one in the Config file. */
2177 /*
2178 * XXX Use of the Host: header here opens a potential
2179 * security hole. There are no checks that the Host: value
2180 * corresponds to one of your servers. It might, for example,
2181 * refer to www.playboy.com. The 'dst' and/or 'dst_domain' ACL
2182 * types should be used to prevent httpd-accelerators
2183 * handling requests for non-local servers */
2184 strtok(t, " :/;@");
2185 url_sz = strlen(url) + 32 + Config.appendDomainLen +
2186 strlen(t);
2187 http->uri = xcalloc(url_sz, 1);
2188 snprintf(http->uri, url_sz, "http://%s:%d%s",
2189 t, (int) Config.Accel.port, url);
2190 } else if (vhost_mode) {
2191 /* Put the local socket IP address as the hostname */
2192 url_sz = strlen(url) + 32 + Config.appendDomainLen;
2193 http->uri = xcalloc(url_sz, 1);
2194 #if IPF_TRANSPARENT
2195 natLookup.nl_inport = http->conn->me.sin_port;
2196 natLookup.nl_outport = http->conn->peer.sin_port;
2197 natLookup.nl_inip = http->conn->me.sin_addr;
2198 natLookup.nl_outip = http->conn->peer.sin_addr;
2199 natLookup.nl_flags = IPN_TCP;
2200 if (natfd < 0)
2201 natfd = open(IPL_NAT, O_RDONLY, 0);
2202 if (natfd < 0) {
2203 debug(50, 1) ("parseHttpRequest: NAT open failed: %s\n",
2204 xstrerror());
2205 return parseHttpRequestAbort(conn, "error:nat-open-failed");
2206 }
2207 if (ioctl(natfd, SIOCGNATL, &natLookup) < 0) {
2208 if (errno != ESRCH) {
2209 debug(50, 1) ("parseHttpRequest: NAT lookup failed: ioctl(SIOCGNATL)\n");
2210 close(natfd);
2211 natfd = -1;
2212 return parseHttpRequestAbort(conn, "error:nat-lookup-failed");
2213 } else
2214 snprintf(http->uri, url_sz, "http://%s:%d%s",
2215 inet_ntoa(http->conn->me.sin_addr),
2216 (int) Config.Accel.port,
2217 url);
2218 } else
2219 snprintf(http->uri, url_sz, "http://%s:%d%s",
2220 inet_ntoa(natLookup.nl_realip),
2221 (int) Config.Accel.port,
2222 url);
2223 #else
2224 snprintf(http->uri, url_sz, "http://%s:%d%s",
2225 inet_ntoa(http->conn->me.sin_addr),
2226 (int) Config.Accel.port,
2227 url);
2228 #endif
2229 debug(33, 5) ("VHOST REWRITE: '%s'\n", http->uri);
2230 } else {
2231 url_sz = strlen(Config2.Accel.prefix) + strlen(url) +
2232 Config.appendDomainLen + 1;
2233 http->uri = xcalloc(url_sz, 1);
2234 snprintf(http->uri, url_sz, "%s%s", Config2.Accel.prefix, url);
2235 }
2236 http->flags.accel = 1;
2237 } else {
2238 /* URL may be rewritten later, so make extra room */
2239 url_sz = strlen(url) + Config.appendDomainLen + 5;
2240 http->uri = xcalloc(url_sz, 1);
2241 strcpy(http->uri, url);
2242 http->flags.accel = 0;
2243 }
2244 if (!stringHasWhitespace(http->uri))
2245 http->log_uri = xstrndup(http->uri, MAX_URL);
2246 else
2247 http->log_uri = xstrndup(rfc1738_escape(http->uri), MAX_URL);
2248 debug(33, 5) ("parseHttpRequest: Complete request received\n");
2249 if (free_request)
2250 safe_free(url);
2251 xfree(inbuf);
2252 *status = 1;
2253 return http;
2254 }
2255
2256 static int
2257 clientReadDefer(int fdnotused, void *data)
2258 {
2259 ConnStateData *conn = data;
2260 return conn->defer.until > squid_curtime;
2261 }
2262
2263 static void
2264 clientReadRequest(int fd, void *data)
2265 {
2266 ConnStateData *conn = data;
2267 int parser_return_code = 0;
2268 int k;
2269 request_t *request = NULL;
2270 int size;
2271 method_t method;
2272 clientHttpRequest *http = NULL;
2273 clientHttpRequest **H = NULL;
2274 char *prefix = NULL;
2275 ErrorState *err = NULL;
2276 fde *F = &fd_table[fd];
2277 int len = conn->in.size - conn->in.offset - 1;
2278 debug(33, 4) ("clientReadRequest: FD %d: reading request...\n", fd);
2279 Counter.syscalls.sock.reads++;
2280 size = read(fd, conn->in.buf + conn->in.offset, len);
2281 if (size > 0) {
2282 fd_bytes(fd, size, FD_READ);
2283 kb_incr(&Counter.client_http.kbytes_in, size);
2284 }
2285 /*
2286 * Don't reset the timeout value here. The timeout value will be
2287 * set to Config.Timeout.request by httpAccept() and
2288 * clientWriteComplete(), and should apply to the request as a
2289 * whole, not individual read() calls. Plus, it breaks our
2290 * lame half-close detection
2291 */
2292 commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, conn, 0);
2293 if (size == 0) {
2294 if (conn->chr == NULL) {
2295 /* no current or pending requests */
2296 comm_close(fd);
2297 return;
2298 } else if (!Config.onoff.half_closed_clients) {
2299 /* admin doesn't want to support half-closed client sockets */
2300 comm_close(fd);
2301 return;
2302 }
2303 /* It might be half-closed, we can't tell */
2304 debug(33, 5) ("clientReadRequest: FD %d closed?\n", fd);
2305 F->flags.socket_eof = 1;
2306 conn->defer.until = squid_curtime + 1;
2307 conn->defer.n++;
2308 fd_note(fd, "half-closed");
2309 return;
2310 } else if (size < 0) {
2311 if (!ignoreErrno(errno)) {
2312 debug(50, 2) ("clientReadRequest: FD %d: %s\n", fd, xstrerror());
2313 comm_close(fd);
2314 return;
2315 } else if (conn->in.offset == 0) {
2316 debug(50, 2) ("clientReadRequest: FD %d: no data to process (%s)\n", fd, xstrerror());
2317 return;
2318 }
2319 /* Continue to process previously read data */
2320 size = 0;
2321 }
2322 conn->in.offset += size;
2323 /* Skip leading (and trailing) whitespace */
2324 while (conn->in.offset > 0) {
2325 int nrequests;
2326 size_t req_line_sz;
2327 while (conn->in.offset > 0 && xisspace(conn->in.buf[0])) {
2328 xmemmove(conn->in.buf, conn->in.buf + 1, conn->in.offset - 1);
2329 conn->in.offset--;
2330 }
2331 conn->in.buf[conn->in.offset] = '\0'; /* Terminate the string */
2332 if (conn->in.offset == 0)
2333 break;
2334 /* Limit the number of concurrent requests to 2 */
2335 for (H = &conn->chr, nrequests = 0; *H; H = &(*H)->next, nrequests++);
2336 if (nrequests >= 2) {
2337 debug(33, 3) ("clientReadRequest: FD %d max concurrent requests reached\n", fd);
2338 debug(33, 5) ("clientReadRequest: FD %d defering new request until one is done\n", fd);
2339 conn->defer.until = squid_curtime + 100; /* Reset when a request is complete */
2340 break;
2341 }
2342 /* Process request */
2343 http = parseHttpRequest(conn,
2344 &method,
2345 &parser_return_code,
2346 &prefix,
2347 &req_line_sz);
2348 if (!http)
2349 safe_free(prefix);
2350 if (http) {
2351 assert(http->req_sz > 0);
2352 conn->in.offset -= http->req_sz;
2353 assert(conn->in.offset >= 0);
2354 debug(33, 5) ("conn->in.offset = %d\n", (int) conn->in.offset);
2355 /*
2356 * If we read past the end of this request, move the remaining
2357 * data to the beginning
2358 */
2359 if (conn->in.offset > 0)
2360 xmemmove(conn->in.buf, conn->in.buf + http->req_sz, conn->in.offset);
2361 /* add to the client request queue */
2362 for (H = &conn->chr; *H; H = &(*H)->next);
2363 *H = http;
2364 conn->nrequests++;
2365 commSetTimeout(fd, Config.Timeout.lifetime, NULL, NULL);
2366 if (parser_return_code < 0) {
2367 debug(33, 1) ("clientReadRequest: FD %d Invalid Request\n", fd);
2368 err = errorCon(ERR_INVALID_REQ, HTTP_BAD_REQUEST);
2369 err->request_hdrs = xstrdup(conn->in.buf);
2370 http->entry = clientCreateStoreEntry(http, method, null_request_flags);
2371 errorAppendEntry(http->entry, err);
2372 safe_free(prefix);
2373 break;
2374 }
2375 if ((request = urlParse(method, http->uri)) == NULL) {
2376 debug(33, 5) ("Invalid URL: %s\n", http->uri);
2377 err = errorCon(ERR_INVALID_URL, HTTP_BAD_REQUEST);
2378 err->src_addr = conn->peer.sin_addr;
2379 err->url = xstrdup(http->uri);
2380 http->al.http.code = err->http_status;
2381 http->entry = clientCreateStoreEntry(http, method, null_request_flags);
2382 errorAppendEntry(http->entry, err);
2383 safe_free(prefix);
2384 break;
2385 } else {
2386 /* compile headers */
2387 /* we should skip request line! */
2388 if (!httpRequestParseHeader(request, prefix + req_line_sz))
2389 debug(33, 1) ("Failed to parse request headers: %s\n%s\n",
2390 http->uri, prefix);
2391 /* continue anyway? */
2392 }
2393 request->flags.accelerated = http->flags.accel;
2394 if (!http->flags.internal) {
2395 if (internalCheck(strBuf(request->urlpath))) {
2396 if (internalHostnameIs(request->host) &&
2397 request->port == Config.Port.http->i) {
2398 http->flags.internal = 1;
2399 } else if (internalStaticCheck(strBuf(request->urlpath))) {
2400 xstrncpy(request->host, internalHostname(), SQUIDHOSTNAMELEN);
2401 request->port = Config.Port.http->i;
2402 http->flags.internal = 1;
2403 }
2404 }
2405 }
2406 /*
2407 * cache the Content-length value in request_t.
2408 */
2409 request->content_length = httpHeaderGetInt(&request->header,
2410 HDR_CONTENT_LENGTH);
2411 request->flags.internal = http->flags.internal;
2412 safe_free(prefix);
2413 safe_free(http->log_uri);
2414 http->log_uri = xstrdup(urlCanonicalClean(request));
2415 request->client_addr = conn->peer.sin_addr;
2416 request->my_addr = conn->me.sin_addr;
2417 request->http_ver = http->http_ver;
2418 if (!urlCheckRequest(request)) {
2419 err = errorCon(ERR_UNSUP_REQ, HTTP_NOT_IMPLEMENTED);
2420 err->src_addr = conn->peer.sin_addr;
2421 err->request = requestLink(request);
2422 request->flags.proxy_keepalive = 0;
2423 http->al.http.code = err->http_status;
2424 http->entry = clientCreateStoreEntry(http, request->method, null_request_flags);
2425 errorAppendEntry(http->entry, err);
2426 break;
2427 }
2428 if (0 == clientCheckContentLength(request)) {
2429 err = errorCon(ERR_INVALID_REQ, HTTP_LENGTH_REQUIRED);
2430 err->src_addr = conn->peer.sin_addr;
2431 err->request = requestLink(request);
2432 http->al.http.code = err->http_status;
2433 http->entry = clientCreateStoreEntry(http, request->method, null_request_flags);
2434 errorAppendEntry(http->entry, err);
2435 break;
2436 }
2437 http->request = requestLink(request);
2438 /*
2439 * We need to set the keepalive flag before doing some
2440 * hacks for POST/PUT requests below. Maybe we could
2441 * set keepalive flag even earlier.
2442 */
2443 clientSetKeepaliveFlag(http);
2444 /*
2445 * break here if the request has a content-length
2446 * because there is a reqeust body following and we
2447 * don't want to parse it as though it was new request.
2448 */
2449 if (request->content_length >= 0) {
2450 int copy_len = XMIN(conn->in.offset, request->content_length);
2451 if (copy_len > 0) {
2452 assert(conn->in.offset >= copy_len);
2453 request->body_sz = copy_len;
2454 request->body = xmalloc(request->body_sz);
2455 xmemcpy(request->body, conn->in.buf, request->body_sz);
2456 conn->in.offset -= copy_len;
2457 if (conn->in.offset)
2458 xmemmove(conn->in.buf, conn->in.buf + copy_len, conn->in.offset);
2459 }
2460 /*
2461 * if we didn't get the full body now, then more will
2462 * be arriving on the client socket. Lets cancel
2463 * the read handler until this request gets forwarded.
2464 */
2465 if (request->body_sz < request->content_length)
2466 commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
2467 if (request->content_length < 0)
2468 (void) 0;
2469 else if (request->content_length > Config.maxRequestBodySize) {
2470 err = errorCon(ERR_TOO_BIG, HTTP_REQUEST_ENTITY_TOO_LARGE);
2471 err->request = requestLink(request);
2472 http->entry = clientCreateStoreEntry(http,
2473 METHOD_NONE, null_request_flags);
2474 errorAppendEntry(http->entry, err);
2475 break;
2476 }
2477 }
2478 clientAccessCheck(http);
2479 continue; /* while offset > 0 */
2480 } else if (parser_return_code == 0) {
2481 /*
2482 * Partial request received; reschedule until parseHttpRequest()
2483 * is happy with the input
2484 */
2485 k = conn->in.size - 1 - conn->in.offset;
2486 if (k == 0) {
2487 if (conn->in.offset >= Config.maxRequestHeaderSize) {
2488 int fd = open("/tmp/error:request-too-large", O_WRONLY | O_CREAT | O_TRUNC);
2489 if (fd >= 0) {
2490 write(fd, conn->in.buf, conn->in.offset);
2491 close(fd);
2492 }
2493 /* The request is too large to handle */
2494 debug(33, 0) ("Request header is too large (%d bytes)\n",
2495 (int) conn->in.offset);
2496 debug(33, 1) ("Config 'request_header_max_size'= %d bytes.\n",
2497 Config.maxRequestHeaderSize);
2498 err = errorCon(ERR_TOO_BIG, HTTP_REQUEST_ENTITY_TOO_LARGE);
2499 http = parseHttpRequestAbort(conn, "error:request-too-large");
2500 /* add to the client request queue */
2501 for (H = &conn->chr; *H; H = &(*H)->next);
2502 *H = http;
2503 http->entry = clientCreateStoreEntry(http, METHOD_NONE, null_request_flags);
2504 errorAppendEntry(http->entry, err);
2505 return;
2506 }
2507 /* Grow the request memory area to accomodate for a large request */
2508 conn->in.size += REQUEST_BUF_SIZE;
2509 conn->in.buf = xrealloc(conn->in.buf, conn->in.size);
2510 /* XXX account conn->in.buf */
2511 debug(33, 3) ("Handling a large request, offset=%d inbufsize=%d\n",
2512 (int) conn->in.offset, conn->in.size);
2513 k = conn->in.size - 1 - conn->in.offset;
2514 }
2515 break;
2516 }
2517 }
2518 }
2519
2520 /* general lifetime handler for HTTP requests */
2521 static void
2522 requestTimeout(int fd, void *data)
2523 {
2524 ConnStateData *conn = data;
2525 ErrorState *err;
2526 debug(33, 3) ("requestTimeout: FD %d: lifetime is expired.\n", fd);
2527 if (fd_table[fd].rwstate) {
2528 /*
2529 * Some data has been sent to the client, just close the FD
2530 */
2531 comm_close(fd);
2532 } else if (conn->nrequests) {
2533 /*
2534 * assume its a persistent connection; just close it
2535 */
2536 comm_close(fd);
2537 } else {
2538 /*
2539 * Generate an error
2540 */
2541 err = errorCon(ERR_LIFETIME_EXP, HTTP_REQUEST_TIMEOUT);
2542 err->url = xstrdup("N/A");
2543 /*
2544 * Normally we shouldn't call errorSend() in client_side.c, but
2545 * it should be okay in this case. Presumably if we get here
2546 * this is the first request for the connection, and no data
2547 * has been written yet
2548 */
2549 assert(conn->chr == NULL);
2550 errorSend(fd, err);
2551 /*
2552 * if we don't close() here, we still need a timeout handler!
2553 */
2554 commSetTimeout(fd, 30, requestTimeout, conn);
2555 }
2556 }
2557
2558 static int
2559 httpAcceptDefer(void)
2560 {
2561 static time_t last_warn = 0;
2562 if (fdNFree() >= RESERVED_FD)
2563 return 0;
2564 if (last_warn + 15 < squid_curtime) {
2565 debug(33, 0) ("WARNING! Your cache is running out of filedescriptors\n");
2566 last_warn = squid_curtime;
2567 }
2568 return 1;
2569 }
2570
2571 /* Handle a new connection on HTTP socket. */
2572 void
2573 httpAccept(int sock, void *data)
2574 {
2575 int *N = data;
2576 int fd = -1;
2577 ConnStateData *connState = NULL;
2578 struct sockaddr_in peer;
2579 struct sockaddr_in me;
2580 int max = INCOMING_HTTP_MAX;
2581 #if USE_IDENT
2582 static aclCheck_t identChecklist;
2583 #endif
2584 commSetSelect(sock, COMM_SELECT_READ, httpAccept, NULL, 0);
2585 while (max-- && !httpAcceptDefer()) {
2586 memset(&peer, '\0', sizeof(struct sockaddr_in));
2587 memset(&me, '\0', sizeof(struct sockaddr_in));
2588 if ((fd = comm_accept(sock, &peer, &me)) < 0) {
2589 if (!ignoreErrno(errno))
2590 debug(50, 1) ("httpAccept: FD %d: accept failure: %s\n",
2591 sock, xstrerror());
2592 break;
2593 }
2594 debug(33, 4) ("httpAccept: FD %d: accepted\n", fd);
2595 connState = xcalloc(1, sizeof(ConnStateData));
2596 connState->peer = peer;
2597 connState->log_addr = peer.sin_addr;
2598 connState->log_addr.s_addr &= Config.Addrs.client_netmask.s_addr;
2599 connState->me = me;
2600 connState->fd = fd;
2601 connState->in.size = REQUEST_BUF_SIZE;
2602 connState->in.buf = xcalloc(connState->in.size, 1);
2603 cbdataAdd(connState, cbdataXfree, 0);
2604 /* XXX account connState->in.buf */
2605 comm_add_close_handler(fd, connStateFree, connState);
2606 if (Config.onoff.log_fqdn)
2607 fqdncache_gethostbyaddr(peer.sin_addr, FQDN_LOOKUP_IF_MISS);
2608 commSetTimeout(fd, Config.Timeout.request, requestTimeout, connState);
2609 #if USE_IDENT
2610 identChecklist.src_addr = peer.sin_addr;
2611 identChecklist.my_addr = me.sin_addr;
2612 if (aclCheckFast(Config.accessList.identLookup, &identChecklist))
2613 identStart(&me, &peer, clientIdentDone, connState);
2614 #endif
2615 commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, connState, 0);
2616 commSetDefer(fd, clientReadDefer, connState);
2617 (*N)++;
2618 }
2619 }
2620
2621 #define SENDING_BODY 0
2622 #define SENDING_HDRSONLY 1
2623 static int
2624 clientCheckTransferDone(clientHttpRequest * http)
2625 {
2626 int sending = SENDING_BODY;
2627 StoreEntry *entry = http->entry;
2628 MemObject *mem;
2629 http_reply *reply;
2630 int sendlen;
2631 if (entry == NULL)
2632 return 0;
2633 /*
2634 * For now, 'done_copying' is used for special cases like
2635 * Range and HEAD requests.
2636 */
2637 if (http->flags.done_copying)
2638 return 1;
2639 /*
2640 * Handle STORE_OK objects.
2641 * objectLen(entry) will be set proprely.
2642 */
2643 if (entry->store_status == STORE_OK) {
2644 if (http->out.offset >= objectLen(entry))
2645 return 1;
2646 else
2647 return 0;
2648 }
2649 /*
2650 * Now, handle STORE_PENDING objects
2651 */
2652 mem = entry->mem_obj;
2653 assert(mem != NULL);
2654 assert(http->request != NULL);
2655 reply = mem->reply;
2656 if (reply->hdr_sz == 0)
2657 return 0; /* haven't found end of headers yet */
2658 else if (reply->sline.status == HTTP_OK)
2659 sending = SENDING_BODY;
2660 else if (reply->sline.status == HTTP_NO_CONTENT)
2661 sending = SENDING_HDRSONLY;
2662 else if (reply->sline.status == HTTP_NOT_MODIFIED)
2663 sending = SENDING_HDRSONLY;
2664 else if (reply->sline.status < HTTP_OK)
2665 sending = SENDING_HDRSONLY;
2666 else if (http->request->method == METHOD_HEAD)
2667 sending = SENDING_HDRSONLY;
2668 else
2669 sending = SENDING_BODY;
2670 /*
2671 * Figure out how much data we are supposed to send.
2672 * If we are sending a body and we don't have a content-length,
2673 * then we must wait for the object to become STORE_OK.
2674 */
2675 if (sending == SENDING_HDRSONLY)
2676 sendlen = reply->hdr_sz;
2677 else if (reply->content_length < 0)
2678 return 0;
2679 else
2680 sendlen = reply->content_length + reply->hdr_sz;
2681 /*
2682 * Now that we have the expected length, did we send it all?
2683 */
2684 if (http->out.offset < sendlen)
2685 return 0;
2686 else
2687 return 1;
2688 }
2689
2690 static int
2691 clientGotNotEnough(clientHttpRequest * http)
2692 {
2693 int cl = http->entry->mem_obj->reply->content_length;
2694 int hs = http->entry->mem_obj->reply->hdr_sz;
2695 assert(cl >= 0);
2696 if (http->out.offset < cl + hs)
2697 return 1;
2698 return 0;
2699 }
2700
2701 /*
2702 * This function is designed to serve a fairly specific purpose.
2703 * Occasionally our vBNS-connected caches can talk to each other, but not
2704 * the rest of the world. Here we try to detect frequent failures which
2705 * make the cache unusable (e.g. DNS lookup and connect() failures). If
2706 * the failure:success ratio goes above 1.0 then we go into "hit only"
2707 * mode where we only return UDP_HIT or UDP_MISS_NOFETCH. Neighbors
2708 * will only fetch HITs from us if they are using the ICP protocol. We
2709 * stay in this mode for 5 minutes.
2710 *
2711 * Duane W., Sept 16, 1996
2712 */
2713
2714 static void
2715 checkFailureRatio(err_type etype, hier_code hcode)
2716 {
2717 static double magic_factor = 100.0;
2718 double n_good;
2719 double n_bad;
2720 if (hcode == HIER_NONE)
2721 return;
2722 n_good = magic_factor / (1.0 + request_failure_ratio);
2723 n_bad = magic_factor - n_good;
2724 switch (etype) {
2725 case ERR_DNS_FAIL:
2726 case ERR_CONNECT_FAIL:
2727 case ERR_READ_ERROR:
2728 n_bad++;
2729 break;
2730 default:
2731 n_good++;
2732 }
2733 request_failure_ratio = n_bad / n_good;
2734 if (hit_only_mode_until > squid_curtime)
2735 return;
2736 if (request_failure_ratio < 1.0)
2737 return;
2738 debug(33, 0) ("Failure Ratio at %4.2f\n", request_failure_ratio);
2739 debug(33, 0) ("Going into hit-only-mode for %d minutes...\n",
2740 FAILURE_MODE_TIME / 60);
2741 hit_only_mode_until = squid_curtime + FAILURE_MODE_TIME;
2742 request_failure_ratio = 0.8; /* reset to something less than 1.0 */
2743 }
2744
2745 void
2746 clientHttpConnectionsOpen(void)
2747 {
2748 ushortlist *u;
2749 int fd;
2750 for (u = Config.Port.http; u; u = u->next) {
2751 enter_suid();
2752 fd = comm_open(SOCK_STREAM,
2753 0,
2754 Config.Addrs.tcp_incoming,
2755 u->i,
2756 COMM_NONBLOCKING,
2757 "HTTP Socket");
2758 leave_suid();
2759 if (fd < 0)
2760 continue;
2761 comm_listen(fd);
2762 commSetSelect(fd, COMM_SELECT_READ, httpAccept, NULL, 0);
2763 /*commSetDefer(fd, httpAcceptDefer, NULL); */
2764 debug(1, 1) ("Accepting HTTP connections on port %d, FD %d.\n",
2765 (int) u->i, fd);
2766 HttpSockets[NHttpSockets++] = fd;
2767 }
2768 if (NHttpSockets < 1)
2769 fatal("Cannot open HTTP Port");
2770 }
2771
2772 void
2773 clientHttpConnectionsClose(void)
2774 {
2775 int i;
2776 for (i = 0; i < NHttpSockets; i++) {
2777 if (HttpSockets[i] >= 0) {
2778 debug(1, 1) ("FD %d Closing HTTP connection\n", HttpSockets[i]);
2779 comm_close(HttpSockets[i]);
2780 HttpSockets[i] = -1;
2781 }
2782 }
2783 NHttpSockets = 0;
2784 }