📄 client_side.c
字号:
#endif request->cache_control = httpHeaderGetCc(req_hdr); if (request->method == METHOD_TRACE) { request->max_forwards = httpHeaderGetInt(req_hdr, HDR_MAX_FORWARDS); } if (clientCachable(http)) request->flags.cachable = 1; if (clientHierarchical(http)) request->flags.hierarchical = 1; debug(33, 5) ("clientInterpretRequestHeaders: REQ_NOCACHE = %s\n", request->flags.nocache ? "SET" : "NOT SET"); debug(33, 5) ("clientInterpretRequestHeaders: REQ_CACHABLE = %s\n", request->flags.cachable ? "SET" : "NOT SET"); debug(33, 5) ("clientInterpretRequestHeaders: REQ_HIERARCHICAL = %s\n", request->flags.hierarchical ? "SET" : "NOT SET");}/* * clientSetKeepaliveFlag() sets request->flags.proxy_keepalive. * This is the client-side persistent connection flag. We need * to set this relatively early in the request processing * to handle hacks for broken servers and clients. */static voidclientSetKeepaliveFlag(clientHttpRequest * http){ request_t *request = http->request; const HttpHeader *req_hdr = &request->header; debug(33, 3) ("clientSetKeepaliveFlag: http_ver = %3.1f\n", request->http_ver); debug(33, 3) ("clientSetKeepaliveFlag: method = %s\n", RequestMethodStr[request->method]); if (httpMsgIsPersistent(request->http_ver, req_hdr)) request->flags.proxy_keepalive = 1;}static intclientCheckContentLength(request_t * r){ /* We only require a content-length for "upload" methods */ if (!pumpMethod(r->method)) return 1; if (httpHeaderGetInt(&r->header, HDR_CONTENT_LENGTH) < 0) return 0; return 1;}static intclientCachable(clientHttpRequest * http){ const char *url = http->uri; request_t *req = http->request; method_t method = req->method; aclCheck_t ch; memset(&ch, '\0', sizeof(ch)); /* * Hopefully, nobody really wants 'no_cache' by client's IP * address, but if they do, this should work if they use IP * addresses in their ACLs, or if the client's address is in * the FQDN cache. * * This may not work yet for 'dst' and 'dst_domain' ACLs. */ ch.src_addr = http->conn->peer.sin_addr; ch.my_addr = http->conn->me.sin_addr; ch.request = http->request; /* * aclCheckFast returns 1 for ALLOW and 0 for DENY. The default * is ALLOW, so we require 'no_cache DENY foo' in squid.conf * to indicate uncachable objects. */ if (!aclCheckFast(Config.accessList.noCache, &ch)) return 0; if (req->protocol == PROTO_HTTP) return httpCachable(method); /* FTP is always cachable */ if (req->protocol == PROTO_GOPHER) return gopherCachable(url); if (req->protocol == PROTO_WAIS) return 0; if (method == METHOD_CONNECT) return 0; if (method == METHOD_TRACE) return 0; if (req->protocol == PROTO_CACHEOBJ) return 0; return 1;}/* Return true if we can query our neighbors for this object */static intclientHierarchical(clientHttpRequest * http){ const char *url = http->uri; request_t *request = http->request; method_t method = request->method; const wordlist *p = NULL; /* IMS needs a private key, so we can use the hierarchy for IMS only * if our neighbors support private keys */ if (request->flags.ims && !neighbors_do_private_keys) return 0; if (request->flags.auth) return 0; if (method == METHOD_TRACE) return 1; if (method != METHOD_GET) return 0; /* scan hierarchy_stoplist */ for (p = Config.hierarchy_stoplist; p; p = p->next) if (strstr(url, p->key)) return 0; if (request->flags.loopdetect) return 0; if (request->protocol == PROTO_HTTP) return httpCachable(method); if (request->protocol == PROTO_GOPHER) return gopherCachable(url); if (request->protocol == PROTO_WAIS) return 0; if (request->protocol == PROTO_CACHEOBJ) return 0; return 1;}intisTcpHit(log_type code){ /* this should be a bitmap for better optimization */ if (code == LOG_TCP_HIT) return 1; if (code == LOG_TCP_IMS_HIT) return 1; if (code == LOG_TCP_REFRESH_FAIL_HIT) return 1; if (code == LOG_TCP_REFRESH_HIT) return 1; if (code == LOG_TCP_NEGATIVE_HIT) return 1; if (code == LOG_TCP_MEM_HIT) return 1; if (code == LOG_TCP_OFFLINE_HIT) return 1; return 0;}/* * returns true if If-Range specs match reply, false otherwise */static intclientIfRangeMatch(clientHttpRequest * http, HttpReply * rep){ const TimeOrTag spec = httpHeaderGetTimeOrTag(&http->request->header, HDR_IF_RANGE); /* check for parsing falure */ if (!spec.valid) return 0; /* got an ETag? */ if (spec.tag.str) { ETag rep_tag = httpHeaderGetETag(&rep->header, HDR_ETAG); debug(33, 3) ("clientIfRangeMatch: ETags: %s and %s\n", spec.tag.str, rep_tag.str ? rep_tag.str : "<none>"); if (!rep_tag.str) return 0; /* entity has no etag to compare with! */ if (spec.tag.weak || rep_tag.weak) { debug(33, 1) ("clientIfRangeMatch: Weak ETags are not allowed in If-Range: %s ? %s\n", spec.tag.str, rep_tag.str); return 0; /* must use strong validator for sub-range requests */ } return etagIsEqual(&rep_tag, &spec.tag); } /* got modification time? */ if (spec.time >= 0) { return http->entry->lastmod <= spec.time; } assert(0); /* should not happen */ return 0;}/* adds appropriate Range headers if needed */static voidclientBuildRangeHeader(clientHttpRequest * http, HttpReply * rep){ HttpHeader *hdr = rep ? &rep->header : 0; const char *range_err = NULL; assert(http->request->range); /* check if we still want to do ranges */ if (!rep) range_err = "no [parse-able] reply"; else if (rep->sline.status != HTTP_OK) range_err = "wrong status code"; else if (httpHeaderHas(hdr, HDR_CONTENT_RANGE)) range_err = "origin server does ranges"; else if (rep->content_length < 0) range_err = "unknown length"; else if (rep->content_length != http->entry->mem_obj->reply->content_length) range_err = "INCONSISTENT length"; /* a bug? */ else if (httpHeaderHas(&http->request->header, HDR_IF_RANGE) && !clientIfRangeMatch(http, rep)) range_err = "If-Range match failed"; else if (!httpHdrRangeCanonize(http->request->range, rep->content_length)) range_err = "canonization failed"; else if (httpHdrRangeIsComplex(http->request->range)) range_err = "too complex range header"; /* get rid of our range specs on error */ if (range_err) { debug(33, 2) ("clientBuildRangeHeader: will not do ranges: %s.\n", range_err); httpHdrRangeDestroy(http->request->range); http->request->range = NULL; } else { const int spec_count = http->request->range->specs.count; debug(33, 2) ("clientBuildRangeHeader: range spec count: %d clen: %d\n", spec_count, rep->content_length); assert(spec_count > 0); /* ETags should not be returned with Partial Content replies? */ httpHeaderDelById(hdr, HDR_ETAG); /* append appropriate header(s) */ if (spec_count == 1) { HttpHdrRangePos pos = HttpHdrRangeInitPos; const HttpHdrRangeSpec *spec = httpHdrRangeGetSpec(http->request->range, &pos); assert(spec); /* append Content-Range */ httpHeaderAddContRange(hdr, *spec, rep->content_length); /* set new Content-Length to the actual number of OCTETs * transmitted in the message-body */ httpHeaderDelById(hdr, HDR_CONTENT_LENGTH); httpHeaderPutInt(hdr, HDR_CONTENT_LENGTH, spec->length); debug(33, 2) ("clientBuildRangeHeader: actual content length: %d\n", spec->length); } else { /* multipart! */ /* generate boundary string */ http->range_iter.boundary = httpHdrRangeBoundaryStr(http); /* delete old Content-Type, add ours */ httpHeaderDelById(hdr, HDR_CONTENT_TYPE); httpHeaderPutStrf(hdr, HDR_CONTENT_TYPE, "multipart/byteranges; boundary=\"%s\"", strBuf(http->range_iter.boundary)); /* no need for Content-Length in multipart responses */ /* but we must delete the original one if we cannot (yet) * calculate the actual length */ httpHeaderDelById(hdr, HDR_CONTENT_LENGTH); } }}/* filters out unwanted entries from original reply header * adds extra entries if we have more info than origin server * adds Squid specific entries */static voidclientBuildReplyHeader(clientHttpRequest * http, HttpReply * rep){ HttpHeader *hdr = &rep->header; int is_hit = isTcpHit(http->log_type); request_t *request = http->request;#if DONT_FILTER_THESE /* but you might want to if you run Squid as an HTTP accelerator */ /* httpHeaderDelById(hdr, HDR_ACCEPT_RANGES); */ httpHeaderDelById(hdr, HDR_ETAG);#endif httpHeaderDelById(hdr, HDR_PROXY_CONNECTION); /* here: Keep-Alive is a field-name, not a connection directive! */ httpHeaderDelByName(hdr, "Keep-Alive"); /* remove Set-Cookie if a hit */ if (is_hit) httpHeaderDelById(hdr, HDR_SET_COOKIE); /* handle Connection header */ if (httpHeaderHas(hdr, HDR_CONNECTION)) { /* anything that matches Connection list member will be deleted */ String strConnection = httpHeaderGetList(hdr, HDR_CONNECTION); const HttpHeaderEntry *e; HttpHeaderPos pos = HttpHeaderInitPos; /* * think: on-average-best nesting of the two loops (hdrEntry * and strListItem) @?@ */ /* * maybe we should delete standard stuff ("keep-alive","close") * from strConnection first? */ while ((e = httpHeaderGetEntry(hdr, &pos))) { if (strListIsMember(&strConnection, strBuf(e->name), ',')) httpHeaderDelAt(hdr, pos); } httpHeaderDelById(hdr, HDR_CONNECTION); stringClean(&strConnection); } /* Handle Ranges */ if (request->range) clientBuildRangeHeader(http, rep); /* * Add Age header, not that our header must replace Age headers * from other caches if any */ if (http->entry->timestamp > 0) { httpHeaderDelById(hdr, HDR_AGE); /* * we do not follow HTTP/1.1 precisely here becuase we rely * on Date header when computing entry->timestamp; we should * be using _request_ time if Date header is not available * or if it is out of sync */ httpHeaderPutInt(hdr, HDR_AGE, http->entry->timestamp <= squid_curtime ? squid_curtime - http->entry->timestamp : 0); } /* Append X-Cache */ httpHeaderPutStrf(hdr, HDR_X_CACHE, "%s from %s", is_hit ? "HIT" : "MISS", getMyHostname());#if USE_CACHE_DIGESTS /* Append X-Cache-Lookup: -- temporary hack, to be removed @?@ @?@ */ httpHeaderPutStrf(hdr, HDR_X_CACHE_LOOKUP, "%s from %s:%d", http->lookup_type ? http->lookup_type : "NONE", getMyHostname(), Config.Port.http->i);#endif /* * Clear keepalive for NON-HEAD requests with invalid content length */ if (request->method != METHOD_HEAD) if (http->entry->mem_obj->reply->content_length < 0) request->flags.proxy_keepalive = 0; /* Signal keep-alive if needed */ httpHeaderPutStr(hdr, http->flags.accel ? HDR_CONNECTION : HDR_PROXY_CONNECTION, request->flags.proxy_keepalive ? "keep-alive" : "close");#if ADD_X_REQUEST_URI /* * Knowing the URI of the request is useful when debugging persistent * connections in a client; we cannot guarantee the order of http headers, * but X-Request-URI is likely to be the very last header to ease use from a * debugger [hdr->entries.count-1]. */ httpHeaderPutStr(hdr, HDR_X_REQUEST_URI, http->entry->mem_obj->url ? http->entry->mem_obj->url : http->uri);#endif}static HttpReply *clientBuildReply(clientHttpRequest * http, const char *buf, size_t size){ HttpReply *rep = httpReplyCreate(); if (httpReplyParse(rep, buf)) { /* enforce 1.0 reply version */ rep->sline.version = 1.0; /* do header conversions */ clientBuildReplyHeader(http, rep); /* if we do ranges, change status to "Partial Content" */ if (http->request->range) httpStatusLineSet(&rep->sline, rep->sline.version, HTTP_PARTIAL_CONTENT, NULL); } else { /* parsing failure, get rid of the invalid reply */ httpReplyDestroy(rep); rep = NULL; /* if we were going to do ranges, backoff */ if (http->request->range) clientBuildRangeHeader(http, rep); /* will fail and destroy request->range */ } return rep;}/* * clientCacheHit should only be called until the HTTP reply headers * have been parsed. Normally this should be a single call, but * it might take more than one. As soon as we have the headers, * we hand off to clientSendMoreData, clientProcessExpired, or * clientProcessMiss. */static voidclientCacheHit(void *data, char *buf, ssize_t size){ clientHttpRequest *http = data; StoreEntry *e = http->entry; MemObject *mem; request_t *r = http->request; debug(33, 3) ("clientCacheHit: %s, %d bytes\n", http->uri, (int) size); if (http->entry == NULL) { memFree(buf, MEM_CLIENT_SOCK_BUF); debug(33, 3) ("clientCacheHit: request aborted\n"); return; } else if (size < 0) { /* swap in failure */ memFree(buf, MEM_CLIENT_SOCK_BUF); debug(33, 3) ("clientCacheHit: swapin failure for %s\n", http->uri); http->log_type = LOG_TCP_SWAPFAIL_MISS; if ((e = http->entry)) { http->entry = NULL; storeUnregister(e, http); storeUnlockObject(e); } clientProcessMiss(http); return; } assert(size > 0); mem = e->mem_obj; assert(!EBIT_TEST(e->flags, ENTRY_ABORTED)); if (mem->reply->sline.status == 0) { /* * we don't have full reply headers yet; either wait for more or * punt to clientProcessMiss. */ if (e->mem_status == IN_MEMORY || e->store_status == STORE_OK) { memFree(buf, MEM_CLIENT_SOCK_BUF); clientProcessMiss(http); } else if (size == CLIENT_SOCK_SZ && http->out.offset == 0) { memFree(buf, MEM_CLIENT_SOCK_BUF);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -