Index: src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java =================================================================== --- src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java (revision 1182249) +++ src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java (working copy) @@ -44,7 +44,6 @@ import org.apache.http.HttpStatus; import org.apache.http.HttpVersion; import org.apache.http.ProtocolException; -import org.apache.http.ProtocolVersion; import org.apache.http.RequestLine; import org.apache.http.annotation.ThreadSafe; import org.apache.http.client.ClientProtocolException; @@ -63,7 +62,6 @@ import org.apache.http.message.BasicHttpResponse; import org.apache.http.params.HttpParams; import org.apache.http.protocol.HttpContext; -import org.apache.http.util.VersionInfo; /** *

The {@link CachingHttpClient} is meant to be a drop-in replacement for @@ -119,6 +117,8 @@ private final AtomicLong cacheHits = new AtomicLong(); private final AtomicLong cacheMisses = new AtomicLong(); private final AtomicLong cacheUpdates = new AtomicLong(); + + private final ViaHeaderCache viaHeaders = new ViaHeaderCache(); private final HttpClient backend; private final HttpCache responseCache; @@ -371,7 +371,7 @@ setResponseStatus(context, CacheResponseStatus.CACHE_MISS); String via = generateViaHeader(request); - + if (clientRequestsOurOptions(request)) { setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE); return new OptionsHttp11Response(); @@ -601,16 +601,7 @@ } private String generateViaHeader(HttpMessage msg) { - final VersionInfo vi = VersionInfo.loadVersionInfo("org.apache.http.client", getClass().getClassLoader()); - final String release = (vi != null) ? vi.getRelease() : VersionInfo.UNAVAILABLE; - final ProtocolVersion pv = msg.getProtocolVersion(); - if ("http".equalsIgnoreCase(pv.getProtocol())) { - return String.format("%d.%d localhost (Apache-HttpClient/%s (cache))", - pv.getMajor(), pv.getMinor(), release); - } else { - return String.format("%s/%d.%d localhost (Apache-HttpClient/%s (cache))", - pv.getProtocol(), pv.getMajor(), pv.getMinor(), release); - } + return viaHeaders.generateViaHeader(msg); } private void setResponseStatus(final HttpContext context, final CacheResponseStatus value) { Index: src/main/java/org/apache/http/impl/client/cache/ViaHeaderCache.java =================================================================== --- src/main/java/org/apache/http/impl/client/cache/ViaHeaderCache.java (revision 0) +++ src/main/java/org/apache/http/impl/client/cache/ViaHeaderCache.java (revision 0) @@ -0,0 +1,148 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package org.apache.http.impl.client.cache; + +import java.util.Map; +import java.util.WeakHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.apache.http.HttpMessage; +import org.apache.http.ProtocolVersion; +import org.apache.http.annotation.ThreadSafe; +import org.apache.http.util.VersionInfo; + +/** + * Generating values to be used in the 'Via' header introduces an unnecessary + * latency to each request. Such values can be cached for each protocol version + * used in the HTTP communication. It is expected that only several protocols + * are used in most of the clients, such as HTTP/1.0 and HTTP/1.1, and the + * overall memory impact would be small. + * + */ +@ThreadSafe +public final class ViaHeaderCache { + + /** The 'Via' values cache. */ + private final Map viaHeaders = new WeakHashMap(4); + + /** Reentrant lock for read/write operations. */ + private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); + + /** Lock to be used on READ operations. */ + private final Lock read = readWriteLock.readLock(); + + /** Lock to be used on WRITE operations. */ + private final Lock write = readWriteLock.writeLock(); + + /** + * Generates or retrieves from the cache a value to be used in a 'Via' + * header, using the provided {@link HttpMessage} message. + * + * @param msg + * The {@link HttpMessage} message to be used when constructing + * or retrieving the value. + * @return The value either from the cache or constructed on-the-fly. + */ + public String generateViaHeader(final HttpMessage msg) { + long start = System.nanoTime(); + + final ProtocolVersion pv = msg.getProtocolVersion(); + + String existingEntry = get(pv); + + if (existingEntry != null) { + long finish = System.nanoTime(); + System.out.println(String.format("Via header took: %d microseconds", (finish-start)/1000 )); + return existingEntry; + } + + final VersionInfo vi = VersionInfo.loadVersionInfo("org.apache.http.client", getClass().getClassLoader()); + final String release = (vi != null) ? vi.getRelease() : VersionInfo.UNAVAILABLE; + + String value; + + if ("http".equalsIgnoreCase(pv.getProtocol())) { + value = String.format("%d.%d localhost (Apache-HttpClient/%s (cache))", pv.getMajor(), pv.getMinor(), + release); + } else { + value = String.format("%s/%d.%d localhost (Apache-HttpClient/%s (cache))", pv.getProtocol(), pv.getMajor(), + pv.getMinor(), release); + } + + long finish = System.nanoTime(); + + System.out.println(String.format("Via header took: %d microseconds", (finish-start)/1000 )); + + add(pv, value); + + return value; + } + + /** + * Adds a value to the cache, using the provided {@link ProtocolVersion} + * instance as a key. + * + * @param pv + * The {@link ProtocolVersion} instance this value was generated + * for. + * @param headerValue + * The generated value. + */ + private void add(final ProtocolVersion pv, final String headerValue) { + write.lock(); + try { + if (!viaHeaders.containsKey(pv)) { + viaHeaders.put(pv, headerValue); + } + } finally { + write.unlock(); + } + } + + /** + * Retrieves a previously generated value from the cache, using the provided + * {@link ProtocolVersion} instance as a key. + * + * @param pv + * The {@link ProtocolVersion} this header value was generated + * for. + * + * @return The associated 'Via' header value for the provided + * {@link ProtocolVersion} instance, or null if the cache doesn't + * have this key. + */ + private String get(final ProtocolVersion pv) { + read.lock(); + try { + return viaHeaders.get(pv); + } finally { + read.unlock(); + } + } + +}