5252import static org .elasticsearch .test .fixture .HttpHeaderParser .parseRangeHeader ;
5353import static org .junit .Assert .assertEquals ;
5454import static org .junit .Assert .assertNotNull ;
55+ import static org .junit .Assert .assertNull ;
5556import static org .w3c .dom .Node .ELEMENT_NODE ;
5657
5758/**
@@ -121,9 +122,11 @@ public void handle(final HttpExchange exchange) throws IOException {
121122 uploadsList .append ("<MaxUploads>10000</MaxUploads>" );
122123 uploadsList .append ("<IsTruncated>false</IsTruncated>" );
123124
124- for (final var multipartUpload : uploads .values ()) {
125- if (multipartUpload .getPath ().startsWith (prefix )) {
126- multipartUpload .appendXml (uploadsList );
125+ synchronized (uploads ) {
126+ for (final var multipartUpload : uploads .values ()) {
127+ if (multipartUpload .getPath ().startsWith (prefix )) {
128+ multipartUpload .appendXml (uploadsList );
129+ }
127130 }
128131 }
129132
@@ -135,9 +138,7 @@ public void handle(final HttpExchange exchange) throws IOException {
135138 exchange .getResponseBody ().write (response );
136139
137140 } else if (request .isInitiateMultipartUploadRequest ()) {
138- final var upload = new MultipartUpload (UUIDs .randomBase64UUID (), request .path ().substring (bucket .length () + 2 ));
139- uploads .put (upload .getUploadId (), upload );
140-
141+ final var upload = putUpload (request .path ().substring (bucket .length () + 2 ));
141142 final var uploadResult = new StringBuilder ();
142143 uploadResult .append ("<?xml version='1.0' encoding='UTF-8'?>" );
143144 uploadResult .append ("<InitiateMultipartUploadResult>" );
@@ -152,7 +153,7 @@ public void handle(final HttpExchange exchange) throws IOException {
152153 exchange .getResponseBody ().write (response );
153154
154155 } else if (request .isUploadPartRequest ()) {
155- final var upload = uploads . get (request .getQueryParamOnce ("uploadId" ));
156+ final var upload = getUpload (request .getQueryParamOnce ("uploadId" ));
156157 if (upload == null ) {
157158 exchange .sendResponseHeaders (RestStatus .NOT_FOUND .getStatus (), -1 );
158159 } else {
@@ -187,42 +188,45 @@ public void handle(final HttpExchange exchange) throws IOException {
187188 }
188189
189190 } else if (request .isCompleteMultipartUploadRequest ()) {
190- final var upload = uploads .remove (request .getQueryParamOnce ("uploadId" ));
191- if (upload == null ) {
192- if (Randomness .get ().nextBoolean ()) {
193- exchange .sendResponseHeaders (RestStatus .NOT_FOUND .getStatus (), -1 );
191+ final byte [] responseBody ;
192+ synchronized (uploads ) {
193+ final var upload = removeUpload (request .getQueryParamOnce ("uploadId" ));
194+ if (upload == null ) {
195+ if (Randomness .get ().nextBoolean ()) {
196+ responseBody = null ;
197+ } else {
198+ responseBody = """
199+ <?xml version="1.0" encoding="UTF-8"?>
200+ <Error>
201+ <Code>NoSuchUpload</Code>
202+ <Message>No such upload</Message>
203+ <RequestId>test-request-id</RequestId>
204+ <HostId>test-host-id</HostId>
205+ </Error>""" .getBytes (StandardCharsets .UTF_8 );
206+ }
194207 } else {
195- byte [] response = ( """
196- <?xml version="1.0" encoding="UTF-8"?>
197- <Error>
198- <Code>NoSuchUpload</Code>
199- <Message>No such upload</Message>
200- <RequestId>test-request-id</RequestId>
201- <HostId>test-host-id</HostId>
202- </Error>""" ). getBytes ( StandardCharsets . UTF_8 );
203- exchange . getResponseHeaders (). add ( "Content-Type" , "application/xml" );
204- exchange . sendResponseHeaders ( RestStatus . OK . getStatus (), response . length );
205- exchange . getResponseBody (). write ( response );
208+ final var blobContents = upload . complete ( extractPartEtags ( Streams . readFully ( exchange . getRequestBody ())));
209+ blobs . put ( request . path (), blobContents );
210+ responseBody = ( "<?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> \n "
211+ + "<CompleteMultipartUploadResult> \n "
212+ + "<Bucket>"
213+ + bucket
214+ + "</Bucket> \n "
215+ + "<Key>"
216+ + request . path ()
217+ + "</Key> \n "
218+ + "</CompleteMultipartUploadResult>" ). getBytes ( StandardCharsets . UTF_8 );
206219 }
220+ }
221+ if (responseBody == null ) {
222+ exchange .sendResponseHeaders (RestStatus .NOT_FOUND .getStatus (), -1 );
207223 } else {
208- final var blobContents = upload .complete (extractPartEtags (Streams .readFully (exchange .getRequestBody ())));
209- blobs .put (request .path (), blobContents );
210-
211- byte [] response = ("<?xml version=\" 1.0\" encoding=\" UTF-8\" ?>\n "
212- + "<CompleteMultipartUploadResult>\n "
213- + "<Bucket>"
214- + bucket
215- + "</Bucket>\n "
216- + "<Key>"
217- + request .path ()
218- + "</Key>\n "
219- + "</CompleteMultipartUploadResult>" ).getBytes (StandardCharsets .UTF_8 );
220224 exchange .getResponseHeaders ().add ("Content-Type" , "application/xml" );
221- exchange .sendResponseHeaders (RestStatus .OK .getStatus (), response .length );
222- exchange .getResponseBody ().write (response );
225+ exchange .sendResponseHeaders (RestStatus .OK .getStatus (), responseBody .length );
226+ exchange .getResponseBody ().write (responseBody );
223227 }
224228 } else if (request .isAbortMultipartUploadRequest ()) {
225- final var upload = uploads . remove (request .getQueryParamOnce ("uploadId" ));
229+ final var upload = removeUpload (request .getQueryParamOnce ("uploadId" ));
226230 exchange .sendResponseHeaders ((upload == null ? RestStatus .NOT_FOUND : RestStatus .NO_CONTENT ).getStatus (), -1 );
227231
228232 } else if (request .isPutObjectRequest ()) {
@@ -521,8 +525,24 @@ private static HttpHeaderParser.Range parsePartRange(final HttpExchange exchange
521525 return parseRangeHeader (sourceRangeHeaders .getFirst ());
522526 }
523527
528+ MultipartUpload putUpload (String path ) {
529+ final var upload = new MultipartUpload (UUIDs .randomBase64UUID (), path );
530+ synchronized (uploads ) {
531+ assertNull ("upload " + upload .getUploadId () + " should not exist" , uploads .put (upload .getUploadId (), upload ));
532+ return upload ;
533+ }
534+ }
535+
524536 MultipartUpload getUpload (String uploadId ) {
525- return uploads .get (uploadId );
537+ synchronized (uploads ) {
538+ return uploads .get (uploadId );
539+ }
540+ }
541+
542+ MultipartUpload removeUpload (String uploadId ) {
543+ synchronized (uploads ) {
544+ return uploads .remove (uploadId );
545+ }
526546 }
527547
528548 public S3Request parseRequest (HttpExchange exchange ) {
0 commit comments