📄 client_side.c
字号:
/* * $Id: client_side.c,v 1.440.2.8 1999/05/10 16:00:40 wessels Exp $ * * DEBUG: section 33 Client-side Routines * AUTHOR: Duane Wessels * * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ * ---------------------------------------------------------- * * Squid is the result of efforts by numerous individuals from the * Internet community. Development is led by Duane Wessels of the * National Laboratory for Applied Network Research and funded by the * National Science Foundation. Squid is Copyrighted (C) 1998 by * Duane Wessels and the University of California San Diego. Please * see the COPYRIGHT file for full details. Squid incorporates * software developed and/or copyrighted by other sources. Please see * the CREDITS file for full details. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */#include "squid.h"#if IPF_TRANSPARENT#if HAVE_SYS_IOCTL_H#include <sys/ioctl.h>#endif#include <netinet/tcp.h>#include <net/if.h>#include <ip_compat.h>#include <ip_fil.h>#include <ip_nat.h>#endif#if LINGERING_CLOSE#define comm_close comm_lingering_close#endifstatic const char *const crlf = "\r\n";#define REQUEST_BUF_SIZE 4096#define FAILURE_MODE_TIME 300/* Local functions */static CWCB clientWriteComplete;static PF clientReadRequest;static PF connStateFree;static PF requestTimeout;static int clientCheckTransferDone(clientHttpRequest *);static int clientGotNotEnough(clientHttpRequest *);static void checkFailureRatio(err_type, hier_code);static void clientProcessMiss(clientHttpRequest *);static void clientBuildReplyHeader(clientHttpRequest * http, HttpReply * rep);static clientHttpRequest *parseHttpRequestAbort(ConnStateData * conn, const char *uri);static clientHttpRequest *parseHttpRequest(ConnStateData *, method_t *, int *, char **, size_t *);static RH clientRedirectDone;static STCB clientHandleIMSReply;static int clientGetsOldEntry(StoreEntry * new, StoreEntry * old, request_t * request);static int checkAccelOnly(clientHttpRequest *);#if USE_IDENTstatic IDCB clientIdentDone;#endifstatic int clientOnlyIfCached(clientHttpRequest * http);static STCB clientSendMoreData;static STCB clientCacheHit;static void clientSetKeepaliveFlag(clientHttpRequest *);static void clientInterpretRequestHeaders(clientHttpRequest *);static void clientProcessRequest(clientHttpRequest *);static void clientProcessExpired(void *data);static void clientProcessOnlyIfCachedMiss(clientHttpRequest * http);static int clientCachable(clientHttpRequest * http);static int clientHierarchical(clientHttpRequest * http);static int clientCheckContentLength(request_t * r);static int httpAcceptDefer(void);static log_type clientProcessRequest2(clientHttpRequest * http);static intcheckAccelOnly(clientHttpRequest * http){ /* return TRUE if someone makes a proxy request to us and * we are in httpd-accel only mode */ if (!Config2.Accel.on) return 0; if (Config.onoff.accel_with_proxy) return 0; if (http->request->protocol == PROTO_CACHEOBJ) return 0; if (http->flags.accel) return 0; return 1;}#if USE_IDENTvoidclientIdentDone(const char *ident, void *data){ ConnStateData *conn = data; if (ident) xstrncpy(conn->ident, ident, sizeof(conn->ident)); else xstrncpy(conn->ident, "-", sizeof(conn->ident));}#endifvoidclientAccessCheck(void *data){ clientHttpRequest *http = data; ConnStateData *conn = http->conn; const char *browser; if (checkAccelOnly(http)) { clientAccessCheckDone(0, http); return; } browser = httpHeaderGetStr(&http->request->header, HDR_USER_AGENT); http->acl_checklist = aclChecklistCreate(Config.accessList.http, http->request, conn->peer.sin_addr, conn->me.sin_addr, browser, conn->ident);#if USE_IDENT /* * hack for ident ACL. It needs to get full addresses, and a * place to store the ident result on persistent connections... */ http->acl_checklist->conn = conn; cbdataLock(http->acl_checklist->conn);#endif aclNBCheck(http->acl_checklist, clientAccessCheckDone, http);}/* * returns true if client specified that the object must come from the cache * witout contacting origin server */static intclientOnlyIfCached(clientHttpRequest * http){ const request_t *r = http->request; assert(r); return r->cache_control && EBIT_TEST(r->cache_control->mask, CC_ONLY_IF_CACHED);}StoreEntry *clientCreateStoreEntry(clientHttpRequest * h, method_t m, request_flags flags){ StoreEntry *e; /* * For erroneous requests, we might not have a h->request, * so make a fake one. */ if (h->request == NULL) h->request = requestLink(requestCreate(m, PROTO_NONE, NULL)); e = storeCreateEntry(h->uri, h->log_uri, flags, m); storeClientListAdd(e, h);#if DELAY_POOLS delaySetStoreClient(e, h, delayClient(h->request));#endif storeClientCopy(e, 0, 0, CLIENT_SOCK_SZ, memAllocate(MEM_CLIENT_SOCK_BUF), clientSendMoreData, h); return e;}voidclientAccessCheckDone(int answer, void *data){ clientHttpRequest *http = data; int page_id = -1; http_status status; ErrorState *err = NULL; debug(33, 5) ("clientAccessCheckDone: '%s' answer=%d\n", http->uri, answer); http->acl_checklist = NULL; if (answer == ACCESS_ALLOWED) { safe_free(http->uri); http->uri = xstrdup(urlCanonical(http->request)); assert(http->redirect_state == REDIRECT_NONE); http->redirect_state = REDIRECT_PENDING; redirectStart(http, clientRedirectDone, http); } else { debug(33, 5) ("Access Denied: %s\n", http->uri); debug(33, 5) ("AclMatchedName = %s\n", AclMatchedName ? AclMatchedName : "<null>"); /* * NOTE: get page_id here, based on AclMatchedName because * if USE_DELAY_POOLS is enabled, then AclMatchedName gets * clobbered in the clientCreateStoreEntry() call * just below. Pedro Ribeiro <pribeiro@isel.pt> */ page_id = aclGetDenyInfoPage(&Config.denyInfoList, AclMatchedName); http->log_type = LOG_TCP_DENIED; http->entry = clientCreateStoreEntry(http, http->request->method, null_request_flags); if (answer == ACCESS_REQ_PROXY_AUTH || aclIsProxyAuth(AclMatchedName)) { if (!http->flags.accel) { /* Proxy authorisation needed */ status = HTTP_PROXY_AUTHENTICATION_REQUIRED; } else { /* WWW authorisation needed */ status = HTTP_UNAUTHORIZED; } if (page_id <= 0) page_id = ERR_CACHE_ACCESS_DENIED; } else { status = HTTP_FORBIDDEN; if (page_id <= 0) page_id = ERR_ACCESS_DENIED; } err = errorCon(page_id, status); err->request = requestLink(http->request); err->src_addr = http->conn->peer.sin_addr; errorAppendEntry(http->entry, err); }}static voidclientRedirectDone(void *data, char *result){ clientHttpRequest *http = data; request_t *new_request = NULL; request_t *old_request = http->request; debug(33, 5) ("clientRedirectDone: '%s' result=%s\n", http->uri, result ? result : "NULL"); assert(http->redirect_state == REDIRECT_PENDING); http->redirect_state = REDIRECT_DONE; if (result) { http_status status = atoi(result); if (status == 301 || status == 302) { char *t = result; if ((t = strchr(result, ':')) != NULL) { http->redirect.status = status; http->redirect.location = xstrdup(t + 1); } else { debug(33, 1) ("clientRedirectDone: bad input: %s\n", result); } } if (strcmp(result, http->uri)) new_request = urlParse(old_request->method, result); } if (new_request) { safe_free(http->uri); http->uri = xstrdup(urlCanonical(new_request)); new_request->http_ver = old_request->http_ver; httpHeaderAppend(&new_request->header, &old_request->header); new_request->client_addr = old_request->client_addr; new_request->my_addr = old_request->my_addr; new_request->flags.redirected = 1; if (old_request->body) { new_request->body = xmalloc(old_request->body_sz); xmemcpy(new_request->body, old_request->body, old_request->body_sz); new_request->body_sz = old_request->body_sz; } requestUnlink(old_request); http->request = requestLink(new_request); } clientInterpretRequestHeaders(http); fd_note(http->conn->fd, http->uri); clientProcessRequest(http);}static voidclientProcessExpired(void *data){ clientHttpRequest *http = data; char *url = http->uri; StoreEntry *entry = NULL; debug(33, 3) ("clientProcessExpired: '%s'\n", http->uri); assert(http->entry->lastmod >= 0); /* * check if we are allowed to contact other servers * @?@: Instead of a 504 (Gateway Timeout) reply, we may want to return * a stale entry *if* it matches client requirements */ if (clientOnlyIfCached(http)) { clientProcessOnlyIfCachedMiss(http); return; } http->request->flags.refresh = 1; http->old_entry = http->entry; /* * Assert that 'http' is already a client of old_entry. If * it is not, then the beginning of the object data might get * freed from memory before we need to access it. */ assert(storeClientListSearch(http->old_entry->mem_obj, http)); entry = storeCreateEntry(url, http->log_uri, http->request->flags, http->request->method); /* NOTE, don't call storeLockObject(), storeCreateEntry() does it */ storeClientListAdd(entry, http);#if DELAY_POOLS /* delay_id is already set on original store client */ delaySetStoreClient(entry, http, delayClient(http->request));#endif entry->lastmod = http->old_entry->lastmod; debug(33, 5) ("clientProcessExpired: lastmod %d\n", (int) entry->lastmod); entry->refcount++; /* EXPIRED CASE */ http->entry = entry; http->out.offset = 0; fwdStart(http->conn->fd, http->entry, http->request, http->conn->peer.sin_addr, http->conn->me.sin_addr); /* Register with storage manager to receive updates when data comes in. */ if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) debug(33, 0) ("clientProcessExpired: found ENTRY_ABORTED object\n"); storeClientCopy(entry, http->out.offset, http->out.offset, CLIENT_SOCK_SZ, memAllocate(MEM_CLIENT_SOCK_BUF), clientHandleIMSReply, http);}static intclientGetsOldEntry(StoreEntry * new_entry, StoreEntry * old_entry, request_t * request){ const http_status status = new_entry->mem_obj->reply->sline.status; if (0 == status) { debug(33, 5) ("clientGetsOldEntry: YES, broken HTTP reply\n"); return 1; } /* If the reply is a failure then send the old object as a last * resort */ if (status >= 500 && status < 600) { debug(33, 2) ("clientGetsOldEntry: YES, failure reply=%d\n", status); return 1; } /* If the reply is anything but "Not Modified" then * we must forward it to the client */ if (HTTP_NOT_MODIFIED != status) { debug(33, 5) ("clientGetsOldEntry: NO, reply=%d\n", status); return 0; } /* If the client did not send IMS in the request, then it * must get the old object, not this "Not Modified" reply */ if (!request->flags.ims) { debug(33, 5) ("clientGetsOldEntry: YES, no client IMS\n"); return 1; } /* If the client IMS time is prior to the entry LASTMOD time we * need to send the old object */ if (modifiedSince(old_entry, request)) { debug(33, 5) ("clientGetsOldEntry: YES, modified since %d\n", (int) request->ims); return 1; } debug(33, 5) ("clientGetsOldEntry: NO, new one is fine\n"); return 0;}static voidclientHandleIMSReply(void *data, char *buf, ssize_t size){ clientHttpRequest *http = data; StoreEntry *entry = http->entry; MemObject *mem; const char *url = storeUrl(entry); int unlink_request = 0; StoreEntry *oldentry; int recopy = 1; http_status status; debug(33, 3) ("clientHandleIMSReply: %s, %d bytes\n", url, (int) size); if (entry == NULL) { memFree(buf, MEM_CLIENT_SOCK_BUF); return; } if (size < 0 && !EBIT_TEST(entry->flags, ENTRY_ABORTED)) { memFree(buf, MEM_CLIENT_SOCK_BUF); return; } mem = entry->mem_obj; status = mem->reply->sline.status; if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { debug(33, 3) ("clientHandleIMSReply: ABORTED '%s'\n", url); /* We have an existing entry, but failed to validate it */ /* Its okay to send the old one anyway */ http->log_type = LOG_TCP_REFRESH_FAIL_HIT; storeUnregister(entry, http); storeUnlockObject(entry); entry = http->entry = http->old_entry; entry->refcount++; } else if (STORE_PENDING == entry->store_status && 0 == status) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -