/*
- * $Id: client_side.cc,v 1.495 2000/07/18 06:16:41 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
#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 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 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)
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
static aclCheck_t *
ConnStateData *conn = http->conn;
ch = aclChecklistCreate(acl,
http->request,
- 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...
*/
+ /* connection oriented auth also needs these two lines for it's operation. */
ch->conn = conn;
cbdataLock(ch->conn);
-#endif
+
return ch;
}
{
clientHttpRequest *http = data;
if (checkAccelOnly(http)) {
- clientAccessCheckDone(ACCESS_ALLOWED, 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);
/*
* 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)
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 == 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);
}
}
new_request->my_addr = old_request->my_addr;
new_request->my_port = old_request->my_port;
new_request->flags.redirected = 1;
- if (old_request->user_ident[0])
- xstrncpy(new_request->user_ident, old_request->user_ident,
- USER_IDENT_SZ);
- 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);
}
StoreEntry *entry;
ErrorState *err = NULL;
HttpReply *r;
+ 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;
/* 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);
}
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
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);
}
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 defined(USE_USERAGENT_LOG) || defined(USE_REFERER_LOG)
const char *str;
-#endif
request->imslen = -1;
request->ims = httpHeaderGetTime(req_hdr, HDR_IF_MODIFIED_SINCE);
if (request->ims > 0)
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)
{
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 (!Config.onoff.client_pconns)
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;
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());
httpHeaderPutStr(hdr, HDR_X_REQUEST_URI,
http->entry->mem_obj->url ? http->entry->mem_obj->url : http->uri);
#endif
+ httpHdrMangleList(hdr, request);
}
static HttpReply *
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 = 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, 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.
*/
}
/*
- * Return true if the first range offset is larger than the configured
- * limit, UNLESS the request is a hit AND the bits we want are in
- * the cache.
+ * Return true if we should force a cache miss on this range request.
+ * entry must be non-NULL.
*/
static int
-clientCheckRangeOffsetLimit(StoreEntry * entry, HttpHdrRange * range)
+clientCheckRangeForceMiss(StoreEntry * entry, HttpHdrRange * range)
{
- ssize_t range_start;
- /*
- * If the range offset limit is disabled, don't force a miss.
- */
- if (-1 == Config.rangeOffsetLimit)
- return 0;
/*
- * If the first range offset is less than the configured limit
- * we won't force a cache miss.
+ * If the range_offset_limit is NOT in effect, there
+ * is no reason to force a miss.
*/
- range_start = httpHdrRangeFirstOffset(range);
- if (Config.rangeOffsetLimit >= range_start)
+ if (0 == httpHdrRangeOffsetLimit(range))
return 0;
/*
- * Now we know it's possibly a hit. If we already have the
+ * 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)
* force a miss.
*/
assert(NULL != entry->mem_obj);
- if (range_start <= entry->mem_obj->inmem_hi)
+ if (httpHdrRangeFirstOffset(range) <= entry->mem_obj->inmem_hi)
return 0;
/*
* Even though we have a PENDING copy of the object, we
}
/* Release negatively cached IP-cache entries on reload */
if (r->flags.nocache)
- ipcacheReleaseInvalid(r->host);
+ ipcacheInvalidate(r->host);
#if HTTP_VIOLATIONS
else if (r->flags.nocache_hack)
- ipcacheReleaseInvalid(r->host);
+ ipcacheInvalidate(r->host);
#endif
#if USE_CACHE_DIGESTS
http->lookup_type = e ? "HIT" : "MISS";
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;
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 (NULL == r->range) {
*/
debug(33, 3) ("clientProcessRequest2: complex range MISS\n");
http->entry = NULL;
- r->flags.we_dont_do_ranges = 1;
return LOG_TCP_MISS;
- } else if (clientCheckRangeOffsetLimit(e, r->range)) {
- debug(33, 1) ("clientProcessRequest2: forcing miss due to range_offset_limit\n");
+ } else if (clientCheckRangeForceMiss(e, r->range)) {
+ debug(33, 3) ("clientProcessRequest2: forcing miss due to range_offset_limit\n");
http->entry = NULL;
- r->flags.we_dont_do_ranges = 1;
return LOG_TCP_MISS;
}
debug(33, 3) ("clientProcessRequest2: default HIT\n");
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->content_length > 0) {
- http->log_type = LOG_TCP_MISS;
- /* XXX oof, POST can be cached! */
- pumpInit(fd, r, http->uri);
} else {
http->log_type = clientProcessRequest2(http);
}
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;
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);
inet_ntoa(natLookup.nl_realip),
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),
vport, url);
else
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;
+ void *p;
method_t method;
clientHttpRequest *http = NULL;
clientHttpRequest **H = NULL;
* 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);
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.
- */
- if (request->content_length >= 0) {
- int copy_len = XMIN(conn->in.offset, request->content_length);
- 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 < request->content_length)
- commSetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
- if (request->content_length < 0)
- (void) 0;
- else if (clientRequestBodyTooLarge(request->content_length)) {
+ /* 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()
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)
#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(int fdunused, void *dataunused)
{
break;
}
debug(33, 4) ("httpAccept: FD %d: accepted\n", fd);
- connState = memAllocate(MEM_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, memFree, MEM_CONNSTATEDATA);
+ 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)