@@ -102,9 +102,10 @@ public void testSimpleObjectOperations() {
102102 assertEquals (new TestHttpResponse (RestStatus .OK , """
103103 {"kind":"storage#objects","items":[],"prefixes":[]}""" ), listBlobs (handler , bucket , "some/other/path" , null ));
104104
105+ var boundary = newMultipartBoundary ();
105106 assertEquals (
106107 new TestHttpResponse (RestStatus .OK , """
107- --__END_OF_PART__d8b50acb-87dc-4630-a3d3-17d187132ebc__
108+ --$boundary
108109 Content-Length: 168
109110 Content-Type: application/http
110111 content-id: 1
@@ -115,13 +116,13 @@ public void testSimpleObjectOperations() {
115116
116117
117118
118- --__END_OF_PART__d8b50acb-87dc-4630-a3d3-17d187132ebc__
119- """ .replaceAll ("\n " , "\r \n " )),
119+ --$boundary--
120+ """ .replace ("\n " , "\r \n " ). replace ( "$boundary" , boundary )),
120121 handleRequest (
121122 handler ,
122123 "POST" ,
123124 "/batch/storage/v1" ,
124- createBatchDeleteRequest (bucket , blobName ),
125+ createBatchDeleteRequest (bucket , boundary , blobName ),
125126 Headers .of ("Content-Type" , "mixed/multipart" )
126127 )
127128 );
@@ -131,7 +132,7 @@ public void testSimpleObjectOperations() {
131132 handler ,
132133 "POST" ,
133134 "/batch/storage/v1" ,
134- createBatchDeleteRequest (bucket , blobName ),
135+ createBatchDeleteRequest (bucket , boundary , blobName ),
135136 Headers .of ("Content-Type" , "mixed/multipart" )
136137 ).restStatus ()
137138 );
@@ -617,12 +618,13 @@ private static TestHttpResponse executeMultipartUpload(
617618 ) {
618619 var headers = new Headers ();
619620 // multipart upload is required to provide boundary header
620- headers .put ("Content-Type" , List .of ("multipart/related; boundary=" + MULTIPART_BOUNDARY ));
621+ var boundary = newMultipartBoundary ();
622+ headers .put ("Content-Type" , List .of ("multipart/related; boundary=" + boundary ));
621623 return handleRequest (
622624 handler ,
623625 "POST" ,
624626 "/upload/storage/v1/b/" + bucket + "/" + generateQueryString ("uploadType" , "multipart" , "ifGenerationMatch" , ifGenerationMatch ),
625- createGzipCompressedMultipartUploadBody (bucket , blobName , bytes ),
627+ createGzipCompressedMultipartUploadBody (bucket , blobName , bytes , boundary ),
626628 headers
627629 );
628630 }
@@ -791,25 +793,37 @@ private static Headers rangeHeader(long start, long end) {
791793
792794 private static final String MULTIPART_BOUNDARY = "__END_OF_PART__a607a67c-6df7-4b87-b8a1-81f639a75a97__" ;
793795
794- private static BytesReference createGzipCompressedMultipartUploadBody (String bucketName , String path , BytesReference content ) {
796+ private static String newMultipartBoundary () {
797+ return "__END_OF_PART__" + randomUUID ();
798+ }
799+
800+ private static BytesReference createGzipCompressedMultipartUploadBody (
801+ String bucketName ,
802+ String path ,
803+ BytesReference content ,
804+ String boundary
805+ ) {
795806 final String metadataString = Strings .format ("{\" bucket\" :\" %s\" , \" name\" :\" %s\" }" , bucketName , path );
796- final BytesReference header = new BytesArray ( Strings . format ( """
797- --__END_OF_PART__a607a67c-6df7-4b87-b8a1-81f639a75a97__
798- Content-Length: %d
807+ final String headerStr = """
808+ --$boundary
809+ Content-Length: $metadata-length
799810 Content-Type: application/json; charset=UTF-8
800811 content-transfer-encoding: binary
801812
802- %s
803- --__END_OF_PART__a607a67c-6df7-4b87-b8a1-81f639a75a97__
813+ $metadata
814+ --$boundary
804815 Content-Type: application/octet-stream
805816 content-transfer-encoding: binary
806817
807- """ .replaceAll ("\n " , "\r \n " ), metadataString .length (), metadataString ).getBytes (StandardCharsets .UTF_8 ));
808-
818+ """ .replace ("\n " , "\r \n " )
819+ .replace ("$boundary" , boundary )
820+ .replace ("$metadata-length" , Integer .toString (metadataString .length ()))
821+ .replace ("$metadata" , metadataString );
822+ final BytesReference header = new BytesArray (headerStr .getBytes (StandardCharsets .UTF_8 ));
809823 final BytesReference footer = new BytesArray ("""
810824
811- --__END_OF_PART__a607a67c-6df7-4b87-b8a1-81f639a75a97__ --
812- """ .replaceAll ("\n " , "\r \n " ));
825+ --$boundary --
826+ """ .replace ("\n " , "\r \n " ). replace ( "$boundary" , boundary ));
813827 final ByteArrayOutputStream out = new ByteArrayOutputStream ();
814828 try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream (out )) {
815829 gzipOutputStream .write (BytesReference .toBytes (CompositeBytesReference .of (header , content , footer )));
@@ -819,7 +833,7 @@ private static BytesReference createGzipCompressedMultipartUploadBody(String buc
819833 return new BytesArray (out .toByteArray ());
820834 }
821835
822- private static String createBatchDeleteRequest (String bucketName , String ... paths ) {
836+ private static String createBatchDeleteRequest (String bucketName , String boundary , String ... paths ) {
823837 final String deleteRequestTemplate = """
824838 DELETE %s/storage/v1/b/%s/o/%s HTTP/1.1
825839 Authorization: Bearer foo
@@ -828,22 +842,22 @@ private static String createBatchDeleteRequest(String bucketName, String... path
828842
829843 """ ;
830844 final String partTemplate = """
831- --__END_OF_PART__d8b50acb-87dc-4630-a3d3-17d187132ebc__
845+ --$boundary
832846 Content-Length: %d
833847 Content-Type: application/http
834848 content-id: %d
835849 content-transfer-encoding: binary
836850
837851 %s
838- """ ;
852+ """ . replace ( "$boundary" , boundary ) ;
839853 StringBuilder builder = new StringBuilder ();
840854 AtomicInteger contentId = new AtomicInteger ();
841855 Arrays .stream (paths ).forEach (p -> {
842856 final String deleteRequest = Strings .format (deleteRequestTemplate , HOST , bucketName , p );
843857 final String part = Strings .format (partTemplate , deleteRequest .length (), contentId .incrementAndGet (), deleteRequest );
844858 builder .append (part );
845859 });
846- builder .append ("--__END_OF_PART__d8b50acb-87dc-4630-a3d3-17d187132ebc__ " );
860+ builder .append ("--" ). append ( boundary ). append ( "-- " );
847861 return builder .toString ();
848862 }
849863
0 commit comments