-
-
Notifications
You must be signed in to change notification settings - Fork 368
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Multi cast upstream response for Chucker consumption. (#267)
* Multi cast response upstream for Chucker consumption. * Read buffer prefix before potentially gunzipping it. * Inform Chucker about unprocessed responses. * Simplify multi casting logic. * Move read offset to a variable. * Inline one-line method. * Give better control over TeeSource read results. * Add documentation to TeeSource. * Close side channel when capacity is exceeded. Co-authored-by: Volodymyr Buberenko <[email protected]>
- Loading branch information
Showing
7 changed files
with
380 additions
and
43 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
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
16 changes: 16 additions & 0 deletions
16
library/src/main/java/com/chuckerteam/chucker/internal/support/AndroidCacheFileFactory.kt
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,16 @@ | ||
package com.chuckerteam.chucker.internal.support | ||
|
||
import android.content.Context | ||
import java.io.File | ||
import java.util.concurrent.atomic.AtomicLong | ||
|
||
internal class AndroidCacheFileFactory( | ||
context: Context | ||
) : FileFactory { | ||
private val fileDir = context.cacheDir | ||
private val uniqueIdGenerator = AtomicLong() | ||
|
||
override fun create(): File { | ||
return File(fileDir, "chucker-${uniqueIdGenerator.getAndIncrement()}") | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
library/src/main/java/com/chuckerteam/chucker/internal/support/FileFactory.kt
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,7 @@ | ||
package com.chuckerteam.chucker.internal.support | ||
|
||
import java.io.File | ||
|
||
internal interface FileFactory { | ||
fun create(): File | ||
} |
90 changes: 90 additions & 0 deletions
90
library/src/main/java/com/chuckerteam/chucker/internal/support/TeeSource.kt
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,90 @@ | ||
package com.chuckerteam.chucker.internal.support | ||
|
||
import java.io.File | ||
import java.io.IOException | ||
import okio.Buffer | ||
import okio.Okio | ||
import okio.Source | ||
import okio.Timeout | ||
|
||
/** | ||
* A source that acts as a tee operator - https://en.wikipedia.org/wiki/Tee_(command). | ||
* | ||
* It takes the input [upstream] and reads from it serving the bytes to the end consumer | ||
* like a regular [Source]. While bytes are read from the [upstream] the are also copied | ||
* to a [sideChannel] file. After the [upstream] is depleted or when a failure occurs | ||
* an appropriate [callback] method is called. | ||
* | ||
* Failure is considered any [IOException] during reading the bytes or exceeding [readBytesLimit] length. | ||
*/ | ||
internal class TeeSource( | ||
private val upstream: Source, | ||
private val sideChannel: File, | ||
private val callback: Callback, | ||
private val readBytesLimit: Long = Long.MAX_VALUE | ||
) : Source { | ||
private val sideStream = Okio.buffer(Okio.sink(sideChannel)) | ||
private var totalBytesRead = 0L | ||
private var reachedLimit = false | ||
private var upstreamFailed = false | ||
|
||
override fun read(sink: Buffer, byteCount: Long): Long { | ||
val bytesRead = try { | ||
upstream.read(sink, byteCount) | ||
} catch (e: IOException) { | ||
callSideChannelFailure(e) | ||
throw e | ||
} | ||
|
||
if (bytesRead == -1L) { | ||
sideStream.close() | ||
return -1L | ||
} | ||
|
||
totalBytesRead += bytesRead | ||
if (!reachedLimit && (totalBytesRead <= readBytesLimit)) { | ||
val offset = sink.size() - bytesRead | ||
sink.copyTo(sideStream.buffer(), offset, bytesRead) | ||
sideStream.emitCompleteSegments() | ||
return bytesRead | ||
} | ||
if (!reachedLimit) { | ||
reachedLimit = true | ||
sideStream.close() | ||
callSideChannelFailure(IOException("Capacity of $readBytesLimit bytes exceeded")) | ||
} | ||
|
||
return bytesRead | ||
} | ||
|
||
override fun close() { | ||
sideStream.close() | ||
upstream.close() | ||
if (!upstreamFailed) { | ||
callback.onSuccess(sideChannel) | ||
} | ||
} | ||
|
||
override fun timeout(): Timeout = upstream.timeout() | ||
|
||
private fun callSideChannelFailure(exception: IOException) { | ||
if (!upstreamFailed) { | ||
upstreamFailed = true | ||
callback.onFailure(exception, sideChannel) | ||
} | ||
} | ||
|
||
interface Callback { | ||
/** | ||
* Called when the upstream was successfully copied to the [file]. | ||
*/ | ||
fun onSuccess(file: File) | ||
|
||
/** | ||
* Called when there was an issue while copying bytes to the [file]. | ||
* | ||
* It might occur due to an exception thrown while reading bytes or due to exceeding capacity limit. | ||
*/ | ||
fun onFailure(exception: IOException, file: File) | ||
} | ||
} |
Oops, something went wrong.