This patch is generated from the pinning branch of s2_5 in squid
Wed Jun  7 00:14:59 2006 GMT
See http://devel.squid-cache.org/

Index: squid/src/HttpHeader.c
diff -u squid/src/HttpHeader.c:1.10.6.26 squid/src/HttpHeader.c:1.10.6.24.2.4
--- squid/src/HttpHeader.c:1.10.6.26	Fri May 12 09:51:33 2006
+++ squid/src/HttpHeader.c	Mon Jun  5 21:43:14 2006
@@ -105,6 +105,7 @@
     {"Proxy-Authentication-Info", HDR_PROXY_AUTHENTICATION_INFO, ftStr},
     {"Proxy-Authorization", HDR_PROXY_AUTHORIZATION, ftStr},
     {"Proxy-Connection", HDR_PROXY_CONNECTION, ftStr},
+    {"Proxy-support", HDR_PROXY_SUPPORT, ftStr},
     {"Public", HDR_PUBLIC, ftStr},
     {"Range", HDR_RANGE, ftPRange},
     {"Referer", HDR_REFERER, ftStr},
@@ -151,6 +152,7 @@
     HDR_IF_MATCH, HDR_IF_NONE_MATCH,
     HDR_LINK, HDR_PRAGMA,
     HDR_PROXY_CONNECTION,
+    HDR_PROXY_SUPPORT,
     HDR_TRANSFER_ENCODING,
     HDR_UPGRADE,
     HDR_VARY,
Index: squid/src/HttpRequest.c
diff -u squid/src/HttpRequest.c:1.7.36.4 squid/src/HttpRequest.c:1.7.36.3.2.2
--- squid/src/HttpRequest.c:1.7.36.4	Fri Mar 10 19:16:30 2006
+++ squid/src/HttpRequest.c	Thu Apr 13 08:16:57 2006
@@ -67,6 +67,9 @@
 	httpHdrCcDestroy(req->cache_control);
     if (req->range)
 	httpHdrRangeDestroy(req->range);
+    if (req->pinned_connection)
+	cbdataUnlock(req->pinned_connection);
+    req->pinned_connection = NULL;
     memFree(req, MEM_REQUEST_T);
 }
 
Index: squid/src/client_side.c
diff -u squid/src/client_side.c:1.47.2.77 squid/src/client_side.c:1.47.2.71.2.10
--- squid/src/client_side.c:1.47.2.77	Fri Jun  2 15:51:12 2006
+++ squid/src/client_side.c	Mon Jun  5 21:43:15 2006
@@ -964,6 +964,8 @@
 	safe_free(connState->in.buf);
     /* XXX account connState->in.buf */
     pconnHistCount(0, connState->nrequests);
+    if (connState->pinning.fd >= 0)
+	comm_close(connState->pinning.fd);
     cbdataFree(connState);
 #ifdef _SQUID_LINUX_
     /* prevent those nasty RST packets */
@@ -978,7 +980,7 @@
 clientInterpretRequestHeaders(clientHttpRequest * http)
 {
     request_t *request = http->request;
-    const HttpHeader *req_hdr = &request->header;
+    HttpHeader *req_hdr = &request->header;
     int no_cache = 0;
     const char *str;
     request->imslen = -1;
@@ -1026,15 +1028,45 @@
 #endif
 	    request->flags.nocache = 1;
     }
+    if (Config.onoff.pipeline_prefetch)
+	request->flags.no_connection_auth = 1;
+
     /* ignore range header in non-GETs */
     if (request->method == METHOD_GET) {
 	request->range = httpHeaderGetRange(req_hdr);
 	if (request->range)
 	    request->flags.range = 1;
     }
-    if (httpHeaderHas(req_hdr, HDR_AUTHORIZATION))
+
+    if (httpHeaderHas(req_hdr, HDR_AUTHORIZATION) || httpHeaderHas(req_hdr, HDR_PROXY_AUTHORIZATION)) {
+	HttpHeaderPos pos = HttpHeaderInitPos;
+	HttpHeaderEntry *e;
 	request->flags.auth = 1;
-    if (request->login[0] != '\0')
+	while ((e = httpHeaderGetEntry(req_hdr, &pos))) {
+	    if (e->id == HDR_AUTHORIZATION || e->id == HDR_PROXY_AUTHORIZATION) {
+		if (!request->flags.no_connection_auth) {
+		    const char *value = strBuf(e->value);
+		    if (strncasecmp(value, "NTLM ", 5) == 0
+			||
+			strncasecmp(value, "Negotiate ", 10) == 0
+			||
+			strncasecmp(value, "Kerberos ", 9) == 0) {
+			request->flags.connection_auth = 1;
+			request->flags.must_keepalive = 1;
+			/* the pinned connection is set below */
+			break;
+		    }
+		} else {
+		    httpHeaderDelAt(req_hdr, pos);
+		}
+	    }
+	}
+    }
+    if (request->flags.connection_auth || http->conn->pinning.fd != -1) {
+	request->flags.auth = 1;
+	request->pinned_connection = http->conn;
+	cbdataLock(request->pinned_connection);
+    } else if (request->login[0] != '\0')
 	request->flags.auth = 1;
     if (httpHeaderHas(req_hdr, HDR_VIA)) {
 	String s = httpHeaderGetList(req_hdr, HDR_VIA);
@@ -1432,18 +1464,29 @@
     }
     /* Filter unproxyable authentication types */
     if (http->log_type != LOG_TCP_DENIED &&
-	(httpHeaderHas(hdr, HDR_WWW_AUTHENTICATE) || httpHeaderHas(hdr, HDR_PROXY_AUTHENTICATE))) {
+	(httpHeaderHas(hdr, HDR_WWW_AUTHENTICATE))) {
 	HttpHeaderPos pos = HttpHeaderInitPos;
 	HttpHeaderEntry *e;
 	while ((e = httpHeaderGetEntry(hdr, &pos))) {
-	    if (e->id == HDR_WWW_AUTHENTICATE || e->id == HDR_PROXY_AUTHENTICATE) {
+	    if (e->id == HDR_WWW_AUTHENTICATE) {
 		const char *value = strBuf(e->value);
 		if ((strncasecmp(value, "NTLM", 4) == 0 &&
 			(value[4] == '\0' || value[4] == ' '))
 		    ||
 		    (strncasecmp(value, "Negotiate", 9) == 0 &&
-			(value[9] == '\0' || value[9] == ' ')))
-		    httpHeaderDelAt(hdr, pos);
+			(value[9] == '\0' || value[9] == ' '))
+		    ||
+		    (strncasecmp(value, "Kerberos", 8) == 0 &&
+			(value[8] == '\0' || value[8] == ' '))) {
+		    if (request->flags.no_connection_auth) {
+			httpHeaderDelAt(hdr, pos);
+			continue;
+		    } else if (!request->flags.accelerated) {
+			httpHeaderPutStr(hdr, HDR_PROXY_SUPPORT, "Session-Based-Authentication");
+			httpHeaderPutStr(hdr, HDR_CONNECTION, "Proxy-support");
+		    }
+		    break;
+		}
 	    }
 	}
     }
@@ -1473,6 +1516,10 @@
     }
     if (!Config.onoff.client_pconns && !request->flags.must_keepalive)
 	request->flags.proxy_keepalive = 0;
+    if (request->flags.connection_auth && !rep->keep_alive) {
+	debug(33, 2) ("clientBuildReplyHeader: Connection oriented auth but server side non-persistent\n");
+	request->flags.proxy_keepalive = 0;
+    }
     /* Signal keep-alive if needed */
     httpHeaderPutStr(hdr,
 	http->flags.accel ? HDR_CONNECTION : HDR_PROXY_CONNECTION,
@@ -2162,6 +2209,11 @@
     debug(33, 3) ("clientKeepaliveNextRequest: FD %d\n", conn->fd);
     conn->defer.until = 0;	/* Kick it to read a new request */
     httpRequestFree(http);
+    if (conn->pinning.pinned && conn->pinning.fd == -1) {
+	debug(33, 2) ("clientKeepaliveNextRequest: FD %d Connection was pinned but server side gone. Terminating client connection\n", conn->fd);
+	comm_close(conn->fd);
+	return;
+    }
     if ((http = conn->chr) == NULL) {
 	debug(33, 5) ("clientKeepaliveNextRequest: FD %d reading next req\n",
 	    conn->fd);
@@ -3536,6 +3588,7 @@
 	connState->fd = fd;
 	connState->in.size = CLIENT_REQ_BUF_SZ;
 	connState->in.buf = memAllocate(MEM_CLIENT_REQ_BUF);
+	connState->pinning.fd = -1;
 	/* XXX account connState->in.buf */
 	comm_add_close_handler(fd, connStateFree, connState);
 	if (Config.onoff.log_fqdn)
@@ -3952,3 +4005,52 @@
 	}
     }
 }
+
+/* This is a handler normally called by comm_close() */
+static void
+clientPinnedConnectionClosed(int fd, void *data)
+{
+    ConnStateData *conn = data;
+    conn->pinning.fd = -1;
+    safe_free(conn->pinning.host);
+    /* NOTE: pinning.pinned should be kept. This combined with fd == -1 at the end of a request indicates that the host
+       connection has gone away */
+}
+
+void
+clientPinConnection(ConnStateData * conn, const char *host, int port, int fd)
+{
+    if (!cbdataValid(conn))
+	comm_close(fd);
+    if (conn->pinning.fd == fd)
+	return;
+    assert(conn->pinning.fd == -1);
+    conn->pinning.fd = fd;
+    safe_free(conn->pinning.host);
+    conn->pinning.host = xstrdup(host);
+    conn->pinning.port = port;
+    conn->pinning.pinned = 1;
+    comm_add_close_handler(fd, clientPinnedConnectionClosed, conn);
+}
+
+int
+clientGetPinnedConnection(ConnStateData *conn, request_t *request)
+{
+    int fd = conn->pinning.fd;
+
+    if (fd < 0)
+	return -1;
+
+    if (request && strcasecmp(conn->pinning.host, request->host) != 0) {
+	comm_close(fd);
+	return -1;
+    }
+    if (request && conn->pinning.port != request->port) {
+	comm_close(fd);
+	return -1;
+    }
+    conn->pinning.fd = -1;
+    comm_remove_close_handler(fd, clientPinnedConnectionClosed, conn);
+    return fd;
+}
+
Index: squid/src/enums.h
diff -u squid/src/enums.h:1.29.2.18 squid/src/enums.h:1.29.2.17.2.2
--- squid/src/enums.h:1.29.2.18	Fri Nov 11 19:13:48 2005
+++ squid/src/enums.h	Thu Apr 13 08:16:58 2006
@@ -245,6 +245,7 @@
 #if X_ACCELERATOR_VARY
     HDR_X_ACCELERATOR_VARY,
 #endif
+    HDR_PROXY_SUPPORT,
     HDR_OTHER,
     HDR_ENUM_END
 } http_hdr_type;
Index: squid/src/forward.c
diff -u squid/src/forward.c:1.13.6.16 squid/src/forward.c:1.13.6.15.2.5
--- squid/src/forward.c:1.13.6.16	Fri Mar 10 19:16:31 2006
+++ squid/src/forward.c	Mon Jun  5 21:14:32 2006
@@ -143,6 +143,8 @@
 	return 0;
     if (fwdState->request->flags.body_sent)
 	return 0;
+    if (fwdState->request->pinned_connection)
+	return 0;
     return 1;
 }
 
@@ -336,6 +338,25 @@
     return aclMapTOS(Config.accessList.outgoing_tos, &ch);
 }
 
+static int
+getPinnedFD(request_t *request)
+{
+    int fd;
+    ConnStateData *conn = request->pinned_connection;
+    if (!conn)
+	return -1;
+
+    fd = clientGetPinnedConnection(conn, request);
+
+    /* No pinned connection. Fall back */
+    if (fd < 0 && !request->flags.connection_auth) {
+	cbdataUnlock(request->pinned_connection);
+	request->pinned_connection = NULL;
+    }
+
+    return fd;
+}
+
 static void
 fwdConnectStart(void *data)
 {
@@ -372,6 +393,19 @@
 	ftimeout = 5;
     if (ftimeout < ctimeout)
 	ctimeout = ftimeout;
+    if (fwdState->request->pinned_connection) {
+       	fd = getPinnedFD(fwdState->request);
+	if (fd >= 0) {
+	    fwdState->server_fd = fd;
+	    fwdState->n_tries++;
+	    fwdState->request->flags.auth = 1;
+	    fwdState->request->flags.connection_auth = 1;
+	    fwdState->request->flags.must_keepalive = 1;
+	    comm_add_close_handler(fd, fwdServerClosed, fwdState);
+	    fwdConnectDone(fd, COMM_OK, fwdState);
+	    return;
+	}
+    }
     if ((fd = pconnPop(host, port)) >= 0) {
 	if (fwdCheckRetriable(fwdState)) {
 	    debug(17, 3) ("fwdConnectStart: reusing pconn FD %d\n", fd);
Index: squid/src/http.c
diff -u squid/src/http.c:1.17.6.32 squid/src/http.c:1.17.6.32.2.5
--- squid/src/http.c:1.17.6.32	Tue Oct 18 19:13:21 2005
+++ squid/src/http.c	Mon Jun  5 21:14:32 2006
@@ -54,6 +54,7 @@
 static void httpMakePublic(StoreEntry *);
 static int httpCachableReply(HttpStateData *);
 static void httpMaybeRemovePublic(StoreEntry *, http_status);
+static int peer_supports_connection_pinning(HttpStateData *httpState);
 
 static void
 httpStateFree(int fd, void *data)
@@ -454,6 +455,8 @@
 	ctx_exit(ctx);
 	return;
     }
+    if (!peer_supports_connection_pinning(httpState))
+	httpState->orig_request->flags.no_connection_auth = 1;
     storeTimestampsSet(entry);
     /* Check if object is cacheable or not based on reply code */
     debug(11, 3) ("httpProcessReplyHeader: HTTP CODE: %d\n", reply->sline.status);
@@ -550,6 +553,35 @@
     return 1;
 }
 
+/* Small helper function to verify if connection pinning is supported or not
+ */
+static int
+peer_supports_connection_pinning(HttpStateData *httpState)
+{
+    const HttpReply *rep = httpState->entry->mem_obj->reply;
+    const HttpHeader *hdr = &rep->header;
+    const request_t *req = httpState->orig_request;
+    int rc;
+    String header;
+
+    if (!httpState->peer)
+	return 1;
+
+    if (req->pinned_connection)
+	if (req->pinned_connection->pinning.host)
+	    return 1;
+
+    if (!httpHeaderHas(hdr, HDR_PROXY_SUPPORT))
+	return 0;
+
+    header = httpHeaderGetStrOrList(hdr, HDR_PROXY_SUPPORT);
+    /* XXX This ought to be done in a case-insensitive manner */
+    rc = (strStr(header, "Session-Based-Authentication") != NULL);
+    stringClean(&header);
+
+    return rc;
+}
+
 /* This will be called when data is ready to be read from fd.  Read until
  * error or connection closed. */
 /* XXX this function is too long! */
@@ -560,6 +592,7 @@
     LOCAL_ARRAY(char, buf, SQUID_TCP_SO_RCVBUF);
     StoreEntry *entry = httpState->entry;
     const request_t *request = httpState->request;
+    const request_t *orig_request = httpState->orig_request;
     int len;
     int bin;
     int clen;
@@ -702,7 +735,7 @@
 		 */
 		if (!httpState->flags.request_sent) {
 		    debug(11, 1) ("httpReadReply: Request not yet fully sent \"%s %s\"\n",
-			RequestMethodStr[httpState->orig_request->method],
+			RequestMethodStr[orig_request->method],
 			storeUrl(entry));
 		    keep_alive = 0;
 		}
@@ -722,7 +755,7 @@
 		    } else if (len > 0) {
 			debug(11, Config.onoff.relaxed_header_parser <= 0 || keep_alive ? 1 : 2)
 			    ("httpReadReply: Excess data from \"%s %s\"\n",
-			    RequestMethodStr[httpState->orig_request->method],
+			    RequestMethodStr[orig_request->method],
 			    storeUrl(entry));
 			storeAppend(entry, buf, len);
 			keep_alive = 0;
@@ -738,7 +771,12 @@
 #endif
 		    comm_remove_close_handler(fd, httpStateFree, httpState);
 		    fwdUnregister(fd, httpState->fwd);
-		    if (request->flags.accelerated && Config.Accel.single_host && Config.Accel.host)
+		    if (orig_request->pinned_connection) {
+			if (peer_supports_connection_pinning(httpState))
+			    clientPinConnection(orig_request->pinned_connection, orig_request->host, orig_request->port, fd);
+			else
+			    comm_close(fd);
+		    } else if (orig_request->flags.accelerated && Config.Accel.single_host && Config.Accel.host)
 			pconnPush(fd, Config.Accel.host, Config.Accel.port);
 		    else
 			pconnPush(fd, request->host, request->port);
@@ -764,7 +802,7 @@
 	    /* Server is nasty on us. Shut down */
 	    debug(11, Config.onoff.relaxed_header_parser <= 0 || entry->mem_obj->reply->keep_alive ? 1 : 2)
 		("httpReadReply: Excess data from \"%s %s\"\n",
-		RequestMethodStr[httpState->orig_request->method],
+		RequestMethodStr[orig_request->method],
 		storeUrl(entry));
 	    fwdComplete(httpState->fwd);
 	    comm_close(fd);
@@ -858,6 +896,8 @@
 	we_do_ranges = 0;
     else if (!orig_request->flags.cachable)
 	we_do_ranges = 0;
+    else if (orig_request->flags.connection_auth)
+	we_do_ranges = 0;
     else if (httpHdrRangeOffsetLimit(orig_request->range))
 	we_do_ranges = 0;
     else
@@ -1091,7 +1131,9 @@
     /*
      * Is keep-alive okay for all request methods?
      */
-    if (!Config.onoff.server_pconns)
+    if (httpState->orig_request->flags.must_keepalive)
+	httpState->flags.keepalive = 1;
+    else if (!Config.onoff.server_pconns)
 	httpState->flags.keepalive = 0;
     else if (p == NULL)
 	httpState->flags.keepalive = 1;
Index: squid/src/protos.h
diff -u squid/src/protos.h:1.41.6.34 squid/src/protos.h:1.41.6.33.2.4
--- squid/src/protos.h:1.41.6.34	Sat Feb 25 19:13:57 2006
+++ squid/src/protos.h	Mon Jun  5 21:14:33 2006
@@ -141,6 +141,8 @@
 extern void clientHttpConnectionsClose(void);
 extern StoreEntry *clientCreateStoreEntry(clientHttpRequest *, method_t, request_flags);
 extern int isTcpHit(log_type);
+extern void clientPinConnection(ConnStateData *conn, const char *host, int port, int fd);
+int clientGetPinnedConnection(ConnStateData *conn, request_t *request);
 
 extern int commSetNonBlocking(int fd);
 extern int commUnsetNonBlocking(int fd);
Index: squid/src/structs.h
diff -u squid/src/structs.h:1.48.2.46 squid/src/structs.h:1.48.2.43.2.4
--- squid/src/structs.h:1.48.2.46	Fri Mar 10 19:16:31 2006
+++ squid/src/structs.h	Mon Jun  5 20:35:25 2006
@@ -1139,6 +1139,12 @@
 	int n;
 	time_t until;
     } defer;
+    struct {
+	int fd;		/* pinned server side connection */
+	char *host;	/* host name of pinned connection */
+	int port;	/* port of pinned connection */
+	int pinned;	/* this connection was pinned */
+    } pinning;
 };
 
 struct _ipcache_addrs {
@@ -1630,6 +1636,8 @@
     unsigned int body_sent:1;
     unsigned int reset_tcp:1;
     unsigned int must_keepalive:1;
+    unsigned int connection_auth:1;	/* Request wants connection oriented auth */
+    unsigned int no_connection_auth:1;	/* Connection oriented auth can not be supported */
 };
 
 struct _link_list {
@@ -1687,6 +1695,7 @@
     const char *vary_headers;	/* Used when varying entities are detected. Changes how the store key is calculated */
     BODY_HANDLER *body_reader;
     void *body_reader_data;
+    ConnStateData *pinned_connection; /* If set then this request is tighly tied to the corresponding client side connetion */
 };
 
 struct _cachemgr_passwd {
