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