diff --git a/integration/cronet/src/main/java/com/bumptech/glide/integration/cronet/BufferQueue.java b/integration/cronet/src/main/java/com/bumptech/glide/integration/cronet/BufferQueue.java index e1d7519899..f1af17f1e3 100644 --- a/integration/cronet/src/main/java/com/bumptech/glide/integration/cronet/BufferQueue.java +++ b/integration/cronet/src/main/java/com/bumptech/glide/integration/cronet/BufferQueue.java @@ -68,21 +68,21 @@ private static long bufferSizeHeuristic(UrlResponseInfo info) { final Map> headers = info.getAllHeaders(); if (headers.containsKey(CONTENT_LENGTH)) { long contentLength = Long.parseLong(headers.get(CONTENT_LENGTH).get(0)); - boolean isCompressed = + boolean isIdentity = !headers.containsKey(CONTENT_ENCODING) || (headers.get(CONTENT_ENCODING).size() == 1 && "identity".equals(headers.get(CONTENT_ENCODING).get(0))); - if (isCompressed) { - // We have to guess at the uncompressed size. In the future, consider guessing a - // compression ratio based on the content-type and content-encoding. For now, - // assume 2. - return 2 * contentLength; - } else { + if (isIdentity) { // In this case, we know exactly how many bytes we're going to get, so we can // size our buffer perfectly. However, we still have to call read() for the last time, // even when we know there shouldn't be any more bytes coming. To avoid allocating another // buffer for that case, add one more byte than we really need. return contentLength + 1; + } else { + // We have to guess at the uncompressed size. In the future, consider guessing a + // compression ratio based on the content-type and content-encoding. For now, + // assume 2. + return 2 * contentLength + 1; } } else { // No content-length. This means we're either being sent a chunked response, or the diff --git a/integration/cronet/src/test/java/com/bumptech/glide/integration/cronet/ChromiumUrlFetcherTest.java b/integration/cronet/src/test/java/com/bumptech/glide/integration/cronet/ChromiumUrlFetcherTest.java index d4321062be..305b8ffeb5 100644 --- a/integration/cronet/src/test/java/com/bumptech/glide/integration/cronet/ChromiumUrlFetcherTest.java +++ b/integration/cronet/src/test/java/com/bumptech/glide/integration/cronet/ChromiumUrlFetcherTest.java @@ -83,6 +83,20 @@ public ByteBuffer answer(InvocationOnMock invocation) throws Throwable { anyString(), any(UrlRequest.Callback.class), any(Executor.class))) .thenReturn(mockUrlRequestBuilder); when(mockUrlRequestBuilder.build()).thenReturn(request); + org.mockito.Mockito.doAnswer( + new org.mockito.stubbing.Answer() { + @Override + public Void answer(org.mockito.invocation.InvocationOnMock invocation) + throws Throwable { + ByteBuffer buffer = invocation.getArgument(0); + if (!buffer.hasRemaining()) { + throw new IllegalArgumentException("ByteBuffer is already full."); + } + return null; + } + }) + .when(request) + .read(any(ByteBuffer.class)); glideUrl = new GlideUrl("http://www.google.com"); @@ -204,7 +218,9 @@ public List> getAllHeadersAsList() { public Map> getAllHeaders() { ImmutableMap.Builder> builder = ImmutableMap.builder(); for (Map.Entry entry : getAllHeadersAsList()) { - builder.put(entry.getKey(), ImmutableList.copyOf(entry.getValue().split(","))); + builder.put( + entry.getKey().toLowerCase(java.util.Locale.US), + ImmutableList.copyOf(entry.getValue().split(","))); } return builder.build(); } @@ -377,6 +393,20 @@ public void testRequestComplete_with200NotCancelledMatchingLength_callsCallbackW .isEqualTo(data); } + @Test + public void testRequestComplete_with200NotCancelledZeroLength_callsCallbackWithEmptyData() + throws Exception { + ByteBuffer expected = ByteBuffer.allocateDirect(0); + ArgumentCaptor captor = ArgumentCaptor.forClass(ByteBuffer.class); + + fetcher.loadData(Priority.LOW, callback); + succeed(getInfo(0, 200), urlRequestListenerCaptor.getValue(), expected.duplicate()); + + verify(callback, timeout(1000)).onDataReady(captor.capture()); + ByteBuffer received = captor.getValue(); + assertThat(received.remaining()).isEqualTo(0); + } + private static Map anyHeaders() { return anyMap(); }