3 * $Id: client_side.cc,v 1.443 1999/04/15 06:15:50 wessels Exp $
5 * DEBUG: section 33 Client-side Routines
6 * AUTHOR: Duane Wessels
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * ----------------------------------------------------------
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.
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.
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.
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.
40 #include <sys/ioctl.h>
42 #include <netinet/tcp.h>
44 #include <ip_compat.h>
52 #define comm_close comm_lingering_close
55 static const char *const crlf
= "\r\n";
57 #define REQUEST_BUF_SIZE 4096
58 #define FAILURE_MODE_TIME 300
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
*);
78 static IDCB clientIdentDone
;
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
);
97 checkAccelOnly(clientHttpRequest
* http
)
99 /* return TRUE if someone makes a proxy request to us and
100 * we are in httpd-accel only mode */
101 if (!Config2
.Accel
.on
)
103 if (Config
.onoff
.accel_with_proxy
)
105 if (http
->request
->protocol
== PROTO_CACHEOBJ
)
107 if (http
->flags
.accel
)
114 clientIdentDone(const char *ident
, void *data
)
116 ConnStateData
*conn
= data
;
118 xstrncpy(conn
->ident
, ident
, sizeof(conn
->ident
));
120 xstrncpy(conn
->ident
, "-", sizeof(conn
->ident
));
125 clientAccessCheck(void *data
)
127 clientHttpRequest
*http
= data
;
128 ConnStateData
*conn
= http
->conn
;
130 if (checkAccelOnly(http
)) {
131 clientAccessCheckDone(0, http
);
134 browser
= httpHeaderGetStr(&http
->request
->header
, HDR_USER_AGENT
);
135 http
->acl_checklist
= aclChecklistCreate(Config
.accessList
.http
,
143 * hack for ident ACL. It needs to get full addresses, and a
144 * place to store the ident result on persistent connections...
146 http
->acl_checklist
->conn
= conn
;
147 cbdataLock(http
->acl_checklist
->conn
);
149 aclNBCheck(http
->acl_checklist
, clientAccessCheckDone
, http
);
153 * returns true if client specified that the object must come from the cache
154 * witout contacting origin server
157 clientOnlyIfCached(clientHttpRequest
* http
)
159 const request_t
*r
= http
->request
;
161 return r
->cache_control
&&
162 EBIT_TEST(r
->cache_control
->mask
, CC_ONLY_IF_CACHED
);
166 clientCreateStoreEntry(clientHttpRequest
* h
, method_t m
, request_flags flags
)
170 * For erroneous requests, we might not have a h->request,
171 * so make a fake one.
173 if (h
->request
== NULL
)
174 h
->request
= requestLink(requestCreate(m
, PROTO_NONE
, NULL
));
175 e
= storeCreateEntry(h
->uri
, h
->log_uri
, flags
, m
);
176 storeClientListAdd(e
, h
);
178 delaySetStoreClient(e
, h
, delayClient(h
->request
));
180 storeClientCopy(e
, 0, 0, CLIENT_SOCK_SZ
,
181 memAllocate(MEM_CLIENT_SOCK_BUF
), clientSendMoreData
, h
);
186 clientAccessCheckDone(int answer
, void *data
)
188 clientHttpRequest
*http
= data
;
191 ErrorState
*err
= NULL
;
192 debug(33, 5) ("clientAccessCheckDone: '%s' answer=%d\n", http
->uri
, answer
);
193 http
->acl_checklist
= NULL
;
194 if (answer
== ACCESS_ALLOWED
) {
195 safe_free(http
->uri
);
196 http
->uri
= xstrdup(urlCanonical(http
->request
));
197 assert(http
->redirect_state
== REDIRECT_NONE
);
198 http
->redirect_state
= REDIRECT_PENDING
;
199 redirectStart(http
, clientRedirectDone
, http
);
201 debug(33, 5) ("Access Denied: %s\n", http
->uri
);
202 debug(33, 5) ("AclMatchedName = %s\n",
203 AclMatchedName
? AclMatchedName
: "<null>");
205 * NOTE: get page_id here, based on AclMatchedName because
206 * if USE_DELAY_POOLS is enabled, then AclMatchedName gets
207 * clobbered in the clientCreateStoreEntry() call
208 * just below. Pedro Ribeiro <pribeiro@isel.pt>
210 page_id
= aclGetDenyInfoPage(&Config
.denyInfoList
, AclMatchedName
);
211 http
->log_type
= LOG_TCP_DENIED
;
212 http
->entry
= clientCreateStoreEntry(http
, http
->request
->method
,
214 if (answer
== ACCESS_REQ_PROXY_AUTH
|| aclIsProxyAuth(AclMatchedName
)) {
215 if (!http
->flags
.accel
) {
216 /* Proxy authorisation needed */
217 status
= HTTP_PROXY_AUTHENTICATION_REQUIRED
;
219 /* WWW authorisation needed */
220 status
= HTTP_UNAUTHORIZED
;
223 page_id
= ERR_CACHE_ACCESS_DENIED
;
225 status
= HTTP_FORBIDDEN
;
227 page_id
= ERR_ACCESS_DENIED
;
229 err
= errorCon(page_id
, status
);
230 err
->request
= requestLink(http
->request
);
231 err
->src_addr
= http
->conn
->peer
.sin_addr
;
232 errorAppendEntry(http
->entry
, err
);
237 clientRedirectDone(void *data
, char *result
)
239 clientHttpRequest
*http
= data
;
240 request_t
*new_request
= NULL
;
241 request_t
*old_request
= http
->request
;
242 debug(33, 5) ("clientRedirectDone: '%s' result=%s\n", http
->uri
,
243 result
? result
: "NULL");
244 assert(http
->redirect_state
== REDIRECT_PENDING
);
245 http
->redirect_state
= REDIRECT_DONE
;
247 http_status status
= atoi(result
);
248 if (status
== 301 || status
== 302) {
250 if ((t
= strchr(result
, ':')) != NULL
) {
251 http
->redirect
.status
= status
;
252 http
->redirect
.location
= xstrdup(t
+ 1);
254 debug(33, 1) ("clientRedirectDone: bad input: %s\n", result
);
257 if (strcmp(result
, http
->uri
))
258 new_request
= urlParse(old_request
->method
, result
);
261 safe_free(http
->uri
);
262 http
->uri
= xstrdup(urlCanonical(new_request
));
263 new_request
->http_ver
= old_request
->http_ver
;
264 httpHeaderAppend(&new_request
->header
, &old_request
->header
);
265 new_request
->client_addr
= old_request
->client_addr
;
266 new_request
->my_addr
= old_request
->my_addr
;
267 new_request
->flags
.redirected
= 1;
268 if (old_request
->body
) {
269 new_request
->body
= xmalloc(old_request
->body_sz
);
270 xmemcpy(new_request
->body
, old_request
->body
, old_request
->body_sz
);
271 new_request
->body_sz
= old_request
->body_sz
;
273 requestUnlink(old_request
);
274 http
->request
= requestLink(new_request
);
276 clientInterpretRequestHeaders(http
);
277 fd_note(http
->conn
->fd
, http
->uri
);
278 clientProcessRequest(http
);
282 clientProcessExpired(void *data
)
284 clientHttpRequest
*http
= data
;
285 char *url
= http
->uri
;
286 StoreEntry
*entry
= NULL
;
287 debug(33, 3) ("clientProcessExpired: '%s'\n", http
->uri
);
288 assert(http
->entry
->lastmod
>= 0);
290 * check if we are allowed to contact other servers
291 * @?@: Instead of a 504 (Gateway Timeout) reply, we may want to return
292 * a stale entry *if* it matches client requirements
294 if (clientOnlyIfCached(http
)) {
295 clientProcessOnlyIfCachedMiss(http
);
298 http
->request
->flags
.refresh
= 1;
299 http
->old_entry
= http
->entry
;
301 * Assert that 'http' is already a client of old_entry. If
302 * it is not, then the beginning of the object data might get
303 * freed from memory before we need to access it.
305 assert(storeClientListSearch(http
->old_entry
->mem_obj
, http
));
306 entry
= storeCreateEntry(url
,
308 http
->request
->flags
,
309 http
->request
->method
);
310 /* NOTE, don't call storeLockObject(), storeCreateEntry() does it */
311 storeClientListAdd(entry
, http
);
313 /* delay_id is already set on original store client */
314 delaySetStoreClient(entry
, http
, delayClient(http
->request
));
316 entry
->lastmod
= http
->old_entry
->lastmod
;
317 debug(33, 5) ("clientProcessExpired: lastmod %d\n", (int) entry
->lastmod
);
318 entry
->refcount
++; /* EXPIRED CASE */
320 http
->out
.offset
= 0;
321 fwdStart(http
->conn
->fd
, http
->entry
, http
->request
,
322 http
->conn
->peer
.sin_addr
, http
->conn
->me
.sin_addr
);
323 /* Register with storage manager to receive updates when data comes in. */
324 if (EBIT_TEST(entry
->flags
, ENTRY_ABORTED
))
325 debug(33, 0) ("clientProcessExpired: found ENTRY_ABORTED object\n");
326 storeClientCopy(entry
,
330 memAllocate(MEM_CLIENT_SOCK_BUF
),
331 clientHandleIMSReply
,
336 clientGetsOldEntry(StoreEntry
* new_entry
, StoreEntry
* old_entry
, request_t
* request
)
338 const http_status status
= new_entry
->mem_obj
->reply
->sline
.status
;
340 debug(33, 5) ("clientGetsOldEntry: YES, broken HTTP reply\n");
343 /* If the reply is a failure then send the old object as a last
345 if (status
>= 500 && status
< 600) {
346 debug(33, 2) ("clientGetsOldEntry: YES, failure reply=%d\n", status
);
349 /* If the reply is anything but "Not Modified" then
350 * we must forward it to the client */
351 if (HTTP_NOT_MODIFIED
!= status
) {
352 debug(33, 5) ("clientGetsOldEntry: NO, reply=%d\n", status
);
355 /* If the client did not send IMS in the request, then it
356 * must get the old object, not this "Not Modified" reply */
357 if (!request
->flags
.ims
) {
358 debug(33, 5) ("clientGetsOldEntry: YES, no client IMS\n");
361 /* If the client IMS time is prior to the entry LASTMOD time we
362 * need to send the old object */
363 if (modifiedSince(old_entry
, request
)) {
364 debug(33, 5) ("clientGetsOldEntry: YES, modified since %d\n",
368 debug(33, 5) ("clientGetsOldEntry: NO, new one is fine\n");
374 clientHandleIMSReply(void *data
, char *buf
, ssize_t size
)
376 clientHttpRequest
*http
= data
;
377 StoreEntry
*entry
= http
->entry
;
379 const char *url
= storeUrl(entry
);
380 int unlink_request
= 0;
381 StoreEntry
*oldentry
;
384 debug(33, 3) ("clientHandleIMSReply: %s, %d bytes\n", url
, (int) size
);
386 memFree(buf
, MEM_CLIENT_SOCK_BUF
);
389 if (size
< 0 && !EBIT_TEST(entry
->flags
, ENTRY_ABORTED
)) {
390 memFree(buf
, MEM_CLIENT_SOCK_BUF
);
393 mem
= entry
->mem_obj
;
394 status
= mem
->reply
->sline
.status
;
395 if (EBIT_TEST(entry
->flags
, ENTRY_ABORTED
)) {
396 debug(33, 3) ("clientHandleIMSReply: ABORTED '%s'\n", url
);
397 /* We have an existing entry, but failed to validate it */
398 /* Its okay to send the old one anyway */
399 http
->log_type
= LOG_TCP_REFRESH_FAIL_HIT
;
400 storeUnregister(entry
, http
);
401 storeUnlockObject(entry
);
402 entry
= http
->entry
= http
->old_entry
;
404 } else if (STORE_PENDING
== entry
->store_status
&& 0 == status
) {
405 debug(33, 3) ("clientHandleIMSReply: Incomplete headers for '%s'\n", url
);
406 if (size
>= CLIENT_SOCK_SZ
) {
407 /* will not get any bigger than that */
408 debug(33, 3) ("clientHandleIMSReply: Reply is too large '%s', using old entry\n", url
);
409 /* use old entry, this repeats the code abovez */
410 http
->log_type
= LOG_TCP_REFRESH_FAIL_HIT
;
411 storeUnregister(entry
, http
);
412 storeUnlockObject(entry
);
413 entry
= http
->entry
= http
->old_entry
;
417 storeClientCopy(entry
,
418 http
->out
.offset
+ size
,
422 clientHandleIMSReply
,
426 } else if (clientGetsOldEntry(entry
, http
->old_entry
, http
->request
)) {
427 /* We initiated the IMS request, the client is not expecting
428 * 304, so put the good one back. First, make sure the old entry
429 * headers have been loaded from disk. */
430 oldentry
= http
->old_entry
;
431 http
->log_type
= LOG_TCP_REFRESH_HIT
;
432 if (oldentry
->mem_obj
->request
== NULL
) {
433 oldentry
->mem_obj
->request
= requestLink(mem
->request
);
436 /* Don't memcpy() the whole reply structure here. For example,
437 * www.thegist.com (Netscape/1.13) returns a content-length for
438 * 304's which seems to be the length of the 304 HEADERS!!! and
439 * not the body they refer to. */
440 httpReplyUpdateOnNotModified(oldentry
->mem_obj
->reply
, mem
->reply
);
441 storeTimestampsSet(oldentry
);
442 storeUnregister(entry
, http
);
443 storeUnlockObject(entry
);
444 entry
= http
->entry
= oldentry
;
445 entry
->timestamp
= squid_curtime
;
446 if (unlink_request
) {
447 requestUnlink(entry
->mem_obj
->request
);
448 entry
->mem_obj
->request
= NULL
;
451 /* the client can handle this reply, whatever it is */
452 http
->log_type
= LOG_TCP_REFRESH_MISS
;
453 if (HTTP_NOT_MODIFIED
== mem
->reply
->sline
.status
) {
454 http
->old_entry
->timestamp
= squid_curtime
;
455 http
->old_entry
->refcount
++;
456 http
->log_type
= LOG_TCP_REFRESH_HIT
;
458 storeUnregister(http
->old_entry
, http
);
459 storeUnlockObject(http
->old_entry
);
462 http
->old_entry
= NULL
; /* done with old_entry */
463 assert(!EBIT_TEST(entry
->flags
, ENTRY_ABORTED
));
465 storeClientCopy(entry
,
473 clientSendMoreData(data
, buf
, size
);
478 modifiedSince(StoreEntry
* entry
, request_t
* request
)
481 MemObject
*mem
= entry
->mem_obj
;
482 time_t mod_time
= entry
->lastmod
;
483 debug(33, 3) ("modifiedSince: '%s'\n", storeUrl(entry
));
485 mod_time
= entry
->timestamp
;
486 debug(33, 3) ("modifiedSince: mod_time = %d\n", (int) mod_time
);
489 /* Find size of the object */
490 object_length
= mem
->reply
->content_length
;
491 if (object_length
< 0)
492 object_length
= contentLen(entry
);
493 if (mod_time
> request
->ims
) {
494 debug(33, 3) ("--> YES: entry newer than client\n");
496 } else if (mod_time
< request
->ims
) {
497 debug(33, 3) ("--> NO: entry older than client\n");
499 } else if (request
->imslen
< 0) {
500 debug(33, 3) ("--> NO: same LMT, no client length\n");
502 } else if (request
->imslen
== object_length
) {
503 debug(33, 3) ("--> NO: same LMT, same length\n");
506 debug(33, 3) ("--> YES: same LMT, different length\n");
512 clientPurgeRequest(clientHttpRequest
* http
)
515 ErrorState
*err
= NULL
;
517 debug(33, 3) ("Config.onoff.enable_purge = %d\n", Config
.onoff
.enable_purge
);
518 if (!Config
.onoff
.enable_purge
) {
519 http
->log_type
= LOG_TCP_DENIED
;
520 err
= errorCon(ERR_ACCESS_DENIED
, HTTP_FORBIDDEN
);
521 err
->request
= requestLink(http
->request
);
522 err
->src_addr
= http
->conn
->peer
.sin_addr
;
523 http
->entry
= clientCreateStoreEntry(http
, http
->request
->method
, null_request_flags
);
524 errorAppendEntry(http
->entry
, err
);
527 http
->log_type
= LOG_TCP_MISS
;
528 if ((entry
= storeGetPublic(http
->uri
, METHOD_GET
)) == NULL
) {
529 http
->http_code
= HTTP_NOT_FOUND
;
532 http
->http_code
= HTTP_OK
;
534 debug(33, 4) ("clientPurgeRequest: Not modified '%s'\n",
537 * Make a new entry to hold the reply to be written
540 http
->entry
= clientCreateStoreEntry(http
, http
->request
->method
, null_request_flags
);
541 httpReplyReset(r
= http
->entry
->mem_obj
->reply
);
542 httpReplySetHeaders(r
, 1.0, http
->http_code
, NULL
, NULL
, 0, 0, -1);
543 httpReplySwapOut(r
, http
->entry
);
544 storeComplete(http
->entry
);
548 checkNegativeHit(StoreEntry
* e
)
550 if (!EBIT_TEST(e
->flags
, ENTRY_NEGCACHED
))
552 if (e
->expires
<= squid_curtime
)
554 if (e
->store_status
!= STORE_OK
)
560 clientUpdateCounters(clientHttpRequest
* http
)
562 int svc_time
= tvSubMsec(http
->start
, current_time
);
564 HierarchyLogEntry
*H
;
565 Counter
.client_http
.requests
++;
566 if (isTcpHit(http
->log_type
))
567 Counter
.client_http
.hits
++;
568 if (http
->request
->err_type
!= ERR_NONE
)
569 Counter
.client_http
.errors
++;
570 statHistCount(&Counter
.client_http
.all_svc_time
, svc_time
);
572 * The idea here is not to be complete, but to get service times
573 * for only well-defined types. For example, we don't include
574 * LOG_TCP_REFRESH_FAIL_HIT because its not really a cache hit
575 * (we *tried* to validate it, but failed).
577 switch (http
->log_type
) {
578 case LOG_TCP_REFRESH_HIT
:
579 statHistCount(&Counter
.client_http
.nh_svc_time
, svc_time
);
581 case LOG_TCP_IMS_HIT
:
582 statHistCount(&Counter
.client_http
.nm_svc_time
, svc_time
);
585 case LOG_TCP_MEM_HIT
:
586 case LOG_TCP_OFFLINE_HIT
:
587 statHistCount(&Counter
.client_http
.hit_svc_time
, svc_time
);
590 case LOG_TCP_CLIENT_REFRESH_MISS
:
591 statHistCount(&Counter
.client_http
.miss_svc_time
, svc_time
);
594 /* make compiler warnings go away */
597 H
= &http
->request
->hier
;
600 Counter
.cd
.times_used
++;
603 Counter
.icp
.times_used
++;
605 if (0 != i
->stop
.tv_sec
&& 0 != i
->start
.tv_sec
)
606 statHistCount(&Counter
.icp
.query_svc_time
,
607 tvSubUsec(i
->start
, i
->stop
));
609 Counter
.icp
.query_timeouts
++;
612 Counter
.netdb
.times_used
++;
620 httpRequestFree(void *data
)
622 clientHttpRequest
*http
= data
;
623 clientHttpRequest
**H
;
624 ConnStateData
*conn
= http
->conn
;
626 request_t
*request
= http
->request
;
627 MemObject
*mem
= NULL
;
628 debug(33, 3) ("httpRequestFree: %s\n", storeUrl(http
->entry
));
629 if (!clientCheckTransferDone(http
)) {
630 if ((e
= http
->entry
)) {
632 storeUnregister(e
, http
);
633 storeUnlockObject(e
);
635 if (http
->entry
&& http
->entry
->ping_status
== PING_WAITING
)
636 storeReleaseRequest(http
->entry
);
638 assert(http
->log_type
< LOG_TYPE_MAX
);
640 mem
= http
->entry
->mem_obj
;
641 if (http
->out
.size
|| http
->log_type
) {
642 http
->al
.icp
.opcode
= ICP_INVALID
;
643 http
->al
.url
= http
->log_uri
;
644 debug(33, 9) ("httpRequestFree: al.url='%s'\n", http
->al
.url
);
646 http
->al
.http
.code
= mem
->reply
->sline
.status
;
647 http
->al
.http
.content_type
= strBuf(mem
->reply
->content_type
);
649 http
->al
.cache
.caddr
= conn
->log_addr
;
650 http
->al
.cache
.size
= http
->out
.size
;
651 http
->al
.cache
.code
= http
->log_type
;
652 http
->al
.cache
.msec
= tvSubMsec(http
->start
, current_time
);
653 if (request
->user_ident
[0])
654 http
->al
.cache
.ident
= request
->user_ident
;
656 http
->al
.cache
.ident
= conn
->ident
;
661 packerToMemInit(&p
, &mb
);
662 httpHeaderPackInto(&request
->header
, &p
);
663 http
->al
.http
.method
= request
->method
;
664 http
->al
.http
.version
= request
->http_ver
;
665 http
->al
.headers
.request
= xstrdup(mb
.buf
);
666 http
->al
.hier
= request
->hier
;
670 accessLogLog(&http
->al
);
671 clientUpdateCounters(http
);
672 clientdbUpdate(conn
->peer
.sin_addr
, http
->log_type
, PROTO_HTTP
, http
->out
.size
);
674 if (http
->acl_checklist
)
675 aclChecklistFree(http
->acl_checklist
);
677 checkFailureRatio(request
->err_type
, http
->al
.hier
.code
);
678 safe_free(http
->uri
);
679 safe_free(http
->log_uri
);
680 safe_free(http
->al
.headers
.request
);
681 safe_free(http
->al
.headers
.reply
);
682 safe_free(http
->redirect
.location
);
683 stringClean(&http
->range_iter
.boundary
);
684 if ((e
= http
->entry
)) {
686 storeUnregister(e
, http
);
687 storeUnlockObject(e
);
689 /* old_entry might still be set if we didn't yet get the reply
690 * code in clientHandleIMSReply() */
691 if ((e
= http
->old_entry
)) {
692 http
->old_entry
= NULL
;
693 storeUnregister(e
, http
);
694 storeUnlockObject(e
);
696 requestUnlink(http
->request
);
697 assert(http
!= http
->next
);
698 assert(http
->conn
->chr
!= NULL
);
699 H
= &http
->conn
->chr
;
708 dlinkDelete(&http
->active
, &ClientActiveRequests
);
712 /* This is a handler normally called by comm_close() */
714 connStateFree(int fd
, void *data
)
716 ConnStateData
*connState
= data
;
717 clientHttpRequest
*http
;
718 debug(33, 3) ("connStateFree: FD %d\n", fd
);
719 assert(connState
!= NULL
);
720 while ((http
= connState
->chr
) != NULL
) {
721 assert(http
->conn
== connState
);
722 assert(connState
->chr
!= connState
->chr
->next
);
723 httpRequestFree(http
);
725 safe_free(connState
->in
.buf
);
726 /* XXX account connState->in.buf */
727 pconnHistCount(0, connState
->nrequests
);
728 cbdataFree(connState
);
730 /* prevent those nasty RST packets */
732 char buf
[SQUID_TCP_SO_RCVBUF
];
733 while (read(fd
, buf
, SQUID_TCP_SO_RCVBUF
) > 0);
739 clientInterpretRequestHeaders(clientHttpRequest
* http
)
741 request_t
*request
= http
->request
;
742 const HttpHeader
*req_hdr
= &request
->header
;
744 #if USE_USERAGENT_LOG
747 request
->imslen
= -1;
748 request
->ims
= httpHeaderGetTime(req_hdr
, HDR_IF_MODIFIED_SINCE
);
749 if (request
->ims
> 0)
750 request
->flags
.ims
= 1;
751 if (httpHeaderHas(req_hdr
, HDR_PRAGMA
)) {
752 String s
= httpHeaderGetList(req_hdr
, HDR_PRAGMA
);
753 if (strListIsMember(&s
, "no-cache", ','))
757 if (request
->cache_control
)
758 if (EBIT_TEST(request
->cache_control
->mask
, CC_NO_CACHE
))
762 if (Config
.onoff
.reload_into_ims
)
763 request
->flags
.nocache_hack
= 1;
764 else if (refresh_nocache_hack
)
765 request
->flags
.nocache_hack
= 1;
768 request
->flags
.nocache
= 1;
770 /* ignore range header in non-GETs */
771 if (request
->method
== METHOD_GET
) {
772 request
->range
= httpHeaderGetRange(req_hdr
);
774 request
->flags
.range
= 1;
776 if (httpHeaderHas(req_hdr
, HDR_AUTHORIZATION
))
777 request
->flags
.auth
= 1;
778 if (request
->login
[0] != '\0')
779 request
->flags
.auth
= 1;
780 if (httpHeaderHas(req_hdr
, HDR_VIA
)) {
781 String s
= httpHeaderGetList(req_hdr
, HDR_VIA
);
783 * ThisCache cannot be a member of Via header, "1.0 ThisCache" can.
784 * Note ThisCache2 has a space prepended to the hostname so we don't
785 * accidentally match super-domains.
787 if (strListIsSubstr(&s
, ThisCache2
, ',')) {
788 debugObj(33, 1, "WARNING: Forwarding loop detected for:\n",
789 request
, (ObjPackMethod
) & httpRequestPack
);
790 request
->flags
.loopdetect
= 1;
793 fvdbCountVia(strBuf(s
));
797 #if USE_USERAGENT_LOG
798 if ((str
= httpHeaderGetStr(req_hdr
, HDR_USER_AGENT
)))
799 logUserAgent(fqdnFromAddr(http
->conn
->peer
.sin_addr
), str
);
802 if (httpHeaderHas(req_hdr
, HDR_X_FORWARDED_FOR
)) {
803 String s
= httpHeaderGetList(req_hdr
, HDR_X_FORWARDED_FOR
);
804 fvdbCountForw(strBuf(s
));
808 request
->cache_control
= httpHeaderGetCc(req_hdr
);
809 if (request
->method
== METHOD_TRACE
) {
810 request
->max_forwards
= httpHeaderGetInt(req_hdr
, HDR_MAX_FORWARDS
);
812 if (clientCachable(http
))
813 request
->flags
.cachable
= 1;
814 if (clientHierarchical(http
))
815 request
->flags
.hierarchical
= 1;
816 debug(33, 5) ("clientInterpretRequestHeaders: REQ_NOCACHE = %s\n",
817 request
->flags
.nocache
? "SET" : "NOT SET");
818 debug(33, 5) ("clientInterpretRequestHeaders: REQ_CACHABLE = %s\n",
819 request
->flags
.cachable
? "SET" : "NOT SET");
820 debug(33, 5) ("clientInterpretRequestHeaders: REQ_HIERARCHICAL = %s\n",
821 request
->flags
.hierarchical
? "SET" : "NOT SET");
825 * clientSetKeepaliveFlag() sets request->flags.proxy_keepalive.
826 * This is the client-side persistent connection flag. We need
827 * to set this relatively early in the request processing
828 * to handle hacks for broken servers and clients.
831 clientSetKeepaliveFlag(clientHttpRequest
* http
)
833 request_t
*request
= http
->request
;
834 const HttpHeader
*req_hdr
= &request
->header
;
835 debug(33, 3) ("clientSetKeepaliveFlag: http_ver = %3.1f\n",
837 debug(33, 3) ("clientSetKeepaliveFlag: method = %s\n",
838 RequestMethodStr
[request
->method
]);
839 if (httpMsgIsPersistent(request
->http_ver
, req_hdr
))
840 request
->flags
.proxy_keepalive
= 1;
844 clientCheckContentLength(request_t
* r
)
846 /* We only require a content-length for "upload" methods */
847 if (!pumpMethod(r
->method
))
849 if (httpHeaderGetInt(&r
->header
, HDR_CONTENT_LENGTH
) < 0)
855 clientCachable(clientHttpRequest
* http
)
857 const char *url
= http
->uri
;
858 request_t
*req
= http
->request
;
859 method_t method
= req
->method
;
861 memset(&ch
, '\0', sizeof(ch
));
863 * Hopefully, nobody really wants 'no_cache' by client's IP
864 * address, but if they do, this should work if they use IP
865 * addresses in their ACLs, or if the client's address is in
868 * This may not work yet for 'dst' and 'dst_domain' ACLs.
870 ch
.src_addr
= http
->conn
->peer
.sin_addr
;
871 ch
.my_addr
= http
->conn
->me
.sin_addr
;
872 ch
.request
= http
->request
;
874 * aclCheckFast returns 1 for ALLOW and 0 for DENY. The default
875 * is ALLOW, so we require 'no_cache DENY foo' in squid.conf
876 * to indicate uncachable objects.
878 if (!aclCheckFast(Config
.accessList
.noCache
, &ch
))
880 if (req
->protocol
== PROTO_HTTP
)
881 return httpCachable(method
);
882 /* FTP is always cachable */
883 if (req
->protocol
== PROTO_GOPHER
)
884 return gopherCachable(url
);
885 if (req
->protocol
== PROTO_WAIS
)
887 if (method
== METHOD_CONNECT
)
889 if (method
== METHOD_TRACE
)
891 if (req
->protocol
== PROTO_CACHEOBJ
)
896 /* Return true if we can query our neighbors for this object */
898 clientHierarchical(clientHttpRequest
* http
)
900 const char *url
= http
->uri
;
901 request_t
*request
= http
->request
;
902 method_t method
= request
->method
;
903 const wordlist
*p
= NULL
;
905 /* IMS needs a private key, so we can use the hierarchy for IMS only
906 * if our neighbors support private keys */
907 if (request
->flags
.ims
&& !neighbors_do_private_keys
)
909 if (request
->flags
.auth
)
911 if (method
== METHOD_TRACE
)
913 if (method
!= METHOD_GET
)
915 /* scan hierarchy_stoplist */
916 for (p
= Config
.hierarchy_stoplist
; p
; p
= p
->next
)
917 if (strstr(url
, p
->key
))
919 if (request
->flags
.loopdetect
)
921 if (request
->protocol
== PROTO_HTTP
)
922 return httpCachable(method
);
923 if (request
->protocol
== PROTO_GOPHER
)
924 return gopherCachable(url
);
925 if (request
->protocol
== PROTO_WAIS
)
927 if (request
->protocol
== PROTO_CACHEOBJ
)
933 isTcpHit(log_type code
)
935 /* this should be a bitmap for better optimization */
936 if (code
== LOG_TCP_HIT
)
938 if (code
== LOG_TCP_IMS_HIT
)
940 if (code
== LOG_TCP_REFRESH_FAIL_HIT
)
942 if (code
== LOG_TCP_REFRESH_HIT
)
944 if (code
== LOG_TCP_NEGATIVE_HIT
)
946 if (code
== LOG_TCP_MEM_HIT
)
948 if (code
== LOG_TCP_OFFLINE_HIT
)
954 * returns true if If-Range specs match reply, false otherwise
957 clientIfRangeMatch(clientHttpRequest
* http
, HttpReply
* rep
)
959 const TimeOrTag spec
= httpHeaderGetTimeOrTag(&http
->request
->header
, HDR_IF_RANGE
);
960 /* check for parsing falure */
965 ETag rep_tag
= httpHeaderGetETag(&rep
->header
, HDR_ETAG
);
966 debug(33, 3) ("clientIfRangeMatch: ETags: %s and %s\n",
967 spec
.tag
.str
, rep_tag
.str
? rep_tag
.str
: "<none>");
969 return 0; /* entity has no etag to compare with! */
970 if (spec
.tag
.weak
|| rep_tag
.weak
) {
971 debug(33, 1) ("clientIfRangeMatch: Weak ETags are not allowed in If-Range: %s ? %s\n",
972 spec
.tag
.str
, rep_tag
.str
);
973 return 0; /* must use strong validator for sub-range requests */
975 return etagIsEqual(&rep_tag
, &spec
.tag
);
977 /* got modification time? */
978 if (spec
.time
>= 0) {
979 return http
->entry
->lastmod
<= spec
.time
;
981 assert(0); /* should not happen */
985 /* returns expected content length for multi-range replies
986 * note: assumes that httpHdrRangeCanonize has already been called
987 * warning: assumes that HTTP headers for individual ranges at the
988 * time of the actuall assembly will be exactly the same as
989 * the headers when clientMRangeCLen() is called */
991 clientMRangeCLen(clientHttpRequest
* http
) {
993 HttpHdrRangePos pos
= HttpHdrRangeInitPos
;
994 const HttpHdrRangeSpec
*spec
;
997 assert(http
->entry
->mem_obj
);
1000 while ((spec
= httpHdrRangeGetSpec(http
->request
->range
, &pos
))) {
1002 /* account for headers for this range */
1004 clientPackRangeHdr(http
->entry
->mem_obj
->reply
,
1005 spec
, http
->range_iter
.boundary
, &mb
);
1008 /* account for range content */
1009 clen
+= spec
->length
;
1011 debug(33, 6) ("clientMRangeCLen: (clen += %d + %d) == %d\n",
1012 mb
.size
, spec
->length
, clen
);
1014 /* account for the terminating boundary */
1016 clientPackTermBound(http
->range_iter
.boundary
, &mb
);
1023 /* adds appropriate Range headers if needed */
1025 clientBuildRangeHeader(clientHttpRequest
* http
, HttpReply
* rep
)
1027 HttpHeader
*hdr
= rep
? &rep
->header
: 0;
1028 const char *range_err
= NULL
;
1029 assert(http
->request
->range
);
1030 /* check if we still want to do ranges */
1032 range_err
= "no [parse-able] reply";
1033 else if (rep
->sline
.status
!= HTTP_OK
)
1034 range_err
= "wrong status code";
1035 else if (httpHeaderHas(hdr
, HDR_CONTENT_RANGE
))
1036 range_err
= "origin server does ranges";
1037 else if (rep
->content_length
< 0)
1038 range_err
= "unknown length";
1039 else if (rep
->content_length
!= http
->entry
->mem_obj
->reply
->content_length
)
1040 range_err
= "INCONSISTENT length"; /* a bug? */
1041 else if (httpHeaderHas(&http
->request
->header
, HDR_IF_RANGE
) && !clientIfRangeMatch(http
, rep
))
1042 range_err
= "If-Range match failed";
1043 else if (!httpHdrRangeCanonize(http
->request
->range
, rep
->content_length
))
1044 range_err
= "canonization failed";
1045 else if (httpHdrRangeIsComplex(http
->request
->range
))
1046 range_err
= "too complex range header";
1047 /* get rid of our range specs on error */
1049 debug(33, 2) ("clientBuildRangeHeader: will not do ranges: %s.\n", range_err
);
1050 httpHdrRangeDestroy(http
->request
->range
);
1051 http
->request
->range
= NULL
;
1053 const int spec_count
= http
->request
->range
->specs
.count
;
1054 int actual_clen
= -1;
1056 debug(33, 2) ("clientBuildRangeHeader: range spec count: %d virgin clen: %d\n",
1057 spec_count
, rep
->content_length
);
1058 assert(spec_count
> 0);
1059 /* ETags should not be returned with Partial Content replies? */
1060 httpHeaderDelById(hdr
, HDR_ETAG
);
1061 /* append appropriate header(s) */
1062 if (spec_count
== 1) {
1063 HttpHdrRangePos pos
= HttpHdrRangeInitPos
;
1064 const HttpHdrRangeSpec
*spec
= httpHdrRangeGetSpec(http
->request
->range
, &pos
);
1066 /* append Content-Range */
1067 httpHeaderAddContRange(hdr
, *spec
, rep
->content_length
);
1068 /* set new Content-Length to the actual number of bytes
1069 * transmitted in the message-body */
1070 actual_clen
= spec
->length
;
1073 /* generate boundary string */
1074 http
->range_iter
.boundary
= httpHdrRangeBoundaryStr(http
);
1075 /* delete old Content-Type, add ours */
1076 httpHeaderDelById(hdr
, HDR_CONTENT_TYPE
);
1077 httpHeaderPutStrf(hdr
, HDR_CONTENT_TYPE
,
1078 "multipart/byteranges; boundary=\"%s\"",
1079 strBuf(http
->range_iter
.boundary
));
1080 /* Content-Length is not required in multipart responses
1081 * but it is always nice to have one */
1082 actual_clen
= clientMRangeCLen(http
);
1085 /* replace Content-Length header */
1086 assert(actual_clen
>= 0);
1087 httpHeaderDelById(hdr
, HDR_CONTENT_LENGTH
);
1088 httpHeaderPutInt(hdr
, HDR_CONTENT_LENGTH
, actual_clen
);
1089 debug(33, 2) ("clientBuildRangeHeader: actual content length: %d\n", actual_clen
);
1093 /* filters out unwanted entries from original reply header
1094 * adds extra entries if we have more info than origin server
1095 * adds Squid specific entries */
1097 clientBuildReplyHeader(clientHttpRequest
* http
, HttpReply
* rep
)
1099 HttpHeader
*hdr
= &rep
->header
;
1100 int is_hit
= isTcpHit(http
->log_type
);
1101 request_t
*request
= http
->request
;
1102 #if DONT_FILTER_THESE
1103 /* but you might want to if you run Squid as an HTTP accelerator */
1104 /* httpHeaderDelById(hdr, HDR_ACCEPT_RANGES); */
1105 httpHeaderDelById(hdr
, HDR_ETAG
);
1107 httpHeaderDelById(hdr
, HDR_PROXY_CONNECTION
);
1108 /* here: Keep-Alive is a field-name, not a connection directive! */
1109 httpHeaderDelByName(hdr
, "Keep-Alive");
1110 /* remove Set-Cookie if a hit */
1112 httpHeaderDelById(hdr
, HDR_SET_COOKIE
);
1113 /* handle Connection header */
1114 if (httpHeaderHas(hdr
, HDR_CONNECTION
)) {
1115 /* anything that matches Connection list member will be deleted */
1116 String strConnection
= httpHeaderGetList(hdr
, HDR_CONNECTION
);
1117 const HttpHeaderEntry
*e
;
1118 HttpHeaderPos pos
= HttpHeaderInitPos
;
1120 * think: on-average-best nesting of the two loops (hdrEntry
1121 * and strListItem) @?@
1124 * maybe we should delete standard stuff ("keep-alive","close")
1125 * from strConnection first?
1127 while ((e
= httpHeaderGetEntry(hdr
, &pos
))) {
1128 if (strListIsMember(&strConnection
, strBuf(e
->name
), ','))
1129 httpHeaderDelAt(hdr
, pos
);
1131 httpHeaderDelById(hdr
, HDR_CONNECTION
);
1132 stringClean(&strConnection
);
1136 clientBuildRangeHeader(http
, rep
);
1138 * Add Age header, not that our header must replace Age headers
1139 * from other caches if any
1141 if (http
->entry
->timestamp
> 0) {
1142 httpHeaderDelById(hdr
, HDR_AGE
);
1144 * we do not follow HTTP/1.1 precisely here becuase we rely
1145 * on Date header when computing entry->timestamp; we should
1146 * be using _request_ time if Date header is not available
1147 * or if it is out of sync
1149 httpHeaderPutInt(hdr
, HDR_AGE
,
1150 http
->entry
->timestamp
<= squid_curtime
?
1151 squid_curtime
- http
->entry
->timestamp
: 0);
1153 /* Append X-Cache */
1154 httpHeaderPutStrf(hdr
, HDR_X_CACHE
, "%s from %s",
1155 is_hit
? "HIT" : "MISS", getMyHostname());
1156 #if USE_CACHE_DIGESTS
1157 /* Append X-Cache-Lookup: -- temporary hack, to be removed @?@ @?@ */
1158 httpHeaderPutStrf(hdr
, HDR_X_CACHE_LOOKUP
, "%s from %s:%d",
1159 http
->lookup_type
? http
->lookup_type
: "NONE",
1160 getMyHostname(), Config
.Port
.http
->i
);
1163 * Clear keepalive for NON-HEAD requests with invalid content length
1165 if (request
->method
!= METHOD_HEAD
)
1166 if (http
->entry
->mem_obj
->reply
->content_length
< 0)
1167 request
->flags
.proxy_keepalive
= 0;
1168 /* Signal keep-alive if needed */
1169 httpHeaderPutStr(hdr
,
1170 http
->flags
.accel
? HDR_CONNECTION
: HDR_PROXY_CONNECTION
,
1171 request
->flags
.proxy_keepalive
? "keep-alive" : "close");
1172 #if ADD_X_REQUEST_URI
1174 * Knowing the URI of the request is useful when debugging persistent
1175 * connections in a client; we cannot guarantee the order of http headers,
1176 * but X-Request-URI is likely to be the very last header to ease use from a
1177 * debugger [hdr->entries.count-1].
1179 httpHeaderPutStr(hdr
, HDR_X_REQUEST_URI
,
1180 http
->entry
->mem_obj
->url
? http
->entry
->mem_obj
->url
: http
->uri
);
1185 clientBuildReply(clientHttpRequest
* http
, const char *buf
, size_t size
)
1187 HttpReply
*rep
= httpReplyCreate();
1188 if (httpReplyParse(rep
, buf
)) {
1189 /* enforce 1.0 reply version */
1190 rep
->sline
.version
= 1.0;
1191 /* do header conversions */
1192 clientBuildReplyHeader(http
, rep
);
1193 /* if we do ranges, change status to "Partial Content" */
1194 if (http
->request
->range
)
1195 httpStatusLineSet(&rep
->sline
, rep
->sline
.version
, HTTP_PARTIAL_CONTENT
, NULL
);
1197 /* parsing failure, get rid of the invalid reply */
1198 httpReplyDestroy(rep
);
1200 /* if we were going to do ranges, backoff */
1201 if (http
->request
->range
)
1202 clientBuildRangeHeader(http
, rep
); /* will fail and destroy request->range */
1208 * clientCacheHit should only be called until the HTTP reply headers
1209 * have been parsed. Normally this should be a single call, but
1210 * it might take more than one. As soon as we have the headers,
1211 * we hand off to clientSendMoreData, clientProcessExpired, or
1212 * clientProcessMiss.
1215 clientCacheHit(void *data
, char *buf
, ssize_t size
)
1217 clientHttpRequest
*http
= data
;
1218 StoreEntry
*e
= http
->entry
;
1220 request_t
*r
= http
->request
;
1221 debug(33, 3) ("clientCacheHit: %s, %d bytes\n", http
->uri
, (int) size
);
1222 if (http
->entry
== NULL
) {
1223 memFree(buf
, MEM_CLIENT_SOCK_BUF
);
1224 debug(33, 3) ("clientCacheHit: request aborted\n");
1226 } else if (size
< 0) {
1227 /* swap in failure */
1228 memFree(buf
, MEM_CLIENT_SOCK_BUF
);
1229 debug(33, 3) ("clientCacheHit: swapin failure for %s\n", http
->uri
);
1230 http
->log_type
= LOG_TCP_SWAPFAIL_MISS
;
1231 if ((e
= http
->entry
)) {
1233 storeUnregister(e
, http
);
1234 storeUnlockObject(e
);
1236 clientProcessMiss(http
);
1241 assert(!EBIT_TEST(e
->flags
, ENTRY_ABORTED
));
1242 if (mem
->reply
->sline
.status
== 0) {
1244 * we don't have full reply headers yet; either wait for more or
1245 * punt to clientProcessMiss.
1247 if (e
->mem_status
== IN_MEMORY
|| e
->store_status
== STORE_OK
) {
1248 memFree(buf
, MEM_CLIENT_SOCK_BUF
);
1249 clientProcessMiss(http
);
1250 } else if (size
== CLIENT_SOCK_SZ
&& http
->out
.offset
== 0) {
1251 memFree(buf
, MEM_CLIENT_SOCK_BUF
);
1252 clientProcessMiss(http
);
1254 debug(33, 3) ("clientCacheHit: waiting for HTTP reply headers\n");
1256 http
->out
.offset
+ size
,
1266 * Got the headers, now grok them
1268 assert(http
->log_type
== LOG_TCP_HIT
);
1269 if (checkNegativeHit(e
)) {
1270 http
->log_type
= LOG_TCP_NEGATIVE_HIT
;
1271 clientSendMoreData(data
, buf
, size
);
1272 } else if (r
->method
== METHOD_HEAD
) {
1274 * RFC 2068 seems to indicate there is no "conditional HEAD"
1275 * request. We cannot validate a cached object for a HEAD
1276 * request, nor can we return 304.
1278 if (e
->mem_status
== IN_MEMORY
)
1279 http
->log_type
= LOG_TCP_MEM_HIT
;
1280 clientSendMoreData(data
, buf
, size
);
1281 } else if (refreshCheckHTTP(e
, r
) && !http
->flags
.internal
) {
1282 debug(33, 5) ("clientCacheHit: in refreshCheck() block\n");
1284 * We hold a stale copy; it needs to be validated
1287 * The 'need_validation' flag is used to prevent forwarding
1288 * loops between siblings. If our copy of the object is stale,
1289 * then we should probably only use parents for the validation
1290 * request. Otherwise two siblings could generate a loop if
1291 * both have a stale version of the object.
1293 r
->flags
.need_validation
= 1;
1294 if (e
->lastmod
< 0) {
1296 * Previous reply didn't have a Last-Modified header,
1297 * we cannot revalidate it.
1299 http
->log_type
= LOG_TCP_MISS
;
1300 clientProcessMiss(http
);
1301 } else if (r
->flags
.nocache
) {
1303 * This did not match a refresh pattern that overrides no-cache
1304 * we should honour the client no-cache header.
1306 http
->log_type
= LOG_TCP_CLIENT_REFRESH_MISS
;
1307 clientProcessMiss(http
);
1308 } else if (r
->protocol
== PROTO_HTTP
) {
1310 * Object needs to be revalidated
1311 * XXX This could apply to FTP as well, if Last-Modified is known.
1313 http
->log_type
= LOG_TCP_REFRESH_MISS
;
1314 clientProcessExpired(http
);
1317 * We don't know how to re-validate other protocols. Handle
1318 * them as if the object has expired.
1320 http
->log_type
= LOG_TCP_MISS
;
1321 clientProcessMiss(http
);
1323 memFree(buf
, MEM_CLIENT_SOCK_BUF
);
1324 } else if (r
->flags
.ims
) {
1326 * Handle If-Modified-Since requests from the client
1328 if (mem
->reply
->sline
.status
!= HTTP_OK
) {
1329 debug(33, 4) ("clientCacheHit: Reply code %d != 200\n",
1330 mem
->reply
->sline
.status
);
1331 memFree(buf
, MEM_CLIENT_SOCK_BUF
);
1332 clientProcessMiss(http
);
1333 } else if (modifiedSince(e
, http
->request
)) {
1334 http
->log_type
= LOG_TCP_IMS_HIT
;
1335 clientSendMoreData(data
, buf
, size
);
1337 MemBuf mb
= httpPacked304Reply(e
->mem_obj
->reply
);
1338 http
->log_type
= LOG_TCP_IMS_HIT
;
1339 memFree(buf
, MEM_CLIENT_SOCK_BUF
);
1340 storeUnregister(e
, http
);
1341 storeUnlockObject(e
);
1342 e
= clientCreateStoreEntry(http
, http
->request
->method
, null_request_flags
);
1344 httpReplyParse(e
->mem_obj
->reply
, mb
.buf
);
1345 storeAppend(e
, mb
.buf
, mb
.size
);
1351 * plain ol' cache hit
1353 if (e
->mem_status
== IN_MEMORY
)
1354 http
->log_type
= LOG_TCP_MEM_HIT
;
1355 else if (Config
.onoff
.offline
)
1356 http
->log_type
= LOG_TCP_OFFLINE_HIT
;
1357 clientSendMoreData(data
, buf
, size
);
1361 /* put terminating boundary for multiparts */
1363 clientPackTermBound(String boundary
, MemBuf
*mb
)
1365 memBufPrintf(mb
, "\r\n--%s--\r\n", strBuf(boundary
));
1366 debug(33, 6) ("clientPackTermBound: buf offset: %d\n", mb
->size
);
1369 /* appends a "part" HTTP header (as in a multi-part/range reply) to the buffer */
1371 clientPackRangeHdr(const HttpReply
*rep
, const HttpHdrRangeSpec
*spec
, String boundary
, MemBuf
*mb
)
1379 debug(33, 5) ("clientPackRangeHdr: appending boundary: %s\n", strBuf(boundary
));
1380 /* rfc2046 requires to _prepend_ boundary with <crlf>! */
1381 memBufPrintf(mb
, "\r\n--%s\r\n", strBuf(boundary
));
1383 /* stuff the header with required entries and pack it */
1384 httpHeaderInit(&hdr
, hoReply
);
1385 if (httpHeaderHas(&rep
->header
, HDR_CONTENT_TYPE
))
1386 httpHeaderPutStr(&hdr
, HDR_CONTENT_TYPE
, httpHeaderGetStr(&rep
->header
, HDR_CONTENT_TYPE
));
1387 httpHeaderAddContRange(&hdr
, *spec
, rep
->content_length
);
1388 packerToMemInit(&p
, mb
);
1389 httpHeaderPackInto(&hdr
, &p
);
1391 httpHeaderClean(&hdr
);
1393 /* append <crlf> (we packed a header, not a reply) */
1394 memBufPrintf(mb
, crlf
);
1397 /* extracts a "range" from *buf and appends them to mb, updating all offsets and such */
1399 clientPackRange(clientHttpRequest
* http
, HttpHdrRangeIter
* i
, const char **buf
, ssize_t
* size
, MemBuf
* mb
)
1401 const size_t copy_sz
= i
->debt_size
<= *size
? i
->debt_size
: *size
;
1402 off_t body_off
= http
->out
.offset
- i
->prefix_size
;
1406 /* intersection of "have" and "need" ranges must not be empty */
1407 assert(body_off
< i
->spec
->offset
+ i
->spec
->length
);
1408 assert(body_off
+ *size
> i
->spec
->offset
);
1410 /* put boundary and headers at the beginning of a range in a multi-range */
1411 if (http
->request
->range
->specs
.count
> 1 && i
->debt_size
== i
->spec
->length
) {
1412 assert(http
->entry
->mem_obj
);
1414 http
->entry
->mem_obj
->reply
, /* original reply */
1415 i
->spec
, /* current range */
1416 i
->boundary
, /* boundary, the same for all */
1421 /* append content */
1422 debug(33, 3) ("clientPackRange: appending %d bytes\n", copy_sz
);
1423 memBufAppend(mb
, *buf
, copy_sz
);
1425 /* update offsets */
1427 i
->debt_size
-= copy_sz
;
1428 body_off
+= copy_sz
;
1430 http
->out
.offset
= body_off
+ i
->prefix_size
; /* sync */
1432 /* paranoid check */
1433 assert(*size
>= 0 && i
->debt_size
>= 0);
1436 /* returns true if there is still data available to pack more ranges
1437 * increments iterator "i"
1438 * used by clientPackMoreRanges */
1440 clientCanPackMoreRanges(const clientHttpRequest
* http
, HttpHdrRangeIter
* i
, ssize_t size
)
1442 /* first update "i" if needed */
1443 if (!i
->debt_size
) {
1444 if ((i
->spec
= httpHdrRangeGetSpec(http
->request
->range
, &i
->pos
)))
1445 i
->debt_size
= i
->spec
->length
;
1447 assert(!i
->debt_size
== !i
->spec
); /* paranoid sync condition */
1448 /* continue condition: need_more_data && have_more_data */
1449 return i
->spec
&& size
> 0;
1452 /* extracts "ranges" from buf and appends them to mb, updating all offsets and such */
1453 /* returns true if we need more data */
1455 clientPackMoreRanges(clientHttpRequest
* http
, const char *buf
, ssize_t size
, MemBuf
* mb
)
1457 HttpHdrRangeIter
*i
= &http
->range_iter
;
1458 /* offset in range specs does not count the prefix of an http msg */
1459 off_t body_off
= http
->out
.offset
- i
->prefix_size
;
1461 /* check: reply was parsed and range iterator was initialized */
1462 assert(i
->prefix_size
> 0);
1463 /* filter out data according to range specs */
1464 while (clientCanPackMoreRanges(http
, i
, size
)) {
1465 off_t start
; /* offset of still missing data */
1467 start
= i
->spec
->offset
+ i
->spec
->length
- i
->debt_size
;
1468 debug(33, 2) ("clientPackMoreRanges: in: offset: %d size: %d\n",
1469 (int) body_off
, size
);
1470 debug(33, 2) ("clientPackMoreRanges: out: start: %d spec[%d]: [%d, %d), len: %d debt: %d\n",
1471 (int) start
, (int) i
->pos
, i
->spec
->offset
, (int) (i
->spec
->offset
+ i
->spec
->length
), i
->spec
->length
, i
->debt_size
);
1472 assert(body_off
<= start
); /* we did not miss it */
1473 /* skip up to start */
1474 if (body_off
+ size
> start
) {
1475 const size_t skip_size
= start
- body_off
;
1480 /* has not reached start yet */
1485 /* put next chunk if any */
1487 http
->out
.offset
= body_off
+ i
->prefix_size
; /* sync */
1488 clientPackRange(http
, i
, &buf
, &size
, mb
);
1489 body_off
= http
->out
.offset
- i
->prefix_size
; /* sync */
1492 assert(!i
->debt_size
== !i
->spec
); /* paranoid sync condition */
1493 debug(33, 2) ("clientPackMoreRanges: buf exhausted: in: offset: %d size: %d need_more: %d\n",
1494 (int) body_off
, size
, i
->debt_size
);
1496 debug(33, 2) ("clientPackMoreRanges: need more: spec[%d]: [%d, %d), len: %d\n",
1497 (int) i
->pos
, i
->spec
->offset
, (int) (i
->spec
->offset
+ i
->spec
->length
), i
->spec
->length
);
1498 /* skip the data we do not need if possible */
1499 if (i
->debt_size
== i
->spec
->length
) /* at the start of the cur. spec */
1500 body_off
= i
->spec
->offset
;
1502 assert(body_off
== i
->spec
->offset
+ i
->spec
->length
- i
->debt_size
);
1503 } else if (http
->request
->range
->specs
.count
> 1) {
1504 /* put terminating boundary for multiparts */
1505 clientPackTermBound(i
->boundary
, mb
);
1507 http
->out
.offset
= body_off
+ i
->prefix_size
; /* sync */
1508 return i
->debt_size
> 0;
1512 * accepts chunk of a http message in buf, parses prefix, filters headers and
1513 * such, writes processed message to the client's socket
1516 clientSendMoreData(void *data
, char *buf
, ssize_t size
)
1518 clientHttpRequest
*http
= data
;
1519 StoreEntry
*entry
= http
->entry
;
1520 ConnStateData
*conn
= http
->conn
;
1522 HttpReply
*rep
= NULL
;
1523 const char *body_buf
= buf
;
1524 ssize_t body_size
= size
;
1526 ssize_t check_size
= 0;
1527 debug(33, 5) ("clientSendMoreData: %s, %d bytes\n", http
->uri
, (int) size
);
1528 assert(size
<= CLIENT_SOCK_SZ
);
1529 assert(http
->request
!= NULL
);
1530 dlinkDelete(&http
->active
, &ClientActiveRequests
);
1531 dlinkAdd(http
, &http
->active
, &ClientActiveRequests
);
1532 debug(33, 5) ("clientSendMoreData: FD %d '%s', out.offset=%d \n",
1533 fd
, storeUrl(entry
), (int) http
->out
.offset
);
1534 if (conn
->chr
!= http
) {
1535 /* there is another object in progress, defer this one */
1536 debug(33, 1) ("clientSendMoreData: Deferring %s\n", storeUrl(entry
));
1537 memFree(buf
, MEM_CLIENT_SOCK_BUF
);
1539 } else if (entry
&& EBIT_TEST(entry
->flags
, ENTRY_ABORTED
)) {
1540 /* call clientWriteComplete so the client socket gets closed */
1541 clientWriteComplete(fd
, NULL
, 0, COMM_OK
, http
);
1542 memFree(buf
, MEM_CLIENT_SOCK_BUF
);
1544 } else if (size
< 0) {
1545 /* call clientWriteComplete so the client socket gets closed */
1546 clientWriteComplete(fd
, NULL
, 0, COMM_OK
, http
);
1547 memFree(buf
, MEM_CLIENT_SOCK_BUF
);
1549 } else if (size
== 0) {
1550 /* call clientWriteComplete so the client socket gets closed */
1551 clientWriteComplete(fd
, NULL
, 0, COMM_OK
, http
);
1552 memFree(buf
, MEM_CLIENT_SOCK_BUF
);
1555 if (http
->out
.offset
== 0) {
1556 if (Config
.onoff
.log_mime_hdrs
) {
1558 if ((k
= headersEnd(buf
, size
))) {
1559 safe_free(http
->al
.headers
.reply
);
1560 http
->al
.headers
.reply
= xcalloc(k
+ 1, 1);
1561 xstrncpy(http
->al
.headers
.reply
, buf
, k
);
1564 rep
= clientBuildReply(http
, buf
, size
);
1566 body_size
= size
- rep
->hdr_sz
;
1567 assert(body_size
>= 0);
1568 body_buf
= buf
+ rep
->hdr_sz
;
1569 http
->range_iter
.prefix_size
= rep
->hdr_sz
;
1570 debug(33, 3) ("clientSendMoreData: Appending %d bytes after %d bytes of headers\n",
1571 body_size
, rep
->hdr_sz
);
1572 } else if (size
< CLIENT_SOCK_SZ
&& entry
->store_status
== STORE_PENDING
) {
1573 /* wait for more to arrive */
1574 storeClientCopy(entry
,
1575 http
->out
.offset
+ size
,
1583 /* reset range iterator */
1584 http
->range_iter
.pos
= HttpHdrRangeInitPos
;
1586 if (http
->request
->method
== METHOD_HEAD
) {
1588 /* do not forward body for HEAD replies */
1590 http
->flags
.done_copying
= 1;
1593 * If we are here, then store_status == STORE_OK and it
1594 * seems we have a HEAD repsponse which is missing the
1595 * empty end-of-headers line (home.mira.net, phttpd/0.99.72
1596 * does this). Because clientBuildReply() fails we just
1597 * call this reply a body, set the done_copying flag and
1600 http
->flags
.done_copying
= 1;
1603 /* write headers and/or body if any */
1604 assert(rep
|| (body_buf
&& body_size
));
1605 /* init mb; put status line and headers if any */
1607 mb
= httpReplyPack(rep
);
1608 http
->out
.offset
+= rep
->hdr_sz
;
1609 check_size
+= rep
->hdr_sz
;
1610 httpReplyDestroy(rep
);
1615 /* append body if any */
1616 if (http
->request
->range
) {
1617 /* Only GET requests should have ranges */
1618 assert(http
->request
->method
== METHOD_GET
);
1619 /* clientPackMoreRanges() updates http->out.offset */
1620 /* force the end of the transfer if we are done */
1621 if (!clientPackMoreRanges(http
, body_buf
, body_size
, &mb
))
1622 http
->flags
.done_copying
= 1;
1623 } else if (body_buf
&& body_size
) {
1624 http
->out
.offset
+= body_size
;
1625 check_size
+= body_size
;
1626 memBufAppend(&mb
, body_buf
, body_size
);
1628 if (!http
->request
->range
&& http
->request
->method
== METHOD_GET
)
1629 assert(check_size
== size
);
1631 comm_write_mbuf(fd
, mb
, clientWriteComplete
, http
);
1632 /* if we don't do it, who will? */
1633 memFree(buf
, MEM_CLIENT_SOCK_BUF
);
1637 clientKeepaliveNextRequest(clientHttpRequest
* http
)
1639 ConnStateData
*conn
= http
->conn
;
1641 debug(33, 3) ("clientKeepaliveNextRequest: FD %d\n", conn
->fd
);
1642 conn
->defer
.until
= 0; /* Kick it to read a new request */
1643 httpRequestFree(http
);
1644 if ((http
= conn
->chr
) == NULL
) {
1645 debug(33, 5) ("clientKeepaliveNextRequest: FD %d reading next req\n",
1647 fd_note(conn
->fd
, "Reading next request");
1649 * Set the timeout BEFORE calling clientReadRequest().
1651 commSetTimeout(conn
->fd
, 15, requestTimeout
, conn
);
1652 clientReadRequest(conn
->fd
, conn
); /* Read next request */
1654 * Note, the FD may be closed at this point.
1656 } else if ((entry
= http
->entry
) == NULL
) {
1658 * this request is in progress, maybe doing an ACL or a redirect,
1659 * execution will resume after the operation completes.
1662 debug(33, 1) ("clientKeepaliveNextRequest: FD %d Sending next\n",
1665 if (0 == storeClientCopyPending(entry
, http
)) {
1666 if (EBIT_TEST(entry
->flags
, ENTRY_ABORTED
))
1667 debug(33, 0) ("clientKeepaliveNextRequest: ENTRY_ABORTED\n");
1668 storeClientCopy(entry
,
1672 memAllocate(MEM_CLIENT_SOCK_BUF
),
1680 clientWriteComplete(int fd
, char *bufnotused
, size_t size
, int errflag
, void *data
)
1682 clientHttpRequest
*http
= data
;
1683 StoreEntry
*entry
= http
->entry
;
1685 http
->out
.size
+= size
;
1686 debug(33, 5) ("clientWriteComplete: FD %d, sz %d, err %d, off %d, len %d\n",
1687 fd
, size
, errflag
, (int) http
->out
.offset
, entry
? objectLen(entry
) : 0);
1689 kb_incr(&Counter
.client_http
.kbytes_out
, size
);
1690 if (isTcpHit(http
->log_type
))
1691 kb_incr(&Counter
.client_http
.hit_kbytes_out
, size
);
1695 * just close the socket, httpRequestFree will abort if needed
1698 } else if (NULL
== entry
) {
1699 comm_close(fd
); /* yuk */
1700 } else if (EBIT_TEST(entry
->flags
, ENTRY_ABORTED
)) {
1702 } else if ((done
= clientCheckTransferDone(http
)) != 0 || size
== 0) {
1703 debug(33, 5) ("clientWriteComplete: FD %d transfer is DONE\n", fd
);
1704 /* We're finished case */
1705 if (http
->entry
->mem_obj
->reply
->content_length
< 0) {
1706 debug(33, 5) ("clientWriteComplete: closing, content_length < 0\n");
1709 debug(33, 5) ("clientWriteComplete: closing, !done\n");
1711 } else if (clientGotNotEnough(http
)) {
1712 debug(33, 5) ("clientWriteComplete: client didn't get all it expected\n");
1714 } else if (http
->request
->flags
.proxy_keepalive
) {
1715 debug(33, 5) ("clientWriteComplete: FD %d Keeping Alive\n", fd
);
1716 clientKeepaliveNextRequest(http
);
1721 /* More data will be coming from primary server; register with
1722 * storage manager. */
1723 if (EBIT_TEST(entry
->flags
, ENTRY_ABORTED
))
1724 debug(33, 0) ("clientWriteComplete 2: ENTRY_ABORTED\n");
1725 storeClientCopy(entry
,
1729 memAllocate(MEM_CLIENT_SOCK_BUF
),
1736 * client issued a request with an only-if-cached cache-control directive;
1737 * we did not find a cached object that can be returned without
1738 * contacting other servers;
1739 * respond with a 504 (Gateway Timeout) as suggested in [RFC 2068]
1742 clientProcessOnlyIfCachedMiss(clientHttpRequest
* http
)
1744 char *url
= http
->uri
;
1745 request_t
*r
= http
->request
;
1746 ErrorState
*err
= NULL
;
1747 debug(33, 4) ("clientProcessOnlyIfCachedMiss: '%s %s'\n",
1748 RequestMethodStr
[r
->method
], url
);
1749 http
->al
.http
.code
= HTTP_GATEWAY_TIMEOUT
;
1750 err
= errorCon(ERR_ONLY_IF_CACHED_MISS
, HTTP_GATEWAY_TIMEOUT
);
1751 err
->request
= requestLink(r
);
1752 err
->src_addr
= http
->conn
->peer
.sin_addr
;
1754 storeUnregister(http
->entry
, http
);
1755 storeUnlockObject(http
->entry
);
1757 http
->entry
= clientCreateStoreEntry(http
, r
->method
, null_request_flags
);
1758 errorAppendEntry(http
->entry
, err
);
1762 clientProcessRequest2(clientHttpRequest
* http
)
1764 request_t
*r
= http
->request
;
1766 e
= http
->entry
= storeGetPublic(http
->uri
, r
->method
);
1767 if (r
->method
== METHOD_HEAD
&& e
== NULL
) {
1768 /* We can generate a HEAD reply from a cached GET object */
1769 e
= http
->entry
= storeGetPublic(http
->uri
, METHOD_GET
);
1771 #if USE_CACHE_DIGESTS
1772 http
->lookup_type
= e
? "HIT" : "MISS";
1775 /* this object isn't in the cache */
1776 debug(33, 3) ("clientProcessRequest2: storeGet() MISS\n");
1777 return LOG_TCP_MISS
;
1779 if (Config
.onoff
.offline
) {
1780 debug(33, 3) ("clientProcessRequest2: offline HIT\n");
1784 if (!storeEntryValidToSend(e
)) {
1785 debug(33, 3) ("clientProcessRequest2: !storeEntryValidToSend MISS\n");
1787 return LOG_TCP_MISS
;
1789 if (EBIT_TEST(e
->flags
, ENTRY_SPECIAL
)) {
1790 /* Special entries are always hits, no matter what the client says */
1791 debug(33, 3) ("clientProcessRequest2: ENTRY_SPECIAL HIT\n");
1796 if (r
->flags
.nocache_hack
) {
1797 /* if nocache_hack is set, nocache should always be clear, right? */
1798 assert(!r
->flags
.nocache
);
1799 ipcacheReleaseInvalid(r
->host
);
1802 if (e
->store_status
== STORE_PENDING
) {
1803 if (r
->flags
.nocache
|| r
->flags
.nocache_hack
) {
1804 debug(33, 3) ("Clearing no-cache for STORE_PENDING request\n\t%s\n",
1806 r
->flags
.nocache
= 0;
1807 r
->flags
.nocache_hack
= 0;
1811 if (r
->flags
.nocache
) {
1812 debug(33, 3) ("clientProcessRequest2: no-cache REFRESH MISS\n");
1814 ipcacheReleaseInvalid(r
->host
);
1815 return LOG_TCP_CLIENT_REFRESH_MISS
;
1817 if (r
->range
&& httpHdrRangeWillBeComplex(r
->range
)) {
1819 * Some clients break if we return "200 OK" for a Range
1820 * request. We would have to return "200 OK" for a _complex_
1821 * Range request that is also a HIT. Thus, let's prevent HITs
1822 * on complex Range requests
1824 debug(33, 3) ("clientProcessRequest2: complex range MISS\n");
1826 return LOG_TCP_MISS
;
1828 debug(33, 3) ("clientProcessRequest2: default HIT\n");
1834 clientProcessRequest(clientHttpRequest
* http
)
1836 char *url
= http
->uri
;
1837 request_t
*r
= http
->request
;
1838 int fd
= http
->conn
->fd
;
1840 debug(33, 4) ("clientProcessRequest: %s '%s'\n",
1841 RequestMethodStr
[r
->method
],
1843 if (r
->method
== METHOD_CONNECT
) {
1844 http
->log_type
= LOG_TCP_MISS
;
1845 sslStart(fd
, url
, r
, &http
->out
.size
);
1847 } else if (r
->method
== METHOD_PURGE
) {
1848 clientPurgeRequest(http
);
1850 } else if (r
->method
== METHOD_TRACE
) {
1851 if (r
->max_forwards
== 0) {
1852 http
->entry
= clientCreateStoreEntry(http
, r
->method
, null_request_flags
);
1853 storeReleaseRequest(http
->entry
);
1854 storeBuffer(http
->entry
);
1855 rep
= httpReplyCreate();
1856 httpReplySetHeaders(rep
, 1.0, HTTP_OK
, NULL
, "text/plain",
1857 httpRequestPrefixLen(r
), 0, squid_curtime
);
1858 httpReplySwapOut(rep
, http
->entry
);
1859 httpReplyDestroy(rep
);
1860 httpRequestSwapOut(r
, http
->entry
);
1861 storeComplete(http
->entry
);
1865 http
->log_type
= LOG_TCP_MISS
;
1866 } else if (pumpMethod(r
->method
)) {
1867 http
->log_type
= LOG_TCP_MISS
;
1868 /* XXX oof, POST can be cached! */
1869 pumpInit(fd
, r
, http
->uri
);
1871 http
->log_type
= clientProcessRequest2(http
);
1873 debug(33, 4) ("clientProcessRequest: %s for '%s'\n",
1874 log_tags
[http
->log_type
],
1876 http
->out
.offset
= 0;
1877 if (NULL
!= http
->entry
) {
1878 storeLockObject(http
->entry
);
1879 storeCreateMemObject(http
->entry
, http
->uri
, http
->log_uri
);
1880 storeClientListAdd(http
->entry
, http
);
1882 delaySetStoreClient(http
->entry
, http
, delayClient(r
));
1884 http
->entry
->refcount
++;
1885 storeClientCopy(http
->entry
,
1889 memAllocate(MEM_CLIENT_SOCK_BUF
),
1894 http
->log_type
= LOG_TCP_MISS
;
1895 clientProcessMiss(http
);
1900 * Prepare to fetch the object as it's a cache miss of some kind.
1903 clientProcessMiss(clientHttpRequest
* http
)
1905 char *url
= http
->uri
;
1906 request_t
*r
= http
->request
;
1907 ErrorState
*err
= NULL
;
1908 debug(33, 4) ("clientProcessMiss: '%s %s'\n",
1909 RequestMethodStr
[r
->method
], url
);
1911 * We might have a left-over StoreEntry from a failed cache hit
1915 if (EBIT_TEST(http
->entry
->flags
, ENTRY_SPECIAL
))
1916 debug(33, 0) ("clientProcessMiss: miss on a special object (%s).\n", url
);
1917 storeUnregister(http
->entry
, http
);
1918 storeUnlockObject(http
->entry
);
1921 if (clientOnlyIfCached(http
)) {
1922 clientProcessOnlyIfCachedMiss(http
);
1926 * Deny loops when running in accelerator/transproxy mode.
1928 if (http
->flags
.accel
&& r
->flags
.loopdetect
) {
1929 http
->al
.http
.code
= HTTP_FORBIDDEN
;
1930 err
= errorCon(ERR_ACCESS_DENIED
, HTTP_FORBIDDEN
);
1931 err
->request
= requestLink(r
);
1932 err
->src_addr
= http
->conn
->peer
.sin_addr
;
1933 http
->entry
= clientCreateStoreEntry(http
, r
->method
, null_request_flags
);
1934 errorAppendEntry(http
->entry
, err
);
1937 assert(http
->out
.offset
== 0);
1938 http
->entry
= clientCreateStoreEntry(http
, r
->method
, r
->flags
);
1939 http
->entry
->refcount
++;
1940 if (http
->redirect
.status
) {
1941 HttpReply
*rep
= httpReplyCreate();
1942 storeReleaseRequest(http
->entry
);
1943 httpRedirectReply(rep
, http
->redirect
.status
, http
->redirect
.location
);
1944 httpReplySwapOut(rep
, http
->entry
);
1945 httpReplyDestroy(rep
);
1946 storeComplete(http
->entry
);
1949 if (http
->flags
.internal
)
1950 r
->protocol
= PROTO_INTERNAL
;
1951 fwdStart(http
->conn
->fd
, http
->entry
, r
,
1952 http
->conn
->peer
.sin_addr
, http
->conn
->me
.sin_addr
);
1955 static clientHttpRequest
*
1956 parseHttpRequestAbort(ConnStateData
* conn
, const char *uri
)
1958 clientHttpRequest
*http
= xcalloc(1, sizeof(clientHttpRequest
));
1959 cbdataAdd(http
, cbdataXfree
, 0);
1961 http
->start
= current_time
;
1962 http
->req_sz
= conn
->in
.offset
;
1963 http
->uri
= xstrdup(uri
);
1964 http
->log_uri
= xstrndup(uri
, MAX_URL
);
1965 http
->range_iter
.boundary
= StringNull
;
1966 dlinkAdd(http
, &http
->active
, &ClientActiveRequests
);
1971 * parseHttpRequest()
1974 * NULL on error or incomplete request
1975 * a clientHttpRequest structure on success
1977 static clientHttpRequest
*
1978 parseHttpRequest(ConnStateData
* conn
, method_t
* method_p
, int *status
,
1979 char **prefix_p
, size_t * req_line_sz_p
)
1984 char *req_hdr
= NULL
;
1989 int free_request
= 0;
1990 size_t header_sz
; /* size of headers, not including first line */
1991 size_t prefix_sz
; /* size of whole request (req-line + headers) */
1995 clientHttpRequest
*http
= NULL
;
1997 struct natlookup natLookup
;
1998 static int natfd
= -1;
2001 if ((req_sz
= headersEnd(conn
->in
.buf
, conn
->in
.offset
)) == 0) {
2002 debug(33, 5) ("Incomplete request, waiting for end of headers\n");
2005 *method_p
= METHOD_NONE
;
2008 assert(req_sz
<= conn
->in
.offset
);
2009 /* Use memcpy, not strdup! */
2010 inbuf
= xmalloc(req_sz
+ 1);
2011 xmemcpy(inbuf
, conn
->in
.buf
, req_sz
);
2012 *(inbuf
+ req_sz
) = '\0';
2014 /* pre-set these values to make aborting simpler */
2016 *method_p
= METHOD_NONE
;
2019 /* Barf on NULL characters in the headers */
2020 if (strlen(inbuf
) != req_sz
) {
2021 debug(33, 1) ("parseHttpRequest: Requestheader contains NULL characters\n");
2022 return parseHttpRequestAbort(conn
, "error:invalid-request");
2024 /* Look for request method */
2025 if ((mstr
= strtok(inbuf
, "\t ")) == NULL
) {
2026 debug(33, 1) ("parseHttpRequest: Can't get request method\n");
2027 return parseHttpRequestAbort(conn
, "error:invalid-request-method");
2029 method
= urlParseMethod(mstr
);
2030 if (method
== METHOD_NONE
) {
2031 debug(33, 1) ("parseHttpRequest: Unsupported method '%s'\n", mstr
);
2032 return parseHttpRequestAbort(conn
, "error:unsupported-request-method");
2034 debug(33, 5) ("parseHttpRequest: Method is '%s'\n", mstr
);
2037 /* look for URL+HTTP/x.x */
2038 if ((url
= strtok(NULL
, "\n")) == NULL
) {
2039 debug(33, 1) ("parseHttpRequest: Missing URL\n");
2040 return parseHttpRequestAbort(conn
, "error:missing-url");
2042 while (xisspace(*url
))
2044 t
= url
+ strlen(url
);
2049 if (xisspace(*t
) && !strncmp(t
+ 1, "HTTP/", 5)) {
2054 while (t
> url
&& xisspace(*t
))
2056 debug(33, 5) ("parseHttpRequest: URI is '%s'\n", url
);
2057 if (token
== NULL
) {
2058 debug(33, 3) ("parseHttpRequest: Missing HTTP identifier\n");
2059 #if RELAXED_HTTP_PARSER
2060 http_ver
= (float) 0.9; /* wild guess */
2062 return parseHttpRequestAbort(conn
, "error:missing-http-ident");
2065 http_ver
= (float) atof(token
+ 5);
2069 * Process headers after request line
2071 req_hdr
= strtok(NULL
, null_string
);
2072 header_sz
= req_sz
- (req_hdr
- inbuf
);
2073 if (0 == header_sz
) {
2074 debug(33, 3) ("parseHttpRequest: header_sz == 0\n");
2078 assert(header_sz
> 0);
2079 debug(33, 3) ("parseHttpRequest: req_hdr = {%s}\n", req_hdr
);
2080 end
= req_hdr
+ header_sz
;
2081 debug(33, 3) ("parseHttpRequest: end = {%s}\n", end
);
2083 prefix_sz
= end
- inbuf
;
2084 *req_line_sz_p
= req_hdr
- inbuf
;
2085 debug(33, 3) ("parseHttpRequest: prefix_sz = %d, req_line_sz = %d\n",
2086 (int) prefix_sz
, (int) *req_line_sz_p
);
2087 assert(prefix_sz
<= conn
->in
.offset
);
2089 /* Ok, all headers are received */
2090 http
= xcalloc(1, sizeof(clientHttpRequest
));
2091 cbdataAdd(http
, cbdataXfree
, 0);
2092 http
->http_ver
= http_ver
;
2094 http
->start
= current_time
;
2095 http
->req_sz
= prefix_sz
;
2096 http
->range_iter
.boundary
= StringNull
;
2097 *prefix_p
= xmalloc(prefix_sz
+ 1);
2098 xmemcpy(*prefix_p
, conn
->in
.buf
, prefix_sz
);
2099 *(*prefix_p
+ prefix_sz
) = '\0';
2100 dlinkAdd(http
, &http
->active
, &ClientActiveRequests
);
2102 debug(33, 5) ("parseHttpRequest: Request Header is\n%s\n", (*prefix_p
) + *req_line_sz_p
);
2103 if ((t
= strchr(url
, '#'))) /* remove HTML anchors */
2106 /* handle internal objects */
2107 if (internalCheck(url
)) {
2108 /* prepend our name & port */
2109 http
->uri
= xstrdup(internalLocalUri(NULL
, url
));
2110 http
->flags
.internal
= 1;
2111 http
->flags
.accel
= 1;
2113 /* see if we running in Config2.Accel.on, if so got to convert it to URL */
2114 else if (Config2
.Accel
.on
&& *url
== '/') {
2115 /* prepend the accel prefix */
2116 if (opt_accel_uses_host
&& (t
= mime_get_header(req_hdr
, "Host"))) {
2117 /* If a Host: header was specified, use it to build the URL
2118 * instead of the one in the Config file. */
2120 * XXX Use of the Host: header here opens a potential
2121 * security hole. There are no checks that the Host: value
2122 * corresponds to one of your servers. It might, for example,
2123 * refer to www.playboy.com. The 'dst' and/or 'dst_domain' ACL
2124 * types should be used to prevent httpd-accelerators
2125 * handling requests for non-local servers */
2127 url_sz
= strlen(url
) + 32 + Config
.appendDomainLen
+
2129 http
->uri
= xcalloc(url_sz
, 1);
2130 snprintf(http
->uri
, url_sz
, "http://%s:%d%s",
2131 t
, (int) Config
.Accel
.port
, url
);
2132 } else if (vhost_mode
) {
2133 /* Put the local socket IP address as the hostname */
2134 url_sz
= strlen(url
) + 32 + Config
.appendDomainLen
;
2135 http
->uri
= xcalloc(url_sz
, 1);
2137 natLookup
.nl_inport
= http
->conn
->me
.sin_port
;
2138 natLookup
.nl_outport
= http
->conn
->peer
.sin_port
;
2139 natLookup
.nl_inip
= http
->conn
->me
.sin_addr
;
2140 natLookup
.nl_outip
= http
->conn
->peer
.sin_addr
;
2141 natLookup
.nl_flags
= IPN_TCP
;
2143 natfd
= open(IPL_NAT
, O_RDONLY
, 0);
2145 debug(50, 1) ("parseHttpRequest: NAT open failed: %s\n",
2147 return parseHttpRequestAbort(conn
, "error:nat-open-failed");
2149 if (ioctl(natfd
, SIOCGNATL
, &natLookup
) < 0) {
2150 if (errno
!= ESRCH
) {
2151 debug(50, 1) ("parseHttpRequest: NAT lookup failed: ioctl(SIOCGNATL)\n");
2154 return parseHttpRequestAbort(conn
, "error:nat-lookup-failed");
2156 snprintf(http
->uri
, url_sz
, "http://%s:%d%s",
2157 inet_ntoa(http
->conn
->me
.sin_addr
),
2158 (int) Config
.Accel
.port
,
2161 snprintf(http
->uri
, url_sz
, "http://%s:%d%s",
2162 inet_ntoa(natLookup
.nl_realip
),
2163 (int) Config
.Accel
.port
,
2166 snprintf(http
->uri
, url_sz
, "http://%s:%d%s",
2167 inet_ntoa(http
->conn
->me
.sin_addr
),
2168 (int) Config
.Accel
.port
,
2171 debug(33, 5) ("VHOST REWRITE: '%s'\n", http
->uri
);
2173 url_sz
= strlen(Config2
.Accel
.prefix
) + strlen(url
) +
2174 Config
.appendDomainLen
+ 1;
2175 http
->uri
= xcalloc(url_sz
, 1);
2176 snprintf(http
->uri
, url_sz
, "%s%s", Config2
.Accel
.prefix
, url
);
2178 http
->flags
.accel
= 1;
2180 /* URL may be rewritten later, so make extra room */
2181 url_sz
= strlen(url
) + Config
.appendDomainLen
+ 5;
2182 http
->uri
= xcalloc(url_sz
, 1);
2183 strcpy(http
->uri
, url
);
2184 http
->flags
.accel
= 0;
2186 if (!stringHasWhitespace(http
->uri
))
2187 http
->log_uri
= xstrndup(http
->uri
, MAX_URL
);
2189 http
->log_uri
= xstrndup(rfc1738_escape(http
->uri
), MAX_URL
);
2190 debug(33, 5) ("parseHttpRequest: Complete request received\n");
2199 clientReadDefer(int fdnotused
, void *data
)
2201 ConnStateData
*conn
= data
;
2202 return conn
->defer
.until
> squid_curtime
;
2206 clientReadRequest(int fd
, void *data
)
2208 ConnStateData
*conn
= data
;
2209 int parser_return_code
= 0;
2211 request_t
*request
= NULL
;
2214 clientHttpRequest
*http
= NULL
;
2215 clientHttpRequest
**H
= NULL
;
2216 char *prefix
= NULL
;
2217 ErrorState
*err
= NULL
;
2218 fde
*F
= &fd_table
[fd
];
2219 int len
= conn
->in
.size
- conn
->in
.offset
- 1;
2220 debug(33, 4) ("clientReadRequest: FD %d: reading request...\n", fd
);
2221 Counter
.syscalls
.sock
.reads
++;
2222 size
= read(fd
, conn
->in
.buf
+ conn
->in
.offset
, len
);
2224 fd_bytes(fd
, size
, FD_READ
);
2225 kb_incr(&Counter
.client_http
.kbytes_in
, size
);
2228 * Don't reset the timeout value here. The timeout value will be
2229 * set to Config.Timeout.request by httpAccept() and
2230 * clientWriteComplete(), and should apply to the request as a
2231 * whole, not individual read() calls. Plus, it breaks our
2232 * lame half-close detection
2234 commSetSelect(fd
, COMM_SELECT_READ
, clientReadRequest
, conn
, 0);
2236 if (conn
->chr
== NULL
) {
2237 /* no current or pending requests */
2240 } else if (!Config
.onoff
.half_closed_clients
) {
2241 /* admin doesn't want to support half-closed client sockets */
2245 /* It might be half-closed, we can't tell */
2246 debug(33, 5) ("clientReadRequest: FD %d closed?\n", fd
);
2247 F
->flags
.socket_eof
= 1;
2248 conn
->defer
.until
= squid_curtime
+ 1;
2250 fd_note(fd
, "half-closed");
2252 } else if (size
< 0) {
2253 if (!ignoreErrno(errno
)) {
2254 debug(50, 2) ("clientReadRequest: FD %d: %s\n", fd
, xstrerror());
2257 } else if (conn
->in
.offset
== 0) {
2258 debug(50, 2) ("clientReadRequest: FD %d: no data to process (%s)\n", fd
, xstrerror());
2261 /* Continue to process previously read data */
2264 conn
->in
.offset
+= size
;
2265 /* Skip leading (and trailing) whitespace */
2266 while (conn
->in
.offset
> 0) {
2269 while (conn
->in
.offset
> 0 && xisspace(conn
->in
.buf
[0])) {
2270 xmemmove(conn
->in
.buf
, conn
->in
.buf
+ 1, conn
->in
.offset
- 1);
2273 conn
->in
.buf
[conn
->in
.offset
] = '\0'; /* Terminate the string */
2274 if (conn
->in
.offset
== 0)
2276 /* Limit the number of concurrent requests to 2 */
2277 for (H
= &conn
->chr
, nrequests
= 0; *H
; H
= &(*H
)->next
, nrequests
++);
2278 if (nrequests
>= 2) {
2279 debug(33, 2) ("clientReadRequest: FD %d max concurrent requests reached\n", fd
);
2280 debug(33, 5) ("clientReadRequest: FD %d defering new request until one is done\n", fd
);
2281 conn
->defer
.until
= squid_curtime
+ 100; /* Reset when a request is complete */
2284 /* Process request */
2285 http
= parseHttpRequest(conn
,
2287 &parser_return_code
,
2293 assert(http
->req_sz
> 0);
2294 conn
->in
.offset
-= http
->req_sz
;
2295 assert(conn
->in
.offset
>= 0);
2296 debug(33, 5) ("conn->in.offset = %d\n", (int) conn
->in
.offset
);
2298 * If we read past the end of this request, move the remaining
2299 * data to the beginning
2301 if (conn
->in
.offset
> 0)
2302 xmemmove(conn
->in
.buf
, conn
->in
.buf
+ http
->req_sz
, conn
->in
.offset
);
2303 /* add to the client request queue */
2304 for (H
= &conn
->chr
; *H
; H
= &(*H
)->next
);
2307 commSetTimeout(fd
, Config
.Timeout
.lifetime
, NULL
, NULL
);
2308 if (parser_return_code
< 0) {
2309 debug(33, 1) ("clientReadRequest: FD %d Invalid Request\n", fd
);
2310 err
= errorCon(ERR_INVALID_REQ
, HTTP_BAD_REQUEST
);
2311 err
->request_hdrs
= xstrdup(conn
->in
.buf
);
2312 http
->entry
= clientCreateStoreEntry(http
, method
, null_request_flags
);
2313 errorAppendEntry(http
->entry
, err
);
2317 if ((request
= urlParse(method
, http
->uri
)) == NULL
) {
2318 debug(33, 5) ("Invalid URL: %s\n", http
->uri
);
2319 err
= errorCon(ERR_INVALID_URL
, HTTP_BAD_REQUEST
);
2320 err
->src_addr
= conn
->peer
.sin_addr
;
2321 err
->url
= xstrdup(http
->uri
);
2322 http
->al
.http
.code
= err
->http_status
;
2323 http
->entry
= clientCreateStoreEntry(http
, method
, null_request_flags
);
2324 errorAppendEntry(http
->entry
, err
);
2328 /* compile headers */
2329 /* we should skip request line! */
2330 if (!httpRequestParseHeader(request
, prefix
+ req_line_sz
))
2331 debug(33, 1) ("Failed to parse request headers: %s\n%s\n",
2333 /* continue anyway? */
2335 request
->flags
.accelerated
= http
->flags
.accel
;
2336 if (!http
->flags
.internal
) {
2337 if (internalCheck(strBuf(request
->urlpath
))) {
2338 if (0 == strcasecmp(request
->host
, internalHostname()) &&
2339 request
->port
== Config
.Port
.http
->i
) {
2340 http
->flags
.internal
= 1;
2341 } else if (internalStaticCheck(strBuf(request
->urlpath
))) {
2342 xstrncpy(request
->host
, internalHostname(), SQUIDHOSTNAMELEN
);
2343 request
->port
= Config
.Port
.http
->i
;
2344 http
->flags
.internal
= 1;
2348 request
->flags
.internal
= http
->flags
.internal
;
2350 safe_free(http
->log_uri
);
2351 http
->log_uri
= xstrdup(urlCanonicalClean(request
));
2352 request
->client_addr
= conn
->peer
.sin_addr
;
2353 request
->my_addr
= conn
->me
.sin_addr
;
2354 request
->http_ver
= http
->http_ver
;
2355 if (!urlCheckRequest(request
)) {
2356 err
= errorCon(ERR_UNSUP_REQ
, HTTP_NOT_IMPLEMENTED
);
2357 err
->src_addr
= conn
->peer
.sin_addr
;
2358 err
->request
= requestLink(request
);
2359 http
->al
.http
.code
= err
->http_status
;
2360 http
->entry
= clientCreateStoreEntry(http
, request
->method
, null_request_flags
);
2361 errorAppendEntry(http
->entry
, err
);
2364 if (0 == clientCheckContentLength(request
)) {
2365 err
= errorCon(ERR_INVALID_REQ
, HTTP_LENGTH_REQUIRED
);
2366 err
->src_addr
= conn
->peer
.sin_addr
;
2367 err
->request
= requestLink(request
);
2368 http
->al
.http
.code
= err
->http_status
;
2369 http
->entry
= clientCreateStoreEntry(http
, request
->method
, null_request_flags
);
2370 errorAppendEntry(http
->entry
, err
);
2373 http
->request
= requestLink(request
);
2375 * We need to set the keepalive flag before doing some
2376 * hacks for POST/PUT requests below. Maybe we could
2377 * set keepalive flag even earlier.
2379 clientSetKeepaliveFlag(http
);
2381 * break here for NON-GET because most likely there is a
2382 * reqeust body following and we don't want to parse it
2383 * as though it was new request
2385 if (request
->method
!= METHOD_GET
) {
2386 int cont_len
= httpHeaderGetInt(&request
->header
, HDR_CONTENT_LENGTH
);
2387 int copy_len
= XMIN(conn
->in
.offset
, cont_len
);
2389 assert(conn
->in
.offset
>= copy_len
);
2390 request
->body_sz
= copy_len
;
2391 request
->body
= xmalloc(request
->body_sz
);
2392 xmemcpy(request
->body
, conn
->in
.buf
, request
->body_sz
);
2393 conn
->in
.offset
-= copy_len
;
2394 if (conn
->in
.offset
)
2395 xmemmove(conn
->in
.buf
, conn
->in
.buf
+ copy_len
, conn
->in
.offset
);
2398 * if we didn't get the full body now, then more will
2399 * be arriving on the client socket. Lets cancel
2400 * the read handler until this request gets forwarded.
2402 if (request
->body_sz
< cont_len
)
2403 commSetSelect(fd
, COMM_SELECT_READ
, NULL
, NULL
, 0);
2405 clientAccessCheck(http
);
2406 continue; /* while offset > 0 */
2407 } else if (parser_return_code
== 0) {
2409 * Partial request received; reschedule until parseHttpRequest()
2410 * is happy with the input
2412 k
= conn
->in
.size
- 1 - conn
->in
.offset
;
2414 if (conn
->in
.offset
>= Config
.maxRequestSize
) {
2415 /* The request is too large to handle */
2416 debug(33, 0) ("Request won't fit in buffer.\n");
2417 debug(33, 0) ("Config 'request_size'= %d bytes.\n",
2418 Config
.maxRequestSize
);
2419 debug(33, 0) ("This request = %d bytes.\n",
2420 (int) conn
->in
.offset
);
2421 err
= errorCon(ERR_INVALID_REQ
, HTTP_REQUEST_ENTITY_TOO_LARGE
);
2422 http
= parseHttpRequestAbort(conn
, "error:request-too-large");
2423 /* add to the client request queue */
2424 for (H
= &conn
->chr
; *H
; H
= &(*H
)->next
);
2426 http
->entry
= clientCreateStoreEntry(http
, METHOD_NONE
, null_request_flags
);
2427 errorAppendEntry(http
->entry
, err
);
2430 /* Grow the request memory area to accomodate for a large request */
2431 conn
->in
.size
+= REQUEST_BUF_SIZE
;
2432 conn
->in
.buf
= xrealloc(conn
->in
.buf
, conn
->in
.size
);
2433 /* XXX account conn->in.buf */
2434 debug(33, 2) ("Handling a large request, offset=%d inbufsize=%d\n",
2435 (int) conn
->in
.offset
, conn
->in
.size
);
2436 k
= conn
->in
.size
- 1 - conn
->in
.offset
;
2443 /* general lifetime handler for HTTP requests */
2445 requestTimeout(int fd
, void *data
)
2447 ConnStateData
*conn
= data
;
2449 debug(33, 2) ("requestTimeout: FD %d: lifetime is expired.\n", fd
);
2450 if (fd_table
[fd
].rwstate
) {
2452 * Some data has been sent to the client, just close the FD
2455 } else if (conn
->nrequests
) {
2457 * assume its a persistent connection; just close it
2464 err
= errorCon(ERR_LIFETIME_EXP
, HTTP_REQUEST_TIMEOUT
);
2465 err
->url
= xstrdup("N/A");
2467 * Normally we shouldn't call errorSend() in client_side.c, but
2468 * it should be okay in this case. Presumably if we get here
2469 * this is the first request for the connection, and no data
2470 * has been written yet
2472 assert(conn
->chr
== NULL
);
2475 * if we don't close() here, we still need a timeout handler!
2477 commSetTimeout(fd
, 30, requestTimeout
, conn
);
2482 httpAcceptDefer(void)
2484 static time_t last_warn
= 0;
2485 if (fdNFree() >= RESERVED_FD
)
2487 if (last_warn
+ 15 < squid_curtime
) {
2488 debug(33, 0) ("WARNING! Your cache is running out of filedescriptors\n");
2489 last_warn
= squid_curtime
;
2494 /* Handle a new connection on HTTP socket. */
2496 httpAccept(int sock
, void *data
)
2500 ConnStateData
*connState
= NULL
;
2501 struct sockaddr_in peer
;
2502 struct sockaddr_in me
;
2503 int max
= INCOMING_HTTP_MAX
;
2505 static aclCheck_t identChecklist
;
2507 commSetSelect(sock
, COMM_SELECT_READ
, httpAccept
, NULL
, 0);
2508 while (max
-- && !httpAcceptDefer()) {
2509 memset(&peer
, '\0', sizeof(struct sockaddr_in
));
2510 memset(&me
, '\0', sizeof(struct sockaddr_in
));
2511 if ((fd
= comm_accept(sock
, &peer
, &me
)) < 0) {
2512 if (!ignoreErrno(errno
))
2513 debug(50, 1) ("httpAccept: FD %d: accept failure: %s\n",
2517 debug(33, 4) ("httpAccept: FD %d: accepted\n", fd
);
2518 connState
= xcalloc(1, sizeof(ConnStateData
));
2519 connState
->peer
= peer
;
2520 connState
->log_addr
= peer
.sin_addr
;
2521 connState
->log_addr
.s_addr
&= Config
.Addrs
.client_netmask
.s_addr
;
2524 connState
->in
.size
= REQUEST_BUF_SIZE
;
2525 connState
->in
.buf
= xcalloc(connState
->in
.size
, 1);
2526 cbdataAdd(connState
, cbdataXfree
, 0);
2527 /* XXX account connState->in.buf */
2528 comm_add_close_handler(fd
, connStateFree
, connState
);
2529 if (Config
.onoff
.log_fqdn
)
2530 fqdncache_gethostbyaddr(peer
.sin_addr
, FQDN_LOOKUP_IF_MISS
);
2531 commSetTimeout(fd
, Config
.Timeout
.request
, requestTimeout
, connState
);
2533 identChecklist
.src_addr
= peer
.sin_addr
;
2534 identChecklist
.my_addr
= me
.sin_addr
;
2535 if (aclCheckFast(Config
.accessList
.identLookup
, &identChecklist
))
2536 identStart(&me
, &peer
, clientIdentDone
, connState
);
2538 commSetSelect(fd
, COMM_SELECT_READ
, clientReadRequest
, connState
, 0);
2539 commSetDefer(fd
, clientReadDefer
, connState
);
2544 #define SENDING_BODY 0
2545 #define SENDING_HDRSONLY 1
2547 clientCheckTransferDone(clientHttpRequest
* http
)
2549 int sending
= SENDING_BODY
;
2550 StoreEntry
*entry
= http
->entry
;
2557 * For now, 'done_copying' is used for special cases like
2558 * Range and HEAD requests.
2560 if (http
->flags
.done_copying
)
2563 * Handle STORE_OK objects.
2564 * objectLen(entry) will be set proprely.
2566 if (entry
->store_status
== STORE_OK
) {
2567 if (http
->out
.offset
>= objectLen(entry
))
2573 * Now, handle STORE_PENDING objects
2575 mem
= entry
->mem_obj
;
2576 assert(mem
!= NULL
);
2577 assert(http
->request
!= NULL
);
2579 if (reply
->hdr_sz
== 0)
2580 return 0; /* haven't found end of headers yet */
2581 else if (reply
->sline
.status
== HTTP_OK
)
2582 sending
= SENDING_BODY
;
2583 else if (reply
->sline
.status
== HTTP_NO_CONTENT
)
2584 sending
= SENDING_HDRSONLY
;
2585 else if (reply
->sline
.status
== HTTP_NOT_MODIFIED
)
2586 sending
= SENDING_HDRSONLY
;
2587 else if (reply
->sline
.status
< HTTP_OK
)
2588 sending
= SENDING_HDRSONLY
;
2589 else if (http
->request
->method
== METHOD_HEAD
)
2590 sending
= SENDING_HDRSONLY
;
2592 sending
= SENDING_BODY
;
2594 * Figure out how much data we are supposed to send.
2595 * If we are sending a body and we don't have a content-length,
2596 * then we must wait for the object to become STORE_OK.
2598 if (sending
== SENDING_HDRSONLY
)
2599 sendlen
= reply
->hdr_sz
;
2600 else if (reply
->content_length
< 0)
2603 sendlen
= reply
->content_length
+ reply
->hdr_sz
;
2605 * Now that we have the expected length, did we send it all?
2607 if (http
->out
.offset
< sendlen
)
2614 clientGotNotEnough(clientHttpRequest
* http
)
2616 int cl
= http
->entry
->mem_obj
->reply
->content_length
;
2617 int hs
= http
->entry
->mem_obj
->reply
->hdr_sz
;
2619 if (http
->out
.offset
< cl
+ hs
)
2625 * This function is designed to serve a fairly specific purpose.
2626 * Occasionally our vBNS-connected caches can talk to each other, but not
2627 * the rest of the world. Here we try to detect frequent failures which
2628 * make the cache unusable (e.g. DNS lookup and connect() failures). If
2629 * the failure:success ratio goes above 1.0 then we go into "hit only"
2630 * mode where we only return UDP_HIT or UDP_MISS_NOFETCH. Neighbors
2631 * will only fetch HITs from us if they are using the ICP protocol. We
2632 * stay in this mode for 5 minutes.
2634 * Duane W., Sept 16, 1996
2638 checkFailureRatio(err_type etype
, hier_code hcode
)
2640 static double magic_factor
= 100.0;
2643 if (hcode
== HIER_NONE
)
2645 n_good
= magic_factor
/ (1.0 + request_failure_ratio
);
2646 n_bad
= magic_factor
- n_good
;
2649 case ERR_CONNECT_FAIL
:
2650 case ERR_READ_ERROR
:
2656 request_failure_ratio
= n_bad
/ n_good
;
2657 if (hit_only_mode_until
> squid_curtime
)
2659 if (request_failure_ratio
< 1.0)
2661 debug(33, 0) ("Failure Ratio at %4.2f\n", request_failure_ratio
);
2662 debug(33, 0) ("Going into hit-only-mode for %d minutes...\n",
2663 FAILURE_MODE_TIME
/ 60);
2664 hit_only_mode_until
= squid_curtime
+ FAILURE_MODE_TIME
;
2665 request_failure_ratio
= 0.8; /* reset to something less than 1.0 */
2669 clientHttpConnectionsOpen(void)
2673 for (u
= Config
.Port
.http
; u
; u
= u
->next
) {
2675 fd
= comm_open(SOCK_STREAM
,
2677 Config
.Addrs
.tcp_incoming
,
2685 commSetSelect(fd
, COMM_SELECT_READ
, httpAccept
, NULL
, 0);
2686 /*commSetDefer(fd, httpAcceptDefer, NULL); */
2687 debug(1, 1) ("Accepting HTTP connections on port %d, FD %d.\n",
2689 HttpSockets
[NHttpSockets
++] = fd
;
2691 if (NHttpSockets
< 1)
2692 fatal("Cannot open HTTP Port");
2696 clientHttpConnectionsClose(void)
2699 for (i
= 0; i
< NHttpSockets
; i
++) {
2700 if (HttpSockets
[i
] >= 0) {
2701 debug(1, 1) ("FD %d Closing HTTP connection\n", HttpSockets
[i
]);
2702 comm_close(HttpSockets
[i
]);
2703 HttpSockets
[i
] = -1;