From 0aabd62a669c6c3c389453c348d37d66871ae4a4 Mon Sep 17 00:00:00 2001
From: Yuri Schimke <yuri@schimke.ee>
Date: Sun, 24 Dec 2023 08:57:28 +0000
Subject: [PATCH 1/3] Support oneshot forms

---
 .../src/main/kotlin/okhttp3/MultipartBody.kt  |  4 ++
 .../test/java/okhttp3/MultipartBodyTest.kt    | 63 +++++++++++++++++++
 2 files changed, 67 insertions(+)

diff --git a/okhttp/src/main/kotlin/okhttp3/MultipartBody.kt b/okhttp/src/main/kotlin/okhttp3/MultipartBody.kt
index cf6dbedb0492..7ec8166309fc 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.find { it.body.isOneShot() } != null
+  }
+
   /** 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)
+      }
+    }
+  }
 }

From d74a5fa91fecd8848c0e27aa50920f839b6fc75a Mon Sep 17 00:00:00 2001
From: Yuri Schimke <yuri@schimke.ee>
Date: Sun, 24 Dec 2023 08:58:59 +0000
Subject: [PATCH 2/3] Support oneshot forms

---
 okhttp/src/main/kotlin/okhttp3/MultipartBody.kt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/okhttp/src/main/kotlin/okhttp3/MultipartBody.kt b/okhttp/src/main/kotlin/okhttp3/MultipartBody.kt
index 7ec8166309fc..83eb3773d090 100644
--- a/okhttp/src/main/kotlin/okhttp3/MultipartBody.kt
+++ b/okhttp/src/main/kotlin/okhttp3/MultipartBody.kt
@@ -50,7 +50,7 @@ class MultipartBody internal constructor(
   fun part(index: Int): Part = parts[index]
 
   override fun isOneShot(): Boolean {
-    return parts.find { it.body.isOneShot() } != null
+    return parts.any { it.body.isOneShot() }
   }
 
   /** A combination of [type] and [boundaryByteString]. */

From 1c24711cce99884fc613319fff682e5c98a9fe52 Mon Sep 17 00:00:00 2001
From: Yuri Schimke <yuri@schimke.ee>
Date: Sun, 24 Dec 2023 09:13:14 +0000
Subject: [PATCH 3/3] Fix

---
 okhttp/api/okhttp.api | 1 +
 1 file changed, 1 insertion(+)

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