diff --git a/okhttp/api/okhttp.api b/okhttp/api/okhttp.api index 91054131362e..3ded82a88057 100644 --- a/okhttp/api/okhttp.api +++ b/okhttp/api/okhttp.api @@ -816,6 +816,7 @@ public final class okhttp3/MultipartBody : okhttp3/RequestBody { public final fun boundary ()Ljava/lang/String; public fun contentLength ()J public fun contentType ()Lokhttp3/MediaType; + public fun isOneShot ()Z public final fun part (I)Lokhttp3/MultipartBody$Part; public final fun parts ()Ljava/util/List; public final fun size ()I diff --git a/okhttp/src/main/kotlin/okhttp3/MultipartBody.kt b/okhttp/src/main/kotlin/okhttp3/MultipartBody.kt index cf6dbedb0492..83eb3773d090 100644 --- a/okhttp/src/main/kotlin/okhttp3/MultipartBody.kt +++ b/okhttp/src/main/kotlin/okhttp3/MultipartBody.kt @@ -49,6 +49,10 @@ class MultipartBody internal constructor( fun part(index: Int): Part = parts[index] + override fun isOneShot(): Boolean { + return parts.any { it.body.isOneShot() } + } + /** A combination of [type] and [boundaryByteString]. */ override fun contentType(): MediaType = contentType diff --git a/okhttp/src/test/java/okhttp3/MultipartBodyTest.kt b/okhttp/src/test/java/okhttp3/MultipartBodyTest.kt index dd8350fb6c13..f48aeb6e9fc4 100644 --- a/okhttp/src/test/java/okhttp3/MultipartBodyTest.kt +++ b/okhttp/src/test/java/okhttp3/MultipartBodyTest.kt @@ -23,6 +23,8 @@ import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody.Companion.toRequestBody import okio.Buffer import okio.BufferedSink +import okio.ByteString.Companion.encodeUtf8 +import okio.utf8Size import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions.fail import org.junit.jupiter.api.Test @@ -298,4 +300,65 @@ class MultipartBodyTest { body.writeTo(buffer) assertThat(buffer.readUtf8()).isEqualTo(expected) } + + @Test + fun writeTwice() { + val expected = """ + |--123 + | + |Hello, World! + |--123-- + | + """.trimMargin().replace("\n", "\r\n") + val body = MultipartBody.Builder("123") + .addPart("Hello, World!".toRequestBody(null)) + .build() + + assertThat(body.isOneShot()).isEqualTo(false) + + val buffer = Buffer() + body.writeTo(buffer) + assertThat(body.contentLength()).isEqualTo(buffer.size) + assertThat(buffer.readUtf8()).isEqualTo(expected) + + val buffer2 = Buffer() + body.writeTo(buffer2) + assertThat(body.contentLength()).isEqualTo(buffer2.size) + assertThat(buffer2.readUtf8()).isEqualTo(expected) + } + + @Test + fun writeTwiceWithOneShot() { + val expected = """ + |--123 + | + |Hello, World! + |--123-- + | + """.trimMargin().replace("\n", "\r\n") + val body = MultipartBody.Builder("123") + .addPart("Hello, World!".toOneShotRequestBody()) + .build() + + assertThat(body.isOneShot()).isEqualTo(true) + + val buffer = Buffer() + body.writeTo(buffer) + assertThat(body.contentLength()).isEqualTo(buffer.size) + assertThat(buffer.readUtf8()).isEqualTo(expected) + } + + fun String.toOneShotRequestBody(): RequestBody { + return object : RequestBody() { + override fun contentType() = null + + override fun isOneShot(): Boolean = true + + override fun contentLength() = this@toOneShotRequestBody.utf8Size() + + override fun writeTo(sink: BufferedSink) { + sink.writeUtf8(this@toOneShotRequestBody) + } + } + } }