/*
- * $Id: client_side.cc,v 1.521 2001/01/05 09:51:36 adrian 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
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;
}
RequestMethodStr[http->request->method], http->uri,
answer == ACCESS_ALLOWED ? "ALLOWED" : "DENIED",
AclMatchedName ? AclMatchedName : "NO ACL's");
- if (http->acl_checklist->auth_user)
- proxy_auth_msg = http->acl_checklist->auth_user->message;
+ 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);
err = errorCon(page_id, status);
err->request = requestLink(http->request);
err->src_addr = http->conn->peer.sin_addr;
- err->proxy_auth_msg = proxy_auth_msg;
+ 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;
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);
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());
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().
*/
}
/* yes, continue */
http->log_type = LOG_TCP_MISS;
- } else if (r->content_length >= 0) {
- /*
- * Need to initialize pump even if 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);
}
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
* 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,
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()
}
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 */