/*
- * $Id: client_side.cc,v 1.460 1999/07/13 14:51:09 wessels Exp $
+ * $Id: client_side.cc,v 1.522 2001/01/07 23:36:37 hno Exp $
*
* DEBUG: section 33 Client-side Routines
* AUTHOR: Duane Wessels
* 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.
+ * the Regents of the University of California. 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
#endif
#include <netinet/tcp.h>
#include <net/if.h>
+#if HAVE_IP_FIL_COMPAT_H
+#include <ip_fil_compat.h>
+#elif HAVE_NETINET_IP_FIL_COMPAT_H
+#include <netinet/ip_fil_compat.h>
+#elif HAVE_IP_COMPAT_H
#include <ip_compat.h>
+#elif HAVE_NETINET_IP_COMPAT_H
+#include <netinet/ip_compat.h>
+#endif
+#if HAVE_IP_FIL_H
#include <ip_fil.h>
+#elif HAVE_NETINET_IP_FIL_H
+#include <netinet/ip_fil.h>
+#endif
+#if HAVE_IP_NAT_H
#include <ip_nat.h>
+#elif HAVE_NETINET_IP_NAT_H
+#include <netinet/ip_nat.h>
+#endif
#endif
+#if LINUX_NETFILTER
+#include <linux/netfilter_ipv4.h>
+#endif
#if LINGERING_CLOSE
static const char *const crlf = "\r\n";
-#define REQUEST_BUF_SIZE 4096
#define FAILURE_MODE_TIME 300
/* Local functions */
static CWCB clientWriteComplete;
+static CWCB clientWriteBodyComplete;
static PF clientReadRequest;
static PF connStateFree;
static PF requestTimeout;
+static PF clientLifetimeTimeout;
static int clientCheckTransferDone(clientHttpRequest *);
static int clientGotNotEnough(clientHttpRequest *);
static void checkFailureRatio(err_type, hier_code);
static clientHttpRequest *parseHttpRequestAbort(ConnStateData * conn, const char *uri);
static clientHttpRequest *parseHttpRequest(ConnStateData *, method_t *, int *, char **, size_t *);
static RH clientRedirectDone;
+static void clientCheckNoCache(clientHttpRequest *);
+static void clientCheckNoCacheDone(int answer, void *data);
static STCB clientHandleIMSReply;
static int clientGetsOldEntry(StoreEntry * new, StoreEntry * old, request_t * request);
static int checkAccelOnly(clientHttpRequest *);
static int clientCachable(clientHttpRequest * http);
static int clientHierarchical(clientHttpRequest * http);
static int clientCheckContentLength(request_t * r);
-static int httpAcceptDefer(void);
+static DEFER httpAcceptDefer;
static log_type clientProcessRequest2(clientHttpRequest * http);
static int clientReplyBodyTooLarge(int clen);
+static int clientRequestBodyTooLarge(int clen);
+static void clientProcessBody(ConnStateData * conn);
static int
checkAccelOnly(clientHttpRequest * http)
}
#if USE_IDENT
-void
+static void
clientIdentDone(const char *ident, void *data)
{
ConnStateData *conn = data;
- if (ident)
- xstrncpy(conn->ident, ident, sizeof(conn->ident));
- else
- xstrncpy(conn->ident, "-", sizeof(conn->ident));
+ xstrncpy(conn->rfc931, ident ? ident : dash_str, USER_IDENT_SZ);
}
+
#endif
-void
-clientAccessCheck(void *data)
+static aclCheck_t *
+clientAclChecklistCreate(const acl_access * acl, const clientHttpRequest * http)
{
- clientHttpRequest *http = data;
+ aclCheck_t *ch;
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,
+ ch = aclChecklistCreate(acl,
http->request,
- conn->peer.sin_addr,
- conn->me.sin_addr,
- browser,
- conn->ident);
-#if USE_IDENT
+ conn->rfc931);
+
/*
* 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
+ /* connection oriented auth also needs these two lines for it's operation. */
+ ch->conn = conn;
+ cbdataLock(ch->conn);
+
+ return ch;
+}
+
+void
+clientAccessCheck(void *data)
+{
+ clientHttpRequest *http = data;
+ if (checkAccelOnly(http)) {
+ /* deny proxy requests in accel_only mode */
+ debug(33, 1) ("clientAccessCheck: proxy request denied in accel_only mode\n");
+ clientAccessCheckDone(ACCESS_DENIED, http);
+ return;
+ }
+ http->acl_checklist = clientAclChecklistCreate(Config.accessList.http, http);
aclNBCheck(http->acl_checklist, clientAccessCheckDone, http);
}
/*
* returns true if client specified that the object must come from the cache
- * witout contacting origin server
+ * without contacting origin server
*/
static int
clientOnlyIfCached(clientHttpRequest * http)
if (h->request == NULL)
h->request = requestLink(requestCreate(m, PROTO_NONE, null_string));
e = storeCreateEntry(h->uri, h->log_uri, flags, m);
- storeClientListAdd(e, h);
+ h->sc = storeClientListAdd(e, h);
#if DELAY_POOLS
- delaySetStoreClient(e, h, delayClient(h->request));
+ delaySetStoreClient(h->sc, delayClient(h->request));
#endif
- storeClientCopy(e, 0, 0, CLIENT_SOCK_SZ,
+ storeClientCopy(h->sc, e, 0, 0, CLIENT_SOCK_SZ,
memAllocate(MEM_CLIENT_SOCK_BUF), clientSendMoreData, h);
return e;
}
int page_id = -1;
http_status status;
ErrorState *err = NULL;
+ char *proxy_auth_msg = NULL;
debug(33, 2) ("The request %s %s is %s, because it matched '%s'\n",
RequestMethodStr[http->request->method], http->uri,
- answer ? "ALLOWED" : "DENIED",
+ answer == ACCESS_ALLOWED ? "ALLOWED" : "DENIED",
AclMatchedName ? AclMatchedName : "NO ACL's");
+ proxy_auth_msg = authenticateAuthUserRequestMessage(http->conn->auth_user_request ? http->conn->auth_user_request : http->request->auth_user_request);
http->acl_checklist = NULL;
if (answer == ACCESS_ALLOWED) {
safe_free(http->uri);
debug(33, 5) ("Access Denied: %s\n", http->uri);
debug(33, 5) ("AclMatchedName = %s\n",
AclMatchedName ? AclMatchedName : "<null>");
+ debug(33, 5) ("Proxy Auth Message = %s\n",
+ proxy_auth_msg ? proxy_auth_msg : "<null>");
/*
* NOTE: get page_id here, based on AclMatchedName because
* if USE_DELAY_POOLS is enabled, then AclMatchedName gets
err = errorCon(page_id, status);
err->request = requestLink(http->request);
err->src_addr = http->conn->peer.sin_addr;
+ if (http->conn->auth_user_request)
+ err->auth_user_request = http->conn->auth_user_request;
+ else if (http->request->auth_user_request)
+ err->auth_user_request = http->request->auth_user_request;
+ /* lock for the error state */
+ if (err->auth_user_request)
+ authenticateAuthUserRequestLock(err->auth_user_request);
+ err->callback_data = NULL;
errorAppendEntry(http->entry, err);
}
}
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->my_port = old_request->my_port;
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;
+ new_request->auth_user_request = old_request->auth_user_request;
+ if (old_request->body_connection) {
+ new_request->body_connection = old_request->body_connection;
+ old_request->body_connection = NULL;
}
+ new_request->content_length = old_request->content_length;
+ new_request->flags.proxy_keepalive = old_request->flags.proxy_keepalive;
requestUnlink(old_request);
http->request = requestLink(new_request);
}
clientInterpretRequestHeaders(http);
+#if HEADERS_LOG
+ headersLog(0, 1, request->method, request);
+#endif
fd_note(http->conn->fd, http->uri);
+ clientCheckNoCache(http);
+}
+
+static void
+clientCheckNoCache(clientHttpRequest * http)
+{
+ if (Config.accessList.noCache && http->request->flags.cachable) {
+ http->acl_checklist = clientAclChecklistCreate(Config.accessList.noCache, http);
+ aclNBCheck(http->acl_checklist, clientCheckNoCacheDone, http);
+ } else {
+ clientCheckNoCacheDone(http->request->flags.cachable, http);
+ }
+}
+
+void
+clientCheckNoCacheDone(int answer, void *data)
+{
+ clientHttpRequest *http = data;
+ http->request->flags.cachable = answer;
+ http->acl_checklist = NULL;
clientProcessRequest(http);
}
}
http->request->flags.refresh = 1;
http->old_entry = http->entry;
+ http->old_sc = http->sc;
/*
* 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));
+ assert(http->sc->callback_data == http);
entry = storeCreateEntry(url,
http->log_uri,
http->request->flags,
http->request->method);
/* NOTE, don't call storeLockObject(), storeCreateEntry() does it */
- storeClientListAdd(entry, http);
+ http->sc = storeClientListAdd(entry, http);
#if DELAY_POOLS
/* delay_id is already set on original store client */
- delaySetStoreClient(entry, http, delayClient(http->request));
+ delaySetStoreClient(http->sc, delayClient(http->request));
#endif
- entry->lastmod = http->old_entry->lastmod;
+ http->request->lastmod = http->old_entry->lastmod;
debug(33, 5) ("clientProcessExpired: lastmod %d\n", (int) entry->lastmod);
- entry->refcount++; /* EXPIRED CASE */
-#if HEAP_REPLACEMENT
- storeHeapPositionUpdate(entry);
-#endif
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);
+ fwdStart(http->conn->fd, http->entry, http->request);
/* 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,
+ storeClientCopy(http->sc, entry,
http->out.offset,
http->out.offset,
CLIENT_SOCK_SZ,
/* 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);
+ storeUnregister(http->sc, entry, http);
storeUnlockObject(entry);
entry = http->entry = http->old_entry;
- entry->refcount++;
-#if HEAP_REPLACEMENT
- storeHeapPositionUpdate(entry);
-#endif
+ http->sc = http->old_sc;
} else if (STORE_PENDING == entry->store_status && 0 == status) {
debug(33, 3) ("clientHandleIMSReply: Incomplete headers for '%s'\n", url);
if (size >= CLIENT_SOCK_SZ) {
debug(33, 3) ("clientHandleIMSReply: Reply is too large '%s', using old entry\n", url);
/* use old entry, this repeats the code abovez */
http->log_type = LOG_TCP_REFRESH_FAIL_HIT;
- storeUnregister(entry, http);
+ storeUnregister(http->sc, entry, http);
storeUnlockObject(entry);
entry = http->entry = http->old_entry;
- entry->refcount++;
-#if HEAP_REPLACEMENT
- storeHeapPositionUpdate(entry);
-#endif
+ http->sc = http->old_sc;
/* continue */
} else {
- storeClientCopy(entry,
+ storeClientCopy(http->sc, entry,
http->out.offset + size,
http->out.offset,
CLIENT_SOCK_SZ,
* not the body they refer to. */
httpReplyUpdateOnNotModified(oldentry->mem_obj->reply, mem->reply);
storeTimestampsSet(oldentry);
- storeUnregister(entry, http);
+ storeUnregister(http->sc, entry, http);
+ http->sc = http->old_sc;
storeUnlockObject(entry);
entry = http->entry = oldentry;
entry->timestamp = squid_curtime;
httpReplyUpdateOnNotModified(http->old_entry->mem_obj->reply,
mem->reply);
storeTimestampsSet(http->old_entry);
- http->old_entry->refcount++;
-#if HEAP_REPLACEMENT
- storeHeapPositionUpdate(http->old_entry);
-#endif
http->log_type = LOG_TCP_REFRESH_HIT;
}
- storeUnregister(http->old_entry, http);
+ storeUnregister(http->old_sc, http->old_entry, http);
storeUnlockObject(http->old_entry);
recopy = 0;
}
http->old_entry = NULL; /* done with old_entry */
+ http->old_sc = NULL;
assert(!EBIT_TEST(entry->flags, ENTRY_ABORTED));
if (recopy) {
- storeClientCopy(entry,
+ storeClientCopy(http->sc, entry,
http->out.offset,
http->out.offset,
CLIENT_SOCK_SZ,
StoreEntry *entry;
ErrorState *err = NULL;
HttpReply *r;
- debug(33, 3) ("Config.onoff.enable_purge = %d\n", Config.onoff.enable_purge);
- if (!Config.onoff.enable_purge) {
+ http_status status;
+ http_version_t version;
+ debug(33, 3) ("Config2.onoff.enable_purge = %d\n", Config2.onoff.enable_purge);
+ if (!Config2.onoff.enable_purge) {
http->log_type = LOG_TCP_DENIED;
err = errorCon(ERR_ACCESS_DENIED, HTTP_FORBIDDEN);
err->request = requestLink(http->request);
return;
}
http->log_type = LOG_TCP_MISS;
+ /* Release both IP and object cache entries */
+ ipcacheInvalidate(http->request->host);
if ((entry = storeGetPublic(http->uri, METHOD_GET)) == NULL) {
- http->http_code = HTTP_NOT_FOUND;
+ status = HTTP_NOT_FOUND;
} else {
storeRelease(entry);
- http->http_code = HTTP_OK;
+ status = HTTP_OK;
}
debug(33, 4) ("clientPurgeRequest: Not modified '%s'\n",
storeUrl(entry));
*/
http->entry = clientCreateStoreEntry(http, http->request->method, null_request_flags);
httpReplyReset(r = http->entry->mem_obj->reply);
- httpReplySetHeaders(r, 1.0, http->http_code, NULL, NULL, 0, 0, -1);
+ httpBuildVersion(&version, 1, 0);
+ httpReplySetHeaders(r, version, status, NULL, NULL, 0, 0, -1);
httpReplySwapOut(r, http->entry);
storeComplete(http->entry);
}
int svc_time = tvSubMsec(http->start, current_time);
ping_data *i;
HierarchyLogEntry *H;
- Counter.client_http.requests++;
+ statCounter.client_http.requests++;
if (isTcpHit(http->log_type))
- Counter.client_http.hits++;
+ statCounter.client_http.hits++;
+ if (http->log_type == LOG_TCP_HIT)
+ statCounter.client_http.disk_hits++;
+ else if (http->log_type == LOG_TCP_MEM_HIT)
+ statCounter.client_http.mem_hits++;
if (http->request->err_type != ERR_NONE)
- Counter.client_http.errors++;
- statHistCount(&Counter.client_http.all_svc_time, svc_time);
+ statCounter.client_http.errors++;
+ statHistCount(&statCounter.client_http.all_svc_time, svc_time);
/*
* The idea here is not to be complete, but to get service times
* for only well-defined types. For example, we don't include
*/
switch (http->log_type) {
case LOG_TCP_REFRESH_HIT:
- statHistCount(&Counter.client_http.nh_svc_time, svc_time);
+ statHistCount(&statCounter.client_http.nh_svc_time, svc_time);
break;
case LOG_TCP_IMS_HIT:
- statHistCount(&Counter.client_http.nm_svc_time, svc_time);
+ statHistCount(&statCounter.client_http.nm_svc_time, svc_time);
break;
case LOG_TCP_HIT:
case LOG_TCP_MEM_HIT:
case LOG_TCP_OFFLINE_HIT:
- statHistCount(&Counter.client_http.hit_svc_time, svc_time);
+ statHistCount(&statCounter.client_http.hit_svc_time, svc_time);
break;
case LOG_TCP_MISS:
case LOG_TCP_CLIENT_REFRESH_MISS:
- statHistCount(&Counter.client_http.miss_svc_time, svc_time);
+ statHistCount(&statCounter.client_http.miss_svc_time, svc_time);
break;
default:
/* make compiler warnings go away */
H = &http->request->hier;
switch (H->alg) {
case PEER_SA_DIGEST:
- Counter.cd.times_used++;
+ statCounter.cd.times_used++;
break;
case PEER_SA_ICP:
- Counter.icp.times_used++;
+ statCounter.icp.times_used++;
i = &H->ping;
if (0 != i->stop.tv_sec && 0 != i->start.tv_sec)
- statHistCount(&Counter.icp.query_svc_time,
+ statHistCount(&statCounter.icp.query_svc_time,
tvSubUsec(i->start, i->stop));
if (i->timeout)
- Counter.icp.query_timeouts++;
+ statCounter.icp.query_timeouts++;
break;
case PEER_SA_NETDB:
- Counter.netdb.times_used++;
+ statCounter.netdb.times_used++;
break;
default:
break;
MemObject *mem = NULL;
debug(33, 3) ("httpRequestFree: %s\n", storeUrl(http->entry));
if (!clientCheckTransferDone(http)) {
+ if (request && request->body_connection)
+ clientAbortBody(request); /* abort body transter */
+#if MYSTERIOUS_CODE
+ /*
+ * DW: this seems odd here, is it really needed? It causes
+ * incomplete transfers to get logged with "000" status
+ * code because http->entry becomes NULL.
+ */
if ((e = http->entry)) {
http->entry = NULL;
- storeUnregister(e, http);
+ storeUnregister(http->sc, e, http);
storeUnlockObject(e);
}
+#endif
if (http->entry && http->entry->ping_status == PING_WAITING)
storeReleaseRequest(http->entry);
}
http->al.http.version = request->http_ver;
http->al.headers.request = xstrdup(mb.buf);
http->al.hier = request->hier;
- if (request->user_ident[0])
- http->al.cache.ident = request->user_ident;
- else
- http->al.cache.ident = conn->ident;
+ if (request->auth_user_request) {
+ http->al.cache.authuser = xstrdup(authenticateUserRequestUsername(request->auth_user_request));
+ authenticateAuthUserRequestUnlock(request->auth_user_request);
+ request->auth_user_request = NULL;
+ }
+ if (conn->rfc931[0])
+ http->al.cache.rfc931 = conn->rfc931;
packerClean(&p);
memBufClean(&mb);
}
stringClean(&http->range_iter.boundary);
if ((e = http->entry)) {
http->entry = NULL;
- storeUnregister(e, http);
+ storeUnregister(http->sc, e, http);
+ http->sc = NULL;
storeUnlockObject(e);
}
/* old_entry might still be set if we didn't yet get the reply
* code in clientHandleIMSReply() */
if ((e = http->old_entry)) {
http->old_entry = NULL;
- storeUnregister(e, http);
+ storeUnregister(http->old_sc, e, http);
+ http->old_sc = NULL;
storeUnlockObject(e);
}
requestUnlink(http->request);
assert(http != http->next);
assert(http->conn->chr != NULL);
+ /* Unlink us from the clients request list */
H = &http->conn->chr;
while (*H) {
if (*H == http)
clientHttpRequest *http;
debug(33, 3) ("connStateFree: FD %d\n", fd);
assert(connState != NULL);
+ authenticateOnCloseConnection(connState);
+ clientdbEstablished(connState->peer.sin_addr, -1); /* decrement */
while ((http = connState->chr) != NULL) {
assert(http->conn == connState);
assert(connState->chr != connState->chr->next);
httpRequestFree(http);
}
- safe_free(connState->in.buf);
+ if (connState->in.size == CLIENT_REQ_BUF_SZ)
+ memFree(connState->in.buf, MEM_CLIENT_REQ_BUF);
+ else
+ safe_free(connState->in.buf);
/* XXX account connState->in.buf */
pconnHistCount(0, connState->nrequests);
cbdataFree(connState);
request_t *request = http->request;
const HttpHeader *req_hdr = &request->header;
int no_cache = 0;
-#if USE_USERAGENT_LOG
const char *str;
-#endif
request->imslen = -1;
request->ims = httpHeaderGetTime(req_hdr, HDR_IF_MODIFIED_SINCE);
if (request->ims > 0)
no_cache++;
stringClean(&s);
}
+ request->cache_control = httpHeaderGetCc(req_hdr);
if (request->cache_control)
if (EBIT_TEST(request->cache_control->mask, CC_NO_CACHE))
no_cache++;
+ /* Work around for supporting the Reload button in IE browsers
+ * when Squid is used as an accelerator or transparent proxy,
+ * by turning accelerated IMS request to no-cache requests.
+ * Now knows about IE 5.5 fix (is actually only fixed in SP1,
+ * but we can't tell whether we are talking to SP1 or not so
+ * all 5.5 versions are treated 'normally').
+ */
+ if (Config.onoff.ie_refresh) {
+ if (http->flags.accel && request->flags.ims) {
+ if ((str = httpHeaderGetStr(req_hdr, HDR_USER_AGENT))) {
+ if (strstr(str, "MSIE 5.01") != NULL)
+ no_cache++;
+ else if (strstr(str, "MSIE 5.0") != NULL)
+ no_cache++;
+ else if (strstr(str, "MSIE 4.") != NULL)
+ no_cache++;
+ else if (strstr(str, "MSIE 3.") != NULL)
+ no_cache++;
+ }
+ }
+ }
if (no_cache) {
#if HTTP_VIOLATIONS
if (Config.onoff.reload_into_ims)
if ((str = httpHeaderGetStr(req_hdr, HDR_USER_AGENT)))
logUserAgent(fqdnFromAddr(http->conn->peer.sin_addr), str);
#endif
+#if USE_REFERER_LOG
+ if ((str = httpHeaderGetStr(req_hdr, HDR_REFERER)))
+ logReferer(fqdnFromAddr(http->conn->peer.sin_addr), str,
+ http->log_uri);
+#endif
#if FORW_VIA_DB
if (httpHeaderHas(req_hdr, HDR_X_FORWARDED_FOR)) {
String s = httpHeaderGetList(req_hdr, HDR_X_FORWARDED_FOR);
stringClean(&s);
}
#endif
- request->cache_control = httpHeaderGetCc(req_hdr);
if (request->method == METHOD_TRACE) {
request->max_forwards = httpHeaderGetInt(req_hdr, HDR_MAX_FORWARDS);
}
{
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: http_ver = %d.%d\n",
+ request->http_ver.major, request->http_ver.minor);
debug(33, 3) ("clientSetKeepaliveFlag: method = %s\n",
RequestMethodStr[request->method]);
- if (httpMsgIsPersistent(request->http_ver, req_hdr))
+ if (!Config.onoff.client_pconns)
+ request->flags.proxy_keepalive = 0;
+ else if (httpMsgIsPersistent(request->http_ver, req_hdr))
request->flags.proxy_keepalive = 1;
}
static int
clientCheckContentLength(request_t * r)
{
- int has_cont_len = (httpHeaderGetInt(&r->header, HDR_CONTENT_LENGTH) >= 0);
switch (r->method) {
case METHOD_PUT:
case METHOD_POST:
/* PUT/POST requires a request entity */
- return has_cont_len;
+ return (r->content_length >= 0);
case METHOD_GET:
case METHOD_HEAD:
/* We do not want to see a request entity on GET/HEAD requests */
- return !has_cont_len;
+ return (r->content_length <= 0);
default:
/* For other types of requests we don't care */
return 1;
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 (method == METHOD_PUT)
+ return 0;
+ if (method == METHOD_POST)
+ return 0; /* XXX POST may be cached sometimes.. ignored for now */
+ if (req->protocol == PROTO_GOPHER)
+ return gopherCachable(url);
if (req->protocol == PROTO_CACHEOBJ)
return 0;
return 1;
{
HttpHeader *hdr = rep ? &rep->header : 0;
const char *range_err = NULL;
- assert(http->request->range);
+ request_t *request = http->request;
+ assert(request->range);
/* check if we still want to do ranges */
if (!rep)
range_err = "no [parse-able] reply";
range_err = "canonization failed";
else if (httpHdrRangeIsComplex(http->request->range))
range_err = "too complex range header";
+ else if (!request->flags.cachable) /* from we_do_ranges in http.c */
+ range_err = "non-cachable request";
/* get rid of our range specs on error */
if (range_err) {
debug(33, 3) ("clientBuildRangeHeader: will not do ranges: %s.\n", range_err);
if (request->range)
clientBuildRangeHeader(http, rep);
/*
- * Add Age header, not that our header must replace Age headers
- * from other caches if any
+ * Add a estimated Age header on cache hits.
*/
- if (http->entry->timestamp > 0) {
+ if (is_hit) {
+ /*
+ * Remove any existing Age header sent by upstream caches
+ * (note that the existing header is passed along unmodified
+ * on cache misses)
+ */
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
+ * This adds the calculated object age. Note that the details of the
+ * age calculation is performed by adjusting the timestamp in
+ * storeTimestampsSet(), not here.
+ *
+ * BROWSER WORKAROUND: IE sometimes hangs when receiving a 0 Age
+ * header, so don't use it unless there is a age to report. Please
+ * note that Age is only used to make a conservative estimation of
+ * the objects age, so a Age: 0 header does not add any useful
+ * information to the reply in any case.
*/
- httpHeaderPutInt(hdr, HDR_AGE,
- http->entry->timestamp <= squid_curtime ?
- squid_curtime - http->entry->timestamp : 0);
- }
+ if (http->entry->timestamp > -1)
+ if (http->entry->timestamp < squid_curtime)
+ httpHeaderPutInt(hdr, HDR_AGE,
+ squid_curtime - http->entry->timestamp);
+ }
+ /* Handle authentication headers */
+ if (request->auth_user_request)
+ authenticateFixHeader(rep, request->auth_user_request, request, http->flags.accel);
/* Append X-Cache */
httpHeaderPutStrf(hdr, HDR_X_CACHE, "%s from %s",
is_hit ? "HIT" : "MISS", getMyHostname());
/* 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);
+ getMyHostname(), ntohs(Config.Sockaddr.http->s.sin_port));
#endif
- if (httpReplyBodySize(request->method, http->entry->mem_obj->reply) < 0) {
+ if (httpReplyBodySize(request->method, rep) < 0) {
debug(33, 3) ("clientBuildReplyHeader: can't keep-alive, unknown body size\n");
request->flags.proxy_keepalive = 0;
}
httpHeaderPutStr(hdr, HDR_X_REQUEST_URI,
http->entry->mem_obj->url ? http->entry->mem_obj->url : http->uri);
#endif
+ httpHdrMangleList(hdr, request);
}
static HttpReply *
clientBuildReply(clientHttpRequest * http, const char *buf, size_t size)
{
HttpReply *rep = httpReplyCreate();
- if (httpReplyParse(rep, buf)) {
+ size_t k = headersEnd(buf, size);
+ if (k && httpReplyParse(rep, buf, k)) {
/* enforce 1.0 reply version */
- rep->sline.version = 1.0;
+ httpBuildVersion(&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);
+ 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 */
+ if (http->request->range) {
+ /* this will fail and destroy request->range */
+ clientBuildRangeHeader(http, rep);
+ }
}
return rep;
}
http->log_type = LOG_TCP_SWAPFAIL_MISS;
if ((e = http->entry)) {
http->entry = NULL;
- storeUnregister(e, http);
+ storeUnregister(http->sc, e, http);
+ http->sc = NULL;
storeUnlockObject(e);
}
clientProcessMiss(http);
clientProcessMiss(http);
} else {
debug(33, 3) ("clientCacheHit: waiting for HTTP reply headers\n");
- storeClientCopy(e,
+ storeClientCopy(http->sc, e,
http->out.offset + size,
http->out.offset,
CLIENT_SOCK_SZ,
http->log_type = LOG_TCP_IMS_HIT;
clientSendMoreData(data, buf, size);
} else {
+ time_t timestamp = e->timestamp;
MemBuf mb = httpPacked304Reply(e->mem_obj->reply);
http->log_type = LOG_TCP_IMS_HIT;
memFree(buf, MEM_CLIENT_SOCK_BUF);
- storeUnregister(e, http);
+ storeUnregister(http->sc, e, http);
+ http->sc = NULL;
storeUnlockObject(e);
e = clientCreateStoreEntry(http, http->request->method, null_request_flags);
+ /*
+ * Copy timestamp from the original entry so the 304
+ * reply has a meaningful Age: header.
+ */
+ e->timestamp = timestamp;
http->entry = e;
- httpReplyParse(e->mem_obj->reply, mb.buf);
+ httpReplyParse(e->mem_obj->reply, mb.buf, mb.size);
storeAppend(e, mb.buf, mb.size);
memBufClean(&mb);
storeComplete(e);
memBufPrintf(mb, crlf);
}
-/* extracts a "range" from *buf and appends them to mb, updating all offsets and such */
+/*
+ * extracts a "range" from *buf and appends them to mb, updating
+ * all offsets and such.
+ */
static void
-clientPackRange(clientHttpRequest * http, HttpHdrRangeIter * i, const char **buf, ssize_t * size, MemBuf * mb)
+clientPackRange(clientHttpRequest * http,
+ HttpHdrRangeIter * i,
+ const char **buf,
+ ssize_t * size,
+ MemBuf * mb)
{
- const size_t copy_sz = i->debt_size <= *size ? i->debt_size : *size;
+ const ssize_t copy_sz = i->debt_size <= *size ? i->debt_size : *size;
off_t body_off = http->out.offset - i->prefix_size;
assert(*size > 0);
assert(i->spec);
-
- /* intersection of "have" and "need" ranges must not be empty */
+ /*
+ * intersection of "have" and "need" ranges must not be empty
+ */
assert(body_off < i->spec->offset + i->spec->length);
assert(body_off + *size > i->spec->offset);
-
- /* put boundary and headers at the beginning of a range in a multi-range */
+ /*
+ * put boundary and headers at the beginning of a range in a
+ * multi-range
+ */
if (http->request->range->specs.count > 1 && i->debt_size == i->spec->length) {
assert(http->entry->mem_obj);
clientPackRangeHdr(
mb
);
}
- /* append content */
+ /*
+ * append content
+ */
debug(33, 3) ("clientPackRange: appending %d bytes\n", copy_sz);
memBufAppend(mb, *buf, copy_sz);
-
- /* update offsets */
+ /*
+ * update offsets
+ */
*size -= copy_sz;
i->debt_size -= copy_sz;
body_off += copy_sz;
*buf += copy_sz;
http->out.offset = body_off + i->prefix_size; /* sync */
-
- /* paranoid check */
+ /*
+ * paranoid check
+ */
assert(*size >= 0 && i->debt_size >= 0);
}
return 0;
}
+static int
+clientRequestBodyTooLarge(int clen)
+{
+ if (0 == Config.maxRequestBodySize)
+ return 0; /* disabled */
+ if (clen < 0)
+ return 0; /* unknown, bug? */
+ if (clen > Config.maxRequestBodySize)
+ return 1; /* too large */
+ return 0;
+}
+
/*
* accepts chunk of a http message in buf, parses prefix, filters headers and
* such, writes processed message to the client's socket
if (rep && clientReplyBodyTooLarge(rep->content_length)) {
ErrorState *err = errorCon(ERR_TOO_BIG, HTTP_FORBIDDEN);
err->request = requestLink(http->request);
- storeUnregister(http->entry, http);
+ storeUnregister(http->sc, http->entry, http);
+ http->sc = NULL;
storeUnlockObject(http->entry);
http->entry = clientCreateStoreEntry(http, http->request->method,
null_request_flags);
errorAppendEntry(http->entry, err);
+ httpReplyDestroy(rep);
return;
} else if (rep) {
body_size = size - rep->hdr_sz;
body_size, rep->hdr_sz);
} else if (size < CLIENT_SOCK_SZ && entry->store_status == STORE_PENDING) {
/* wait for more to arrive */
- storeClientCopy(entry,
+ storeClientCopy(http->sc, entry,
http->out.offset + size,
http->out.offset,
CLIENT_SOCK_SZ,
}
/* reset range iterator */
http->range_iter.pos = HttpHdrRangeInitPos;
+ } else if (!http->request->range) {
+ /* Avoid copying to MemBuf for non-range requests */
+ /* Note, if we're here, then 'rep' is known to be NULL */
+ http->out.offset += body_size;
+ comm_write(fd, buf, size, clientWriteBodyComplete, http, NULL);
+ /* NULL because clientWriteBodyComplete frees it */
+ return;
}
if (http->request->method == METHOD_HEAD) {
if (rep) {
mb = httpReplyPack(rep);
http->out.offset += rep->hdr_sz;
check_size += rep->hdr_sz;
+#if HEADERS_LOG
+ headersLog(0, 0, http->request->method, rep);
+#endif
httpReplyDestroy(rep);
rep = NULL;
} else {
memFree(buf, MEM_CLIENT_SOCK_BUF);
}
+/*
+ * clientWriteBodyComplete is called for MEM_CLIENT_SOCK_BUF's
+ * written directly to the client socket, versus copying to a MemBuf
+ * and going through comm_write_mbuf. Most non-range responses after
+ * the headers probably go through here.
+ */
+static void
+clientWriteBodyComplete(int fd, char *buf, size_t size, int errflag, void *data)
+{
+ /*
+ * NOTE: clientWriteComplete doesn't currently use its "buf"
+ * (second) argument, so we pass in NULL.
+ */
+ clientWriteComplete(fd, NULL, size, errflag, data);
+ memFree(buf, MEM_CLIENT_SOCK_BUF);
+}
+
static void
clientKeepaliveNextRequest(clientHttpRequest * http)
{
if ((http = conn->chr) == NULL) {
debug(33, 5) ("clientKeepaliveNextRequest: FD %d reading next req\n",
conn->fd);
- fd_note(conn->fd, "Reading next request");
+ fd_note(conn->fd, "Waiting for next request");
/*
* Set the timeout BEFORE calling clientReadRequest().
*/
- commSetTimeout(conn->fd, 15, requestTimeout, conn);
+ commSetTimeout(conn->fd, Config.Timeout.pconn, requestTimeout, conn);
+ /*
+ * CYGWIN has a problem and is blocking on read() requests when there
+ * is no data present.
+ * This hack may hit performance a little, but it's better than
+ * blocking!.
+ */
+#ifdef _SQUID_CYGWIN_
+ commSetSelect(conn->fd, COMM_SELECT_READ, clientReadRequest, conn, 0);
+#else
clientReadRequest(conn->fd, conn); /* Read next request */
+#endif
/*
* Note, the FD may be closed at this point.
*/
debug(33, 1) ("clientKeepaliveNextRequest: FD %d Sending next\n",
conn->fd);
assert(entry);
- if (0 == storeClientCopyPending(entry, http)) {
+ if (0 == storeClientCopyPending(http->sc, entry, http)) {
if (EBIT_TEST(entry->flags, ENTRY_ABORTED))
debug(33, 0) ("clientKeepaliveNextRequest: ENTRY_ABORTED\n");
- storeClientCopy(entry,
+ storeClientCopy(http->sc, entry,
http->out.offset,
http->out.offset,
CLIENT_SOCK_SZ,
debug(33, 5) ("clientWriteComplete: FD %d, sz %d, err %d, off %d, len %d\n",
fd, size, errflag, (int) http->out.offset, entry ? objectLen(entry) : 0);
if (size > 0) {
- kb_incr(&Counter.client_http.kbytes_out, size);
+ kb_incr(&statCounter.client_http.kbytes_out, size);
if (isTcpHit(http->log_type))
- kb_incr(&Counter.client_http.hit_kbytes_out, size);
+ kb_incr(&statCounter.client_http.hit_kbytes_out, size);
}
if (errflag) {
/*
} else if ((done = clientCheckTransferDone(http)) != 0 || size == 0) {
debug(33, 5) ("clientWriteComplete: FD %d transfer is DONE\n", fd);
/* We're finished case */
- if (http->entry->mem_obj->reply->content_length < 0) {
+ if (httpReplyBodySize(http->request->method, entry->mem_obj->reply) < 0) {
debug(33, 5) ("clientWriteComplete: closing, content_length < 0\n");
comm_close(fd);
} else if (!done) {
* storage manager. */
if (EBIT_TEST(entry->flags, ENTRY_ABORTED))
debug(33, 0) ("clientWriteComplete 2: ENTRY_ABORTED\n");
- storeClientCopy(entry,
+ storeClientCopy(http->sc, entry,
http->out.offset,
http->out.offset,
CLIENT_SOCK_SZ,
err->request = requestLink(r);
err->src_addr = http->conn->peer.sin_addr;
if (http->entry) {
- storeUnregister(http->entry, http);
+ storeUnregister(http->sc, http->entry, http);
+ http->sc = NULL;
storeUnlockObject(http->entry);
}
http->entry = clientCreateStoreEntry(http, r->method, null_request_flags);
errorAppendEntry(http->entry, err);
}
+/*
+ * Return true if we should force a cache miss on this range request.
+ * entry must be non-NULL.
+ */
+static int
+clientCheckRangeForceMiss(StoreEntry * entry, HttpHdrRange * range)
+{
+ /*
+ * If the range_offset_limit is NOT in effect, there
+ * is no reason to force a miss.
+ */
+ if (0 == httpHdrRangeOffsetLimit(range))
+ return 0;
+ /*
+ * Here, we know it's possibly a hit. If we already have the
+ * whole object cached, we won't force a miss.
+ */
+ if (STORE_OK == entry->store_status)
+ return 0; /* we have the whole object */
+ /*
+ * Now we have a hit on a PENDING object. We need to see
+ * if the part we want is already cached. If so, we don't
+ * force a miss.
+ */
+ assert(NULL != entry->mem_obj);
+ if (httpHdrRangeFirstOffset(range) <= entry->mem_obj->inmem_hi)
+ return 0;
+ /*
+ * Even though we have a PENDING copy of the object, we
+ * don't want to wait to reach the first range offset,
+ * so we force a miss for a new range request to the
+ * origin.
+ */
+ return 1;
+}
+
static log_type
clientProcessRequest2(clientHttpRequest * http)
{
/* We can generate a HEAD reply from a cached GET object */
e = http->entry = storeGetPublic(http->uri, METHOD_GET);
}
+ /* Release negatively cached IP-cache entries on reload */
+ if (r->flags.nocache)
+ ipcacheInvalidate(r->host);
+#if HTTP_VIOLATIONS
+ else if (r->flags.nocache_hack)
+ ipcacheInvalidate(r->host);
+#endif
#if USE_CACHE_DIGESTS
http->lookup_type = e ? "HIT" : "MISS";
#endif
http->entry = e;
return LOG_TCP_HIT;
}
+ if (http->redirect.status) {
+ /* force this to be a miss */
+ http->entry = NULL;
+ return LOG_TCP_MISS;
+ }
if (!storeEntryValidToSend(e)) {
debug(33, 3) ("clientProcessRequest2: !storeEntryValidToSend MISS\n");
http->entry = NULL;
return LOG_TCP_HIT;
}
#if HTTP_VIOLATIONS
- if (r->flags.nocache_hack) {
- /* if nocache_hack is set, nocache should always be clear, right? */
- assert(!r->flags.nocache);
- ipcacheReleaseInvalid(r->host);
- /* continue! */
- }
if (e->store_status == STORE_PENDING) {
if (r->flags.nocache || r->flags.nocache_hack) {
debug(33, 3) ("Clearing no-cache for STORE_PENDING request\n\t%s\n",
if (r->flags.nocache) {
debug(33, 3) ("clientProcessRequest2: no-cache REFRESH MISS\n");
http->entry = NULL;
- ipcacheReleaseInvalid(r->host);
+ ipcacheInvalidate(r->host);
return LOG_TCP_CLIENT_REFRESH_MISS;
}
- if (r->range && httpHdrRangeWillBeComplex(r->range)) {
+ if (NULL == r->range) {
+ (void) 0;
+ } else if (httpHdrRangeWillBeComplex(r->range)) {
/*
* Some clients break if we return "200 OK" for a Range
* request. We would have to return "200 OK" for a _complex_
debug(33, 3) ("clientProcessRequest2: complex range MISS\n");
http->entry = NULL;
return LOG_TCP_MISS;
+ } else if (clientCheckRangeForceMiss(e, r->range)) {
+ debug(33, 3) ("clientProcessRequest2: forcing miss due to range_offset_limit\n");
+ http->entry = NULL;
+ return LOG_TCP_MISS;
}
debug(33, 3) ("clientProcessRequest2: default HIT\n");
http->entry = e;
request_t *r = http->request;
int fd = http->conn->fd;
HttpReply *rep;
+ http_version_t version;
debug(33, 4) ("clientProcessRequest: %s '%s'\n",
RequestMethodStr[r->method],
url);
if (r->method == METHOD_CONNECT) {
http->log_type = LOG_TCP_MISS;
- sslStart(fd, url, r, &http->out.size);
+ sslStart(fd, url, r, &http->out.size, &http->al.http.code);
return;
} else if (r->method == METHOD_PURGE) {
clientPurgeRequest(http);
storeReleaseRequest(http->entry);
storeBuffer(http->entry);
rep = httpReplyCreate();
- httpReplySetHeaders(rep, 1.0, HTTP_OK, NULL, "text/plain",
+ httpBuildVersion(&version, 1, 0);
+ httpReplySetHeaders(rep, version, HTTP_OK, NULL, "text/plain",
httpRequestPrefixLen(r), 0, squid_curtime);
httpReplySwapOut(rep, http->entry);
httpReplyDestroy(rep);
}
/* yes, continue */
http->log_type = LOG_TCP_MISS;
- } else if (r->body) {
- http->log_type = LOG_TCP_MISS;
- /* XXX oof, POST can be cached! */
- pumpInit(fd, r, http->uri);
} else {
http->log_type = clientProcessRequest2(http);
}
storeLockObject(http->entry);
storeCreateMemObject(http->entry, http->uri, http->log_uri);
http->entry->mem_obj->method = r->method;
- storeClientListAdd(http->entry, http);
+ http->sc = storeClientListAdd(http->entry, http);
#if DELAY_POOLS
- delaySetStoreClient(http->entry, http, delayClient(r));
-#endif
- http->entry->refcount++;
-#if HEAP_REPLACEMENT
- storeHeapPositionUpdate(http->entry);
+ delaySetStoreClient(http->sc, delayClient(r));
#endif
- storeClientCopy(http->entry,
+ storeClientCopy(http->sc, http->entry,
http->out.offset,
http->out.offset,
CLIENT_SOCK_SZ,
clientCacheHit,
http);
} else {
- /* MISS CASE */
- http->log_type = LOG_TCP_MISS;
+ /* MISS CASE, http->log_type is already set! */
clientProcessMiss(http);
}
}
* or IMS request.
*/
if (http->entry) {
- if (EBIT_TEST(http->entry->flags, ENTRY_SPECIAL))
+ if (EBIT_TEST(http->entry->flags, ENTRY_SPECIAL)) {
debug(33, 0) ("clientProcessMiss: miss on a special object (%s).\n", url);
- storeUnregister(http->entry, http);
+ debug(33, 0) ("\tlog_type = %s\n", log_tags[http->log_type]);
+ storeEntryDump(http->entry, 1);
+ }
+ storeUnregister(http->sc, http->entry, http);
+ http->sc = NULL;
storeUnlockObject(http->entry);
http->entry = NULL;
}
}
assert(http->out.offset == 0);
http->entry = clientCreateStoreEntry(http, r->method, r->flags);
- http->entry->refcount++;
-#if HEAP_REPLACEMENT
- storeHeapPositionUpdate(http->entry);
-#endif
if (http->redirect.status) {
HttpReply *rep = httpReplyCreate();
+#if LOG_TCP_REDIRECTS
+ http->log_type = LOG_TCP_REDIRECT;
+#endif
storeReleaseRequest(http->entry);
httpRedirectReply(rep, http->redirect.status, http->redirect.location);
httpReplySwapOut(rep, http->entry);
}
if (http->flags.internal)
r->protocol = PROTO_INTERNAL;
- fwdStart(http->conn->fd, http->entry, r,
- http->conn->peer.sin_addr, http->conn->me.sin_addr);
+ fwdStart(http->conn->fd, http->entry, r);
}
static clientHttpRequest *
parseHttpRequestAbort(ConnStateData * conn, const char *uri)
{
- clientHttpRequest *http = xcalloc(1, sizeof(clientHttpRequest));
- cbdataAdd(http, cbdataXfree, 0);
+ clientHttpRequest *http;
+ http = CBDATA_ALLOC(clientHttpRequest, NULL);
http->conn = conn;
http->start = current_time;
http->req_sz = conn->in.offset;
char *mstr = NULL;
char *url = NULL;
char *req_hdr = NULL;
- float http_ver;
+ http_version_t http_ver;
char *token = NULL;
char *t = NULL;
char *end;
- int free_request = 0;
size_t header_sz; /* size of headers, not including first line */
size_t prefix_sz; /* size of whole request (req-line + headers) */
size_t url_sz;
#if IPF_TRANSPARENT
struct natlookup natLookup;
static int natfd = -1;
+ static int siocgnatl_cmd = SIOCGNATL & 0xff;
+ int x;
+#endif
+#if LINUX_NETFILTER
+ size_t sock_sz = sizeof(conn->me);
#endif
if ((req_sz = headersEnd(conn->in.buf, conn->in.offset)) == 0) {
if (token == NULL) {
debug(33, 3) ("parseHttpRequest: Missing HTTP identifier\n");
#if RELAXED_HTTP_PARSER
- http_ver = (float) 0.9; /* wild guess */
+ httpBuildVersion(&http_ver, 0, 9); /* wild guess */
#else
return parseHttpRequestAbort(conn, "error:missing-http-ident");
#endif
} else {
- http_ver = (float) atof(token + 5);
+ if (sscanf(token + 5, "%d.%d", &http_ver.major, &http_ver.minor) != 2) {
+ debug(33, 3) ("parseHttpRequest: Invalid HTTP identifier.\n");
+ return parseHttpRequestAbort(conn, "error: invalid HTTP-ident");
+ }
+ debug(33, 6) ("parseHttpRequest: Client HTTP version %d.%d.\n", http_ver.major, http_ver.minor);
}
/*
assert(prefix_sz <= conn->in.offset);
/* Ok, all headers are received */
- http = xcalloc(1, sizeof(clientHttpRequest));
- cbdataAdd(http, cbdataXfree, 0);
+ http = CBDATA_ALLOC(clientHttpRequest, NULL);
http->http_ver = http_ver;
http->conn = conn;
http->start = current_time;
else if (Config2.Accel.on && *url == '/') {
/* prepend the accel prefix */
if (opt_accel_uses_host && (t = mime_get_header(req_hdr, "Host"))) {
+ int vport = (int) Config.Accel.port;
+ char *q;
/* If a Host: header was specified, use it to build the URL
* instead of the one in the Config file. */
/*
* refer to www.playboy.com. The 'dst' and/or 'dst_domain' ACL
* types should be used to prevent httpd-accelerators
* handling requests for non-local servers */
- strtok(t, " :/;@");
+ strtok(t, " /;@");
+ if ((q = strchr(t, ':'))) {
+ *q++ = '\0';
+ vport = atoi(q);
+ }
url_sz = strlen(url) + 32 + Config.appendDomainLen +
strlen(t);
http->uri = xcalloc(url_sz, 1);
snprintf(http->uri, url_sz, "http://%s:%d%s",
- t, (int) Config.Accel.port, url);
+ t, vport, url);
} else if (vhost_mode) {
+ int vport;
/* Put the local socket IP address as the hostname */
url_sz = strlen(url) + 32 + Config.appendDomainLen;
http->uri = xcalloc(url_sz, 1);
+ if (vport_mode)
+ vport = (int) ntohs(http->conn->me.sin_port);
+ else
+ vport = (int) Config.Accel.port;
#if IPF_TRANSPARENT
natLookup.nl_inport = http->conn->me.sin_port;
natLookup.nl_outport = http->conn->peer.sin_port;
xstrerror());
return parseHttpRequestAbort(conn, "error:nat-open-failed");
}
- if (ioctl(natfd, SIOCGNATL, &natLookup) < 0) {
+ /*
+ * IP-Filter changed the type for SIOCGNATL between
+ * 3.3 and 3.4. It also changed the cmd value for
+ * SIOCGNATL, so at least we can detect it. We could
+ * put something in configure and use ifdefs here, but
+ * this seems simpler.
+ */
+ if (63 == siocgnatl_cmd) {
+ struct natlookup *nlp = &natLookup;
+ x = ioctl(natfd, SIOCGNATL, &nlp);
+ } else {
+ x = ioctl(natfd, SIOCGNATL, &natLookup);
+ }
+ if (x < 0) {
if (errno != ESRCH) {
debug(50, 1) ("parseHttpRequest: NAT lookup failed: ioctl(SIOCGNATL)\n");
close(natfd);
} else
snprintf(http->uri, url_sz, "http://%s:%d%s",
inet_ntoa(http->conn->me.sin_addr),
- (int) Config.Accel.port,
- url);
+ vport, url);
} else
snprintf(http->uri, url_sz, "http://%s:%d%s",
inet_ntoa(natLookup.nl_realip),
- (int) Config.Accel.port,
- url);
+ vport, url);
#else
+#if LINUX_NETFILTER
+ /* If the call fails the address structure will be unchanged */
+ getsockopt(conn->fd, SOL_IP, SO_ORIGINAL_DST, &conn->me, &sock_sz);
+ debug(33, 5) ("parseHttpRequest: addr = %s", inet_ntoa(conn->me.sin_addr));
+#endif
snprintf(http->uri, url_sz, "http://%s:%d%s",
inet_ntoa(http->conn->me.sin_addr),
- (int) Config.Accel.port,
- url);
+ vport, url);
#endif
debug(33, 5) ("VHOST REWRITE: '%s'\n", http->uri);
} else {
strcpy(http->uri, url);
http->flags.accel = 0;
}
- if (!stringHasWhitespace(http->uri))
+ if (!stringHasCntl(http->uri))
http->log_uri = xstrndup(http->uri, MAX_URL);
else
- http->log_uri = xstrndup(rfc1738_escape(http->uri), MAX_URL);
+ http->log_uri = xstrndup(rfc1738_escape_unescaped(http->uri), MAX_URL);
debug(33, 5) ("parseHttpRequest: Complete request received\n");
- if (free_request)
- safe_free(url);
xfree(inbuf);
*status = 1;
return http;
clientReadDefer(int fdnotused, void *data)
{
ConnStateData *conn = data;
- return conn->defer.until > squid_curtime;
+ if (conn->body.size_left)
+ return conn->in.offset >= conn->in.size;
+ else
+ return conn->defer.until > squid_curtime;
}
static void
int k;
request_t *request = NULL;
int size;
- int cont_len;
+ void *p;
method_t method;
clientHttpRequest *http = NULL;
clientHttpRequest **H = NULL;
fde *F = &fd_table[fd];
int len = conn->in.size - conn->in.offset - 1;
debug(33, 4) ("clientReadRequest: FD %d: reading request...\n", fd);
- Counter.syscalls.sock.reads++;
+ statCounter.syscalls.sock.reads++;
size = read(fd, conn->in.buf + conn->in.offset, len);
if (size > 0) {
fd_bytes(fd, size, FD_READ);
- kb_incr(&Counter.client_http.kbytes_in, size);
+ kb_incr(&statCounter.client_http.kbytes_in, size);
}
/*
* Don't reset the timeout value here. The timeout value will be
* whole, not individual read() calls. Plus, it breaks our
* lame half-close detection
*/
- commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, conn, 0);
- if (size == 0) {
- if (conn->chr == NULL) {
+ if (size > 0) {
+ conn->in.offset += size;
+ conn->in.buf[conn->in.offset] = '\0'; /* Terminate the string */
+ } else if (size == 0 && len > 0) {
+ if (conn->chr == NULL && conn->in.offset == 0) {
/* no current or pending requests */
+ debug(33, 4) ("clientReadRequest: FD %d closed\n", fd);
comm_close(fd);
return;
} else if (!Config.onoff.half_closed_clients) {
/* admin doesn't want to support half-closed client sockets */
+ debug(33, 3) ("clientReadRequest: FD %d aborted (half_closed_clients disabled)\n", fd);
comm_close(fd);
return;
}
conn->defer.until = squid_curtime + 1;
conn->defer.n++;
fd_note(fd, "half-closed");
- return;
+ /* There is one more close check at the end, to detect aborted
+ * (partial) requests. At this point we can't tell if the request
+ * is partial.
+ */
+ /* Continue to process previously read data */
} else if (size < 0) {
if (!ignoreErrno(errno)) {
debug(50, 2) ("clientReadRequest: FD %d: %s\n", fd, xstrerror());
return;
}
/* Continue to process previously read data */
- size = 0;
}
- conn->in.offset += size;
- /* Skip leading (and trailing) whitespace */
- while (conn->in.offset > 0) {
+ commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, conn, 0);
+ /* Process request body if any */
+ if (conn->in.offset > 0 && conn->body.callback != NULL)
+ clientProcessBody(conn);
+ /* Process next request */
+ while (conn->in.offset > 0 && conn->body.size_left == 0) {
int nrequests;
size_t req_line_sz;
+ /* Skip leading (and trailing) whitespace */
while (conn->in.offset > 0 && xisspace(conn->in.buf[0])) {
xmemmove(conn->in.buf, conn->in.buf + 1, conn->in.offset - 1);
conn->in.offset--;
conn->defer.until = squid_curtime + 100; /* Reset when a request is complete */
break;
}
+ conn->in.buf[conn->in.offset] = '\0'; /* Terminate the string */
+ if (nrequests == 0)
+ fd_note(conn->fd, "Reading next request");
/* Process request */
http = parseHttpRequest(conn,
&method,
for (H = &conn->chr; *H; H = &(*H)->next);
*H = http;
conn->nrequests++;
- commSetTimeout(fd, Config.Timeout.lifetime, NULL, NULL);
+ /*
+ * I wanted to lock 'http' here since its callback data for
+ * clientLifetimeTimeout(), but there's no logical place to
+ * cbdataUnlock if the timeout never happens. Maybe its safe
+ * enough to assume that if the FD is open, and the timeout
+ * triggers, that 'http' is valid.
+ */
+ commSetTimeout(fd, Config.Timeout.lifetime, clientLifetimeTimeout, http);
if (parser_return_code < 0) {
debug(33, 1) ("clientReadRequest: FD %d Invalid Request\n", fd);
err = errorCon(ERR_INVALID_REQ, HTTP_BAD_REQUEST);
request->flags.accelerated = http->flags.accel;
if (!http->flags.internal) {
if (internalCheck(strBuf(request->urlpath))) {
- if (0 == strcasecmp(request->host, internalHostname()) &&
- request->port == Config.Port.http->i) {
+ if (internalHostnameIs(request->host) &&
+ request->port == ntohs(Config.Sockaddr.http->s.sin_port)) {
http->flags.internal = 1;
} else if (internalStaticCheck(strBuf(request->urlpath))) {
xstrncpy(request->host, internalHostname(), SQUIDHOSTNAMELEN);
- request->port = Config.Port.http->i;
+ request->port = ntohs(Config.Sockaddr.http->s.sin_port);
http->flags.internal = 1;
}
}
}
+ /*
+ * cache the Content-length value in request_t.
+ */
+ request->content_length = httpHeaderGetInt(&request->header,
+ HDR_CONTENT_LENGTH);
request->flags.internal = http->flags.internal;
safe_free(prefix);
safe_free(http->log_uri);
http->log_uri = xstrdup(urlCanonicalClean(request));
request->client_addr = conn->peer.sin_addr;
request->my_addr = conn->me.sin_addr;
+ request->my_port = ntohs(conn->me.sin_port);
request->http_ver = http->http_ver;
if (!urlCheckRequest(request)) {
err = errorCon(ERR_UNSUP_REQ, HTTP_NOT_IMPLEMENTED);
errorAppendEntry(http->entry, err);
break;
}
- if (0 == clientCheckContentLength(request)) {
+ if (!clientCheckContentLength(request)) {
err = errorCon(ERR_INVALID_REQ, HTTP_LENGTH_REQUIRED);
err->src_addr = conn->peer.sin_addr;
err->request = requestLink(request);
break;
}
http->request = requestLink(request);
- /*
- * We need to set the keepalive flag before doing some
- * hacks for POST/PUT requests below. Maybe we could
- * set keepalive flag even earlier.
- */
clientSetKeepaliveFlag(http);
- /*
- * break here if the request has a content-length
- * because there is a reqeust body following and we
- * don't want to parse it as though it was new request.
- */
- cont_len = httpHeaderGetInt(&request->header, HDR_CONTENT_LENGTH);
- if (cont_len >= 0) {
- int copy_len = XMIN(conn->in.offset, cont_len);
- if (copy_len > 0) {
- assert(conn->in.offset >= copy_len);
- request->body_sz = copy_len;
- request->body = xmalloc(request->body_sz);
- xmemcpy(request->body, conn->in.buf, request->body_sz);
- conn->in.offset -= copy_len;
- if (conn->in.offset)
- xmemmove(conn->in.buf, conn->in.buf + copy_len, conn->in.offset);
- }
- /*
- * if we didn't get the full body now, then more will
- * be arriving on the client socket. Lets cancel
- * the read handler until this request gets forwarded.
- */
- if (request->body_sz < cont_len)
- commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
- if (cont_len < 0)
- (void) 0;
- else if (cont_len > Config.maxRequestBodySize) {
+ /* Do we expect a request-body? */
+ if (request->content_length > 0) {
+ conn->body.size_left = request->content_length;
+ request->body_connection = conn;
+ /* Is it too large? */
+ if (clientRequestBodyTooLarge(request->content_length)) {
err = errorCon(ERR_TOO_BIG, HTTP_REQUEST_ENTITY_TOO_LARGE);
err->request = requestLink(request);
http->entry = clientCreateStoreEntry(http,
}
}
clientAccessCheck(http);
- continue; /* while offset > 0 */
+ continue; /* while offset > 0 && body.size_left == 0 */
} else if (parser_return_code == 0) {
/*
* Partial request received; reschedule until parseHttpRequest()
if (k == 0) {
if (conn->in.offset >= Config.maxRequestHeaderSize) {
/* The request is too large to handle */
- debug(33, 0) ("Request won't fit in buffer.\n");
- debug(33, 0) ("Config 'request_header_max_size'= %d bytes.\n",
- Config.maxRequestHeaderSize);
- debug(33, 0) ("This request = %d bytes.\n",
+ debug(33, 1) ("Request header is too large (%d bytes)\n",
(int) conn->in.offset);
+ debug(33, 1) ("Config 'request_header_max_size'= %d bytes.\n",
+ Config.maxRequestHeaderSize);
err = errorCon(ERR_TOO_BIG, HTTP_REQUEST_ENTITY_TOO_LARGE);
http = parseHttpRequestAbort(conn, "error:request-too-large");
/* add to the client request queue */
return;
}
/* Grow the request memory area to accomodate for a large request */
- conn->in.size += REQUEST_BUF_SIZE;
- conn->in.buf = xrealloc(conn->in.buf, conn->in.size);
+ conn->in.size += CLIENT_REQ_BUF_SZ;
+ if (conn->in.size == 2 * CLIENT_REQ_BUF_SZ) {
+ p = conn->in.buf; /* get rid of fixed size Pooled buffer */
+ conn->in.buf = xcalloc(2, CLIENT_REQ_BUF_SZ);
+ xmemcpy(conn->in.buf, p, CLIENT_REQ_BUF_SZ);
+ memFree(p, MEM_CLIENT_REQ_BUF);
+ } else
+ conn->in.buf = xrealloc(conn->in.buf, conn->in.size);
/* XXX account conn->in.buf */
debug(33, 3) ("Handling a large request, offset=%d inbufsize=%d\n",
(int) conn->in.offset, conn->in.size);
}
break;
}
+ } /* while offset > 0 && conn->body.size_left == 0 */
+ /* Check if a half-closed connection was aborted in the middle */
+ if (F->flags.socket_eof) {
+ if (conn->in.offset != conn->body.size_left) { /* != 0 when no request body */
+ /* Partial request received. Abort client connection! */
+ debug(33, 3) ("clientReadRequest: FD %d aborted\n", fd);
+ comm_close(fd);
+ return;
+ }
+ }
+}
+
+/* file_read like function, for reading body content */
+void
+clientReadBody(request_t * request, char *buf, size_t size, CBCB * callback, void *cbdata)
+{
+ ConnStateData *conn = request->body_connection;
+ if (!conn) {
+ debug(33, 5) ("clientReadBody: no body to read, request=%p\n", request);
+ callback(buf, 0, cbdata); /* Signal end of body */
+ return;
+ }
+ debug(33, 2) ("clientReadBody: start fd=%d body_size=%d in.offset=%d cb=%p req=%p\n", conn->fd, conn->body.size_left, conn->in.offset, callback, request);
+ conn->body.callback = callback;
+ conn->body.cbdata = cbdata;
+ conn->body.buf = buf;
+ conn->body.bufsize = size;
+ conn->body.request = requestLink(request);
+ if (conn->in.offset) {
+ /* Data available */
+ clientProcessBody(conn);
+ } else {
+ debug(33, 2) ("clientReadBody: fd %d wait for clientReadRequest\n", conn->fd);
+ }
+}
+
+/* Called by clientReadRequest to process body content */
+static void
+clientProcessBody(ConnStateData * conn)
+{
+ int size;
+ char *buf = conn->body.buf;
+ void *cbdata = conn->body.cbdata;
+ CBCB *callback = conn->body.callback;
+ request_t *request = conn->body.request;
+ /* Note: request is null while eating "aborted" transfers */
+ debug(33, 2) ("clientProcessBody: start fd=%d body_size=%d in.offset=%d cb=%p req=%p\n", conn->fd, conn->body.size_left, conn->in.offset, callback, request);
+ /* Some sanity checks... */
+ assert(conn->body.size_left > 0);
+ assert(conn->in.offset > 0);
+ assert(callback != NULL);
+ assert(buf != NULL);
+ /* How much do we have to process? */
+ size = conn->in.offset;
+ if (size > conn->body.size_left) /* only process the body part */
+ size = conn->body.size_left;
+ if (size > conn->body.bufsize) /* don't copy more than requested */
+ size = conn->body.bufsize;
+ xmemcpy(buf, conn->in.buf, size);
+ conn->body.size_left -= size;
+ /* Move any remaining data */
+ conn->in.offset -= size;
+ if (conn->in.offset > 0)
+ xmemmove(conn->in.buf, conn->in.buf + size, conn->in.offset);
+ /* Remove request link if this is the last part of the body, as
+ * clientReadRequest automatically continues to process next request */
+ if (conn->body.size_left <= 0 && request != NULL)
+ request->body_connection = NULL;
+ /* Remove clientReadBody arguments (the call is completed) */
+ conn->body.request = NULL;
+ conn->body.callback = NULL;
+ conn->body.buf = NULL;
+ conn->body.bufsize = 0;
+ /* Remember that we have touched the body, not restartable */
+ if (request != NULL)
+ request->flags.body_sent = 1;
+ /* Invoke callback function */
+ callback(buf, size, cbdata);
+ if (request != NULL)
+ requestUnlink(request); /* Linked in clientReadBody */
+ debug(33, 2) ("clientProcessBody: end fd=%d size=%d body_size=%d in.offset=%d cb=%p req=%p\n", conn->fd, size, conn->body.size_left, conn->in.offset, callback, request);
+ return;
+}
+
+/* A dummy handler that throws away a request-body */
+static char bodyAbortBuf[SQUID_TCP_SO_RCVBUF];
+void
+clientReadBodyAbortHandler(char *buf, size_t size, void *data)
+{
+ ConnStateData *conn = (ConnStateData *) data;
+ debug(33, 2) ("clientReadBodyAbortHandler: fd=%d body_size=%d in.offset=%d\n", conn->fd, conn->body.size_left, conn->in.offset);
+ if (size != 0 && conn->body.size_left != 0) {
+ debug(33, 3) ("clientReadBodyAbortHandler: fd=%d shedule next read\n", conn->fd);
+ conn->body.callback = clientReadBodyAbortHandler;
+ conn->body.buf = bodyAbortBuf;
+ conn->body.bufsize = sizeof(bodyAbortBuf);
+ conn->body.cbdata = data;
}
}
+/* Abort a body request */
+int
+clientAbortBody(request_t * request)
+{
+ ConnStateData *conn = request->body_connection;
+ char *buf;
+ CBCB *callback;
+ void *cbdata;
+ request->body_connection = NULL;
+ if (!conn || conn->body.size_left <= 0)
+ return 0; /* No body to abort */
+ if (conn->body.callback != NULL) {
+ buf = conn->body.buf;
+ callback = conn->body.callback;
+ cbdata = conn->body.cbdata;
+ assert(request == conn->body.request);
+ conn->body.buf = NULL;
+ conn->body.callback = NULL;
+ conn->body.cbdata = NULL;
+ conn->body.request = NULL;
+ callback(buf, -1, cbdata); /* Signal abort to clientReadBody caller */
+ requestUnlink(request);
+ }
+ clientReadBodyAbortHandler(NULL, -1, conn); /* Install abort handler */
+ /* clientProcessBody() */
+ return 1; /* Aborted */
+}
+
/* general lifetime handler for HTTP requests */
static void
requestTimeout(int fd, void *data)
{
+#if THIS_CONFUSES_PERSISTENT_CONNECTION_AWARE_BROWSERS_AND_USERS
ConnStateData *conn = data;
ErrorState *err;
debug(33, 3) ("requestTimeout: FD %d: lifetime is expired.\n", fd);
* if we don't close() here, we still need a timeout handler!
*/
commSetTimeout(fd, 30, requestTimeout, conn);
+ /*
+ * Aha, but we don't want a read handler!
+ */
+ commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
}
+#else
+ /*
+ * Just close the connection to not confuse browsers
+ * using persistent connections. Some browsers opens
+ * an connection and then does not use it until much
+ * later (presumeably because the request triggering
+ * the open has already been completed on another
+ * connection)
+ */
+ debug(33, 3) ("requestTimeout: FD %d: lifetime is expired.\n", fd);
+ comm_close(fd);
+#endif
+}
+
+static void
+clientLifetimeTimeout(int fd, void *data)
+{
+ clientHttpRequest *http = data;
+ ConnStateData *conn = http->conn;
+ debug(33, 1) ("WARNING: Closing client %s connection due to lifetime timeout\n",
+ inet_ntoa(conn->peer.sin_addr));
+ debug(33, 1) ("\t%s\n", http->uri);
+ comm_close(fd);
}
static int
-httpAcceptDefer(void)
+httpAcceptDefer(int fdunused, void *dataunused)
{
static time_t last_warn = 0;
if (fdNFree() >= RESERVED_FD)
static aclCheck_t identChecklist;
#endif
commSetSelect(sock, COMM_SELECT_READ, httpAccept, NULL, 0);
- while (max-- && !httpAcceptDefer()) {
+ while (max-- && !httpAcceptDefer(sock, NULL)) {
memset(&peer, '\0', sizeof(struct sockaddr_in));
memset(&me, '\0', sizeof(struct sockaddr_in));
if ((fd = comm_accept(sock, &peer, &me)) < 0) {
break;
}
debug(33, 4) ("httpAccept: FD %d: accepted\n", fd);
- connState = xcalloc(1, sizeof(ConnStateData));
+ connState = CBDATA_ALLOC(ConnStateData, NULL);
connState->peer = peer;
connState->log_addr = peer.sin_addr;
connState->log_addr.s_addr &= Config.Addrs.client_netmask.s_addr;
connState->me = me;
connState->fd = fd;
- connState->in.size = REQUEST_BUF_SIZE;
- connState->in.buf = xcalloc(connState->in.size, 1);
- cbdataAdd(connState, cbdataXfree, 0);
+ connState->in.size = CLIENT_REQ_BUF_SZ;
+ connState->in.buf = memAllocate(MEM_CLIENT_REQ_BUF);
/* XXX account connState->in.buf */
comm_add_close_handler(fd, connStateFree, connState);
if (Config.onoff.log_fqdn)
#if USE_IDENT
identChecklist.src_addr = peer.sin_addr;
identChecklist.my_addr = me.sin_addr;
+ identChecklist.my_port = ntohs(me.sin_port);
if (aclCheckFast(Config.accessList.identLookup, &identChecklist))
identStart(&me, &peer, clientIdentDone, connState);
#endif
commSetSelect(fd, COMM_SELECT_READ, clientReadRequest, connState, 0);
commSetDefer(fd, clientReadDefer, connState);
+ clientdbEstablished(peer.sin_addr, 1);
+ assert(N);
(*N)++;
}
}
static int
clientGotNotEnough(clientHttpRequest * http)
{
- int cl = http->entry->mem_obj->reply->content_length;
+ int cl = httpReplyBodySize(http->request->method, http->entry->mem_obj->reply);
int hs = http->entry->mem_obj->reply->hdr_sz;
assert(cl >= 0);
if (http->out.offset < cl + hs)
void
clientHttpConnectionsOpen(void)
{
- ushortlist *u;
+ sockaddr_in_list *s;
int fd;
- for (u = Config.Port.http; u; u = u->next) {
+ for (s = Config.Sockaddr.http; s; s = s->next) {
+ if (MAXHTTPPORTS == NHttpSockets) {
+ debug(1, 1) ("WARNING: You have too many 'http_port' lines.\n");
+ debug(1, 1) (" The limit is %d\n", MAXHTTPPORTS);
+ continue;
+ }
enter_suid();
fd = comm_open(SOCK_STREAM,
0,
- Config.Addrs.tcp_incoming,
- u->i,
+ s->s.sin_addr,
+ ntohs(s->s.sin_port),
COMM_NONBLOCKING,
"HTTP Socket");
leave_suid();
continue;
comm_listen(fd);
commSetSelect(fd, COMM_SELECT_READ, httpAccept, NULL, 0);
- /*commSetDefer(fd, httpAcceptDefer, NULL); */
- debug(1, 1) ("Accepting HTTP connections on port %d, FD %d.\n",
- (int) u->i, fd);
+ /*
+ * We need to set a defer handler here so that we don't
+ * peg the CPU with select() when we hit the FD limit.
+ */
+ commSetDefer(fd, httpAcceptDefer, NULL);
+ debug(1, 1) ("Accepting HTTP connections at %s, port %d, FD %d.\n",
+ inet_ntoa(s->s.sin_addr),
+ (int) ntohs(s->s.sin_port),
+ fd);
HttpSockets[NHttpSockets++] = fd;
}
if (NHttpSockets < 1)