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 {