52
52
import static org .elasticsearch .test .fixture .HttpHeaderParser .parseRangeHeader ;
53
53
import static org .junit .Assert .assertEquals ;
54
54
import static org .junit .Assert .assertNotNull ;
55
+ import static org .junit .Assert .assertNull ;
55
56
import static org .w3c .dom .Node .ELEMENT_NODE ;
56
57
57
58
/**
@@ -121,9 +122,11 @@ public void handle(final HttpExchange exchange) throws IOException {
121
122
uploadsList .append ("<MaxUploads>10000</MaxUploads>" );
122
123
uploadsList .append ("<IsTruncated>false</IsTruncated>" );
123
124
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
+ }
127
130
}
128
131
}
129
132
@@ -135,9 +138,7 @@ public void handle(final HttpExchange exchange) throws IOException {
135
138
exchange .getResponseBody ().write (response );
136
139
137
140
} 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 ));
141
142
final var uploadResult = new StringBuilder ();
142
143
uploadResult .append ("<?xml version='1.0' encoding='UTF-8'?>" );
143
144
uploadResult .append ("<InitiateMultipartUploadResult>" );
@@ -152,7 +153,7 @@ public void handle(final HttpExchange exchange) throws IOException {
152
153
exchange .getResponseBody ().write (response );
153
154
154
155
} else if (request .isUploadPartRequest ()) {
155
- final var upload = uploads . get (request .getQueryParamOnce ("uploadId" ));
156
+ final var upload = getUpload (request .getQueryParamOnce ("uploadId" ));
156
157
if (upload == null ) {
157
158
exchange .sendResponseHeaders (RestStatus .NOT_FOUND .getStatus (), -1 );
158
159
} else {
@@ -187,42 +188,45 @@ public void handle(final HttpExchange exchange) throws IOException {
187
188
}
188
189
189
190
} 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
+ }
194
207
} 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 );
206
219
}
220
+ }
221
+ if (responseBody == null ) {
222
+ exchange .sendResponseHeaders (RestStatus .NOT_FOUND .getStatus (), -1 );
207
223
} 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 );
220
224
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 );
223
227
}
224
228
} else if (request .isAbortMultipartUploadRequest ()) {
225
- final var upload = uploads . remove (request .getQueryParamOnce ("uploadId" ));
229
+ final var upload = removeUpload (request .getQueryParamOnce ("uploadId" ));
226
230
exchange .sendResponseHeaders ((upload == null ? RestStatus .NOT_FOUND : RestStatus .NO_CONTENT ).getStatus (), -1 );
227
231
228
232
} else if (request .isPutObjectRequest ()) {
@@ -521,8 +525,24 @@ private static HttpHeaderParser.Range parsePartRange(final HttpExchange exchange
521
525
return parseRangeHeader (sourceRangeHeaders .getFirst ());
522
526
}
523
527
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
+
524
536
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
+ }
526
546
}
527
547
528
548
public S3Request parseRequest (HttpExchange exchange ) {
0 commit comments