diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/BasicIdGenerator.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/BasicIdGenerator.java deleted file mode 100644 index d81b66e19..000000000 --- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/BasicIdGenerator.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * ==================================================================== - * 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.hc.client5.http.impl.cache; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.util.Formatter; -import java.util.Locale; -import java.util.concurrent.locks.ReentrantLock; - -/** - * Should produce reasonably unique tokens. - */ -class BasicIdGenerator { - - private final String hostname; - private final SecureRandom rnd; - - private long count; - - private final ReentrantLock lock; - - public BasicIdGenerator() { - super(); - String hostname; - try { - hostname = InetAddress.getLocalHost().getHostName(); - } catch (final UnknownHostException ex) { - hostname = "localhost"; - } - this.hostname = hostname; - try { - this.rnd = SecureRandom.getInstance("SHA1PRNG"); - } catch (final NoSuchAlgorithmException ex) { - throw new Error(ex); - } - this.rnd.setSeed(System.currentTimeMillis()); - this.lock = new ReentrantLock(); - } - - public void generate(final StringBuilder buffer) { - lock.lock(); - try { - this.count++; - final int rndnum = this.rnd.nextInt(); - buffer.append(System.currentTimeMillis()); - buffer.append('.'); - try (Formatter formatter = new Formatter(buffer, Locale.ROOT)) { - formatter.format("%1$016x-%2$08x", this.count, rndnum); - } - buffer.append('.'); - buffer.append(this.hostname); - } finally { - lock.unlock(); - } - } - - public String generate() { - final StringBuilder buffer = new StringBuilder(); - generate(buffer); - return buffer.toString(); - } - -} diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/FileResourceFactory.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/FileResourceFactory.java index 22d2cae1d..e0842c992 100644 --- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/FileResourceFactory.java +++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/FileResourceFactory.java @@ -29,13 +29,17 @@ package org.apache.hc.client5.http.impl.cache; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import org.apache.hc.client5.http.cache.Resource; import org.apache.hc.client5.http.cache.ResourceFactory; import org.apache.hc.client5.http.cache.ResourceIOException; import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.ThreadingBehavior; +import org.apache.hc.core5.net.PercentCodec; import org.apache.hc.core5.util.Args; +import org.apache.hc.core5.util.TextUtils; /** * Generates {@link Resource} instances whose body is stored in a temporary file. @@ -46,37 +50,44 @@ import org.apache.hc.core5.util.Args; public class FileResourceFactory implements ResourceFactory { private final File cacheDir; - private final BasicIdGenerator idgen; public FileResourceFactory(final File cacheDir) { super(); this.cacheDir = cacheDir; - this.idgen = new BasicIdGenerator(); } - private File generateUniqueCacheFile(final String requestId) { - final StringBuilder buffer = new StringBuilder(); - this.idgen.generate(buffer); - buffer.append('.'); - final int len = Math.min(requestId.length(), 100); - for (int i = 0; i < len; i++) { - final char ch = requestId.charAt(i); - if (Character.isLetterOrDigit(ch) || ch == '.') { - buffer.append(ch); - } else { - buffer.append('-'); + static String generateUniqueCacheFileName(final String requestId, final String eTag, final byte[] content, final int off, final int len) { + final StringBuilder buf = new StringBuilder(); + if (eTag != null) { + PercentCodec.RFC3986.encode(buf, eTag); + buf.append('@'); + } else if (content != null) { + final MessageDigest sha256; + try { + sha256 = MessageDigest.getInstance("SHA-256"); + } catch (final NoSuchAlgorithmException ex) { + throw new IllegalStateException(ex); } + sha256.update(content, off, len); + buf.append(TextUtils.toHexString(sha256.digest())); + buf.append('@'); } - return new File(this.cacheDir, buffer.toString()); + PercentCodec.RFC3986.encode(buf, requestId); + return buf.toString(); } + /** + * @since 5.4 + */ @Override public Resource generate( final String requestId, + final String eTag, final byte[] content, final int off, final int len) throws ResourceIOException { Args.notNull(requestId, "Request id"); - final File file = generateUniqueCacheFile(requestId); - try (FileOutputStream outStream = new FileOutputStream(file)) { + final String filename = generateUniqueCacheFileName(requestId, eTag, content, off, len); + final File file = new File(cacheDir, filename); + try (FileOutputStream outStream = new FileOutputStream(file, false)) { if (content != null) { outStream.write(content, off, len); } @@ -86,10 +97,22 @@ public class FileResourceFactory implements ResourceFactory { return new FileResource(file); } + @Override + public Resource generate(final String requestId, final byte[] content, final int off, final int len) throws ResourceIOException { + if (content != null) { + return generate(requestId, null, content, off, len); + } else { + return generate(requestId, null, null, 0, 0); + } + } + @Override public Resource generate(final String requestId, final byte[] content) throws ResourceIOException { - Args.notNull(content, "Content"); - return generate(requestId, content, 0, content.length); + if (content != null) { + return generate(requestId, null, content, 0, content.length); + } else { + return generate(requestId, null, null, 0, 0); + } } /** diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestFileResourceFactory.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestFileResourceFactory.java new file mode 100644 index 000000000..952f75712 --- /dev/null +++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestFileResourceFactory.java @@ -0,0 +1,69 @@ +/* + * ==================================================================== + * 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.hc.client5.http.impl.cache; + + +import java.net.URI; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class TestFileResourceFactory { + + CacheKeyGenerator keyGenerator; + + @BeforeEach + public void setUp() { + keyGenerator = new CacheKeyGenerator(); + } + + @Test + public void testViaValueLookup() throws Exception { + final String requestId = keyGenerator.generateKey(new URI("http://localhost/stuff")); + + Assertions.assertEquals( + "blah%20blah@http%3A%2F%2Flocalhost%3A80%2Fstuff", + FileResourceFactory.generateUniqueCacheFileName(requestId, "blah blah", null, 0, 0)); + Assertions.assertEquals( + "blah-blah@http%3A%2F%2Flocalhost%3A80%2Fstuff", + FileResourceFactory.generateUniqueCacheFileName(requestId, "blah-blah", null, 0, 0)); + Assertions.assertEquals( + "blah%40blah@http%3A%2F%2Flocalhost%3A80%2Fstuff", + FileResourceFactory.generateUniqueCacheFileName(requestId, "blah@blah", null, 0, 0)); + Assertions.assertEquals( + "039058c6f2c0cb492c533b0a4d14ef77cc0f78abccced5287d84a1a2011cfb81@http%3A%2F%2Flocalhost%3A80%2Fstuff", + FileResourceFactory.generateUniqueCacheFileName(requestId, null, new byte[]{1, 2, 3}, 0, 3)); + Assertions.assertEquals( + "039058c6f2c0cb492c533b0a4d14ef77cc0f78abccced5287d84a1a2011cfb81@http%3A%2F%2Flocalhost%3A80%2Fstuff", + FileResourceFactory.generateUniqueCacheFileName(requestId, null, new byte[]{1, 2, 3, 4, 5}, 0, 3)); + Assertions.assertEquals( + "http%3A%2F%2Flocalhost%3A80%2Fstuff", + FileResourceFactory.generateUniqueCacheFileName(requestId, null, null, 0, 0)); + } + +}