-
Notifications
You must be signed in to change notification settings - Fork 6.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
14 changed files
with
1,160 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.bumptech.glide.integration.cronet | ||
|
||
apply plugin: 'com.android.library' | ||
|
||
dependencies { | ||
implementation project(':library') | ||
annotationProcessor project(':annotation:compiler') | ||
|
||
api "androidx.annotation:annotation:${ANDROID_X_VERSION}" | ||
} | ||
|
||
android { | ||
compileSdkVersion COMPILE_SDK_VERSION as int | ||
|
||
defaultConfig { | ||
minSdkVersion MIN_SDK_VERSION as int | ||
targetSdkVersion TARGET_SDK_VERSION as int | ||
|
||
versionName VERSION_NAME as String | ||
} | ||
|
||
compileOptions { | ||
sourceCompatibility JavaVersion.VERSION_1_7 | ||
targetCompatibility JavaVersion.VERSION_1_7 | ||
} | ||
} | ||
|
||
apply from: "${rootProject.projectDir}/scripts/upload.gradle" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
POM_NAME=Glide Cronet Integration | ||
POM_ARTIFACT_ID=cronet-integration | ||
POM_PACKAGING=aar | ||
POM_DESCRIPTION=An integration library to use Cronet to fetch data over http/https in Glide |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<lint> | ||
<issue id="AllowBackup" severity="ignore"/> | ||
</lint> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<manifest package="com.bumptech.glide.integration.cronet"> | ||
<application /> | ||
</manifest> |
134 changes: 134 additions & 0 deletions
134
integration/cronet/src/main/java/com/bumptech/glide/integration/cronet/BufferQueue.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
package com.bumptech.glide.integration.cronet; | ||
|
||
import java.nio.ByteBuffer; | ||
import java.util.ArrayDeque; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Queue; | ||
import java.util.concurrent.atomic.AtomicBoolean; | ||
import org.chromium.net.UrlResponseInfo; | ||
|
||
/** | ||
* A utility for processing response bodies, as one contiguous buffer rather than an asynchronous | ||
* stream. | ||
*/ | ||
final class BufferQueue { | ||
public static final String CONTENT_LENGTH = "content-length"; | ||
public static final String CONTENT_ENCODING = "content-encoding"; | ||
private final Queue<ByteBuffer> mBuffers; | ||
private final AtomicBoolean mIsCoalesced = new AtomicBoolean(false); | ||
|
||
public static Builder builder() { | ||
return new Builder(); | ||
} | ||
|
||
/** | ||
* Use this class during a request, to combine streamed buffers of a response into a single final | ||
* buffer. | ||
* | ||
* <p>For example: {@code @Override public void onResponseStarted(UrlRequest request, | ||
* UrlResponseInfo info) { request.read(builder.getFirstBuffer(info)); } @Override public void | ||
* onReadCompleted(UrlRequest request, UrlResponseInfo info, ByteBuffer buffer) { | ||
* request.read(builder.getNextBuffer(buffer)); } } | ||
*/ | ||
public static final class Builder { | ||
private ArrayDeque<ByteBuffer> mBuffers = new ArrayDeque<>(); | ||
private RuntimeException whenClosed; | ||
|
||
private Builder() {} | ||
|
||
/** Returns the next buffer to write data into. */ | ||
public ByteBuffer getNextBuffer(ByteBuffer lastBuffer) { | ||
if (mBuffers == null) { | ||
throw new RuntimeException(whenClosed); | ||
} | ||
if (lastBuffer != mBuffers.peekLast()) { | ||
mBuffers.addLast(lastBuffer); | ||
} | ||
if (lastBuffer.hasRemaining()) { | ||
return lastBuffer; | ||
} else { | ||
return ByteBuffer.allocateDirect(8096); | ||
} | ||
} | ||
|
||
/** Returns a ByteBuffer heuristically sized to hold the whole response body. */ | ||
public ByteBuffer getFirstBuffer(UrlResponseInfo info) { | ||
// Security note - a malicious server could attempt to exhaust client memory by sending | ||
// down a Content-Length of a very large size, which we would eagerly allocate without | ||
// the server having to actually send those bytes. This isn't considered to be an | ||
// issue, because that same malicious server could use our transparent gzip to force us | ||
// to allocate 1032 bytes per byte sent by the server. | ||
return ByteBuffer.allocateDirect((int) Math.min(bufferSizeHeuristic(info), 524288)); | ||
} | ||
|
||
private static long bufferSizeHeuristic(UrlResponseInfo info) { | ||
final Map<String, List<String>> headers = info.getAllHeaders(); | ||
if (headers.containsKey(CONTENT_LENGTH)) { | ||
long contentLength = Long.parseLong(headers.get(CONTENT_LENGTH).get(0)); | ||
boolean isCompressed = | ||
!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 { | ||
// 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 { | ||
// No content-length. This means we're either being sent a chunked response, or the | ||
// java stack stripped content length because of transparent gzip. In either case we really | ||
// have no idea, and so we fall back to a reasonable guess. | ||
return 8192; | ||
} | ||
} | ||
|
||
public BufferQueue build() { | ||
whenClosed = new RuntimeException(); | ||
final ArrayDeque<ByteBuffer> buffers = mBuffers; | ||
mBuffers = null; | ||
return new BufferQueue(buffers); | ||
} | ||
} | ||
|
||
private BufferQueue(Queue<ByteBuffer> buffers) { | ||
mBuffers = buffers; | ||
for (ByteBuffer buffer : mBuffers) { | ||
buffer.flip(); | ||
} | ||
} | ||
|
||
/** Returns the response body as a single contiguous buffer. */ | ||
public ByteBuffer coalesceToBuffer() { | ||
markCoalesced(); | ||
if (mBuffers.size() == 0) { | ||
return ByteBuffer.allocateDirect(0); | ||
} else if (mBuffers.size() == 1) { | ||
return mBuffers.remove(); | ||
} else { | ||
int size = 0; | ||
for (ByteBuffer buffer : mBuffers) { | ||
size += buffer.remaining(); | ||
} | ||
ByteBuffer result = ByteBuffer.allocateDirect(size); | ||
while (!mBuffers.isEmpty()) { | ||
result.put(mBuffers.remove()); | ||
} | ||
result.flip(); | ||
return result; | ||
} | ||
} | ||
|
||
private void markCoalesced() { | ||
if (!mIsCoalesced.compareAndSet(false, true)) { | ||
throw new IllegalStateException("This BufferQueue has already been consumed"); | ||
} | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
integration/cronet/src/main/java/com/bumptech/glide/integration/cronet/ByteBufferParser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.bumptech.glide.integration.cronet; | ||
|
||
import java.nio.ByteBuffer; | ||
|
||
/** | ||
* Parses a {@link java.nio.ByteBuffer} to a particular data type. | ||
* | ||
* @param <T> The type of data to parse the buffer to. | ||
*/ | ||
interface ByteBufferParser<T> { | ||
/** Returns the required type of data parsed from the given {@link ByteBuffer}. */ | ||
T parse(ByteBuffer byteBuffer); | ||
/** Returns the {@link Class} of the data that will be parsed from {@link ByteBuffer}s. */ | ||
Class<T> getDataClass(); | ||
} |
Oops, something went wrong.