From d92087114fffc592aeec229c6da659e5d1bc6a62 Mon Sep 17 00:00:00 2001 From: Stefan Bratanov Date: Mon, 29 Jan 2024 09:37:45 +0200 Subject: [PATCH] Use UUID for `multipart/form-data` boundaries --- .../jvm/openai/AudioClient.java | 18 +++++++++------- .../jvm/openai/FilesClient.java | 5 ++--- .../jvm/openai/ImagesClient.java | 18 +++++++++------- .../jvm/openai/MultipartBodyPublisher.java | 21 ++++++++++++------- 4 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/main/java/io/github/stefanbratanov/jvm/openai/AudioClient.java b/src/main/java/io/github/stefanbratanov/jvm/openai/AudioClient.java index 64ca98b..76f0ca2 100644 --- a/src/main/java/io/github/stefanbratanov/jvm/openai/AudioClient.java +++ b/src/main/java/io/github/stefanbratanov/jvm/openai/AudioClient.java @@ -121,9 +121,8 @@ private HttpRequest createSpeechPostRequest(SpeechRequest request) { } private HttpRequest createTranscriptPostRequest(TranscriptionRequest request) { - long boundary = System.currentTimeMillis(); MultipartBodyPublisher.Builder multipartBodyPublisherBuilder = - MultipartBodyPublisher.newBuilder(boundary) + MultipartBodyPublisher.newBuilder() .filePart("file", request.file()) .textPart("model", request.model()); request @@ -135,17 +134,18 @@ private HttpRequest createTranscriptPostRequest(TranscriptionRequest request) { .ifPresent( temperature -> multipartBodyPublisherBuilder.textPart("temperature", temperature)); + MultipartBodyPublisher multipartBodyPublisher = multipartBodyPublisherBuilder.build(); + return newHttpRequestBuilder( - Constants.CONTENT_TYPE_HEADER, "multipart/form-data; boundary=" + boundary) + Constants.CONTENT_TYPE_HEADER, multipartBodyPublisher.getContentTypeHeader()) .uri(baseUrl.resolve(Endpoint.TRANSCRIPTION.getPath())) - .POST(multipartBodyPublisherBuilder.build()) + .POST(multipartBodyPublisher) .build(); } private HttpRequest createTranslationPostRequest(TranslationRequest request) { - long boundary = System.currentTimeMillis(); MultipartBodyPublisher.Builder multipartBodyPublisherBuilder = - MultipartBodyPublisher.newBuilder(boundary) + MultipartBodyPublisher.newBuilder() .filePart("file", request.file()) .textPart("model", request.model()); request.prompt().ifPresent(prompt -> multipartBodyPublisherBuilder.textPart("prompt", prompt)); @@ -154,10 +154,12 @@ private HttpRequest createTranslationPostRequest(TranslationRequest request) { .ifPresent( temperature -> multipartBodyPublisherBuilder.textPart("temperature", temperature)); + MultipartBodyPublisher multipartBodyPublisher = multipartBodyPublisherBuilder.build(); + return newHttpRequestBuilder( - Constants.CONTENT_TYPE_HEADER, "multipart/form-data; boundary=" + boundary) + Constants.CONTENT_TYPE_HEADER, multipartBodyPublisher.getContentTypeHeader()) .uri(baseUrl.resolve(Endpoint.TRANSLATION.getPath())) - .POST(multipartBodyPublisherBuilder.build()) + .POST(multipartBodyPublisher) .build(); } } diff --git a/src/main/java/io/github/stefanbratanov/jvm/openai/FilesClient.java b/src/main/java/io/github/stefanbratanov/jvm/openai/FilesClient.java index 417b827..905573a 100644 --- a/src/main/java/io/github/stefanbratanov/jvm/openai/FilesClient.java +++ b/src/main/java/io/github/stefanbratanov/jvm/openai/FilesClient.java @@ -29,16 +29,15 @@ public final class FilesClient extends OpenAIClient { * @throws OpenAIException in case of API errors */ public File uploadFile(UploadFileRequest request) { - long boundary = System.currentTimeMillis(); MultipartBodyPublisher multipartBodyPublisher = - MultipartBodyPublisher.newBuilder(boundary) + MultipartBodyPublisher.newBuilder() .filePart("file", request.file()) .textPart("purpose", request.purpose()) .build(); HttpRequest httpRequest = newHttpRequestBuilder( - Constants.CONTENT_TYPE_HEADER, "multipart/form-data; boundary=" + boundary) + Constants.CONTENT_TYPE_HEADER, multipartBodyPublisher.getContentTypeHeader()) .uri(baseUrl.resolve(Endpoint.FILES.getPath())) .POST(multipartBodyPublisher) .build(); diff --git a/src/main/java/io/github/stefanbratanov/jvm/openai/ImagesClient.java b/src/main/java/io/github/stefanbratanov/jvm/openai/ImagesClient.java index db7e701..31b3f14 100644 --- a/src/main/java/io/github/stefanbratanov/jvm/openai/ImagesClient.java +++ b/src/main/java/io/github/stefanbratanov/jvm/openai/ImagesClient.java @@ -98,9 +98,8 @@ private HttpRequest createImagePostRequest(CreateImageRequest request) { } private HttpRequest editImagePostRequest(EditImageRequest request) { - long boundary = System.currentTimeMillis(); MultipartBodyPublisher.Builder multipartBodyPublisherBuilder = - MultipartBodyPublisher.newBuilder(boundary) + MultipartBodyPublisher.newBuilder() .filePart("image", request.image()) .textPart("prompt", request.prompt()); request.mask().ifPresent(mask -> multipartBodyPublisherBuilder.filePart("mask", mask)); @@ -114,17 +113,18 @@ private HttpRequest editImagePostRequest(EditImageRequest request) { multipartBodyPublisherBuilder.textPart("response_format", responseFormat)); request.user().ifPresent(user -> multipartBodyPublisherBuilder.textPart("user", user)); + MultipartBodyPublisher multipartBodyPublisher = multipartBodyPublisherBuilder.build(); + return newHttpRequestBuilder( - Constants.CONTENT_TYPE_HEADER, "multipart/form-data; boundary=" + boundary) + Constants.CONTENT_TYPE_HEADER, multipartBodyPublisher.getContentTypeHeader()) .uri(baseUrl.resolve(Endpoint.IMAGE_EDIT.getPath())) - .POST(multipartBodyPublisherBuilder.build()) + .POST(multipartBodyPublisher) .build(); } private HttpRequest createImageVariationPostRequest(CreateImageVariationRequest request) { - long boundary = System.currentTimeMillis(); MultipartBodyPublisher.Builder multipartBodyPublisherBuilder = - MultipartBodyPublisher.newBuilder(boundary).filePart("image", request.image()); + MultipartBodyPublisher.newBuilder().filePart("image", request.image()); request.model().ifPresent(model -> multipartBodyPublisherBuilder.textPart("model", model)); request .responseFormat() @@ -135,10 +135,12 @@ private HttpRequest createImageVariationPostRequest(CreateImageVariationRequest request.size().ifPresent(size -> multipartBodyPublisherBuilder.textPart("size", size)); request.user().ifPresent(user -> multipartBodyPublisherBuilder.textPart("user", user)); + MultipartBodyPublisher multipartBodyPublisher = multipartBodyPublisherBuilder.build(); + return newHttpRequestBuilder( - Constants.CONTENT_TYPE_HEADER, "multipart/form-data; boundary=" + boundary) + Constants.CONTENT_TYPE_HEADER, multipartBodyPublisher.getContentTypeHeader()) .uri(baseUrl.resolve(Endpoint.IMAGE_VARIATION.getPath())) - .POST(multipartBodyPublisherBuilder.build()) + .POST(multipartBodyPublisher) .build(); } } diff --git a/src/main/java/io/github/stefanbratanov/jvm/openai/MultipartBodyPublisher.java b/src/main/java/io/github/stefanbratanov/jvm/openai/MultipartBodyPublisher.java index 9c97042..5ba1e2f 100644 --- a/src/main/java/io/github/stefanbratanov/jvm/openai/MultipartBodyPublisher.java +++ b/src/main/java/io/github/stefanbratanov/jvm/openai/MultipartBodyPublisher.java @@ -8,13 +8,16 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import java.util.concurrent.Flow; class MultipartBodyPublisher implements HttpRequest.BodyPublisher { + private final String boundary; private final List multipartBodyParts; - private MultipartBodyPublisher(List multipartBodyParts) { + private MultipartBodyPublisher(String boundary, List multipartBodyParts) { + this.boundary = boundary; this.multipartBodyParts = multipartBodyParts; } @@ -48,21 +51,25 @@ public void cancel() { }); } - static Builder newBuilder(long boundary) { - return new Builder(boundary); + String getContentTypeHeader() { + return "multipart/form-data; boundary=" + boundary; + } + + static Builder newBuilder() { + return new Builder(); } static class Builder { private static final String CRLF = "\r\n"; - private final long boundary; + private final String boundary; private final String separator; private final List multipartBodyParts = new ArrayList<>(); - Builder(long boundary) { - this.boundary = boundary; + Builder() { + boundary = UUID.randomUUID().toString(); separator = "--" + boundary + CRLF + "Content-Disposition: form-data; name="; } @@ -102,7 +109,7 @@ Builder filePart(String key, Path value) { MultipartBodyPublisher build() { multipartBodyParts.add(("--" + boundary + "--").getBytes()); - return new MultipartBodyPublisher(multipartBodyParts); + return new MultipartBodyPublisher(boundary, multipartBodyParts); } } }