@@ -83,9 +83,12 @@ public void testSimpleObjectOperations() {
8383 <?xml version="1.0" encoding="UTF-8"?><ListBucketResult><Prefix></Prefix><IsTruncated>false</IsTruncated>\
8484 </ListBucketResult>""" );
8585
86- final var body = randomAlphaOfLength (50 );
86+ final var body = new BytesArray ( randomAlphaOfLength (50 ). getBytes ( StandardCharsets . UTF_8 ) );
8787 assertEquals (RestStatus .OK , handleRequest (handler , "PUT" , "/bucket/path/blob" , body ).status ());
88- assertEquals (new TestHttpResponse (RestStatus .OK , body ), handleRequest (handler , "GET" , "/bucket/path/blob" ));
88+ assertEquals (
89+ new TestHttpResponse (RestStatus .OK , body , addETag (S3HttpHandler .getEtagFromContents (body ), TestHttpExchange .EMPTY_HEADERS )),
90+ handleRequest (handler , "GET" , "/bucket/path/blob" )
91+ );
8992
9093 assertListObjectsResponse (handler , "" , null , """
9194 <?xml version="1.0" encoding="UTF-8"?><ListBucketResult><Prefix></Prefix><IsTruncated>false</IsTruncated>\
@@ -135,39 +138,53 @@ public void testGetWithBytesRange() {
135138 final var blobBytes = randomBytesReference (256 );
136139 assertEquals (RestStatus .OK , handleRequest (handler , "PUT" , blobPath , blobBytes ).status ());
137140
141+ final var expectedEtag = S3HttpHandler .getEtagFromContents (blobBytes );
142+
138143 assertEquals (
139144 "No Range" ,
140- new TestHttpResponse (RestStatus .OK , blobBytes , TestHttpExchange .EMPTY_HEADERS ),
145+ new TestHttpResponse (RestStatus .OK , blobBytes , addETag ( expectedEtag , TestHttpExchange .EMPTY_HEADERS ) ),
141146 handleRequest (handler , "GET" , blobPath )
142147 );
143148
144149 var end = blobBytes .length () - 1 ;
145150 assertEquals (
146151 "Exact Range: bytes=0-" + end ,
147- new TestHttpResponse (RestStatus .PARTIAL_CONTENT , blobBytes , contentRangeHeader (0 , end , blobBytes .length ())),
152+ new TestHttpResponse (
153+ RestStatus .PARTIAL_CONTENT ,
154+ blobBytes ,
155+ addETag (expectedEtag , contentRangeHeader (0 , end , blobBytes .length ()))
156+ ),
148157 handleRequest (handler , "GET" , blobPath , BytesArray .EMPTY , bytesRangeHeader (0 , end ))
149158 );
150159
151160 end = randomIntBetween (blobBytes .length () - 1 , Integer .MAX_VALUE );
152161 assertEquals (
153162 "Larger Range: bytes=0-" + end ,
154- new TestHttpResponse (RestStatus .PARTIAL_CONTENT , blobBytes , contentRangeHeader (0 , blobBytes .length () - 1 , blobBytes .length ())),
163+ new TestHttpResponse (
164+ RestStatus .PARTIAL_CONTENT ,
165+ blobBytes ,
166+ addETag (expectedEtag , contentRangeHeader (0 , blobBytes .length () - 1 , blobBytes .length ()))
167+ ),
155168 handleRequest (handler , "GET" , blobPath , BytesArray .EMPTY , bytesRangeHeader (0 , end ))
156169 );
157170
158171 var start = randomIntBetween (blobBytes .length (), Integer .MAX_VALUE - 1 );
159172 end = randomIntBetween (start , Integer .MAX_VALUE );
160173 assertEquals (
161174 "Invalid Range: bytes=" + start + '-' + end ,
162- new TestHttpResponse (RestStatus .REQUESTED_RANGE_NOT_SATISFIED , BytesArray .EMPTY , TestHttpExchange .EMPTY_HEADERS ),
175+ new TestHttpResponse (
176+ RestStatus .REQUESTED_RANGE_NOT_SATISFIED ,
177+ BytesArray .EMPTY ,
178+ addETag (expectedEtag , TestHttpExchange .EMPTY_HEADERS )
179+ ),
163180 handleRequest (handler , "GET" , blobPath , BytesArray .EMPTY , bytesRangeHeader (start , end ))
164181 );
165182
166183 start = randomIntBetween (2 , Integer .MAX_VALUE - 1 );
167184 end = randomIntBetween (0 , start - 1 );
168185 assertEquals (
169186 "Weird Valid Range: bytes=" + start + '-' + end ,
170- new TestHttpResponse (RestStatus .OK , blobBytes , TestHttpExchange .EMPTY_HEADERS ),
187+ new TestHttpResponse (RestStatus .OK , blobBytes , addETag ( expectedEtag , TestHttpExchange .EMPTY_HEADERS ) ),
171188 handleRequest (handler , "GET" , blobPath , BytesArray .EMPTY , bytesRangeHeader (start , end ))
172189 );
173190
@@ -179,7 +196,7 @@ public void testGetWithBytesRange() {
179196 new TestHttpResponse (
180197 RestStatus .PARTIAL_CONTENT ,
181198 blobBytes .slice (start , length ),
182- contentRangeHeader (start , end , blobBytes .length ())
199+ addETag ( expectedEtag , contentRangeHeader (start , end , blobBytes .length () ))
183200 ),
184201 handleRequest (handler , "GET" , blobPath , BytesArray .EMPTY , bytesRangeHeader (start , end ))
185202 );
@@ -245,7 +262,15 @@ public void testSingleMultipartUpload() {
245262 <Contents><Key>path/blob</Key><Size>100</Size></Contents>\
246263 </ListBucketResult>""" );
247264
248- assertEquals (new TestHttpResponse (RestStatus .OK , part1 + part2 ), handleRequest (handler , "GET" , "/bucket/path/blob" ));
265+ final var expectedContents = new BytesArray ((part1 + part2 ).getBytes (StandardCharsets .UTF_8 ));
266+ assertEquals (
267+ new TestHttpResponse (
268+ RestStatus .OK ,
269+ expectedContents ,
270+ addETag (S3HttpHandler .getEtagFromContents (expectedContents ), TestHttpExchange .EMPTY_HEADERS )
271+ ),
272+ handleRequest (handler , "GET" , "/bucket/path/blob" )
273+ );
249274
250275 assertEquals (new TestHttpResponse (RestStatus .OK , """
251276 <?xml version='1.0' encoding='UTF-8'?>\
@@ -416,7 +441,11 @@ public void testPreventObjectOverwrite() throws InterruptedException {
416441 });
417442
418443 assertEquals (
419- new TestHttpResponse (RestStatus .OK , successfulTasks .getFirst ().body , TestHttpExchange .EMPTY_HEADERS ),
444+ new TestHttpResponse (
445+ RestStatus .OK ,
446+ successfulTasks .getFirst ().body ,
447+ addETag (S3HttpHandler .getEtagFromContents (successfulTasks .getFirst ().body ), TestHttpExchange .EMPTY_HEADERS )
448+ ),
420449 handleRequest (handler , "GET" , "/bucket/path/blob" )
421450 );
422451 }
@@ -459,6 +488,20 @@ private static TestWriteTask createMultipartUploadTask(S3HttpHandler handler) {
459488 return multipartUploadTask ;
460489 }
461490
491+ public void testGetETagFromContents () {
492+ // empty-string value from Wikipedia, see also org.elasticsearch.common.hash.MessageDigestsTests.testSha256
493+ assertETag ("" , "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" );
494+ assertETag ("The quick brown fox jumps over the lazy dog" , "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592" );
495+ assertETag ("The quick brown fox jumps over the lazy cog" , "e4c4d8f3bf76b692de791a173e05321150f7a345b46484fe427f6acc7ecc81be" );
496+ }
497+
498+ private static void assertETag (String input , String expectedHash ) {
499+ assertEquals (
500+ "\" es-test-sha-256-" + expectedHash + '"' ,
501+ S3HttpHandler .getEtagFromContents (new BytesArray (input .getBytes (StandardCharsets .UTF_8 )))
502+ );
503+ }
504+
462505 private static class TestWriteTask {
463506 final BytesReference body ;
464507 final Runnable consumer ;
@@ -562,6 +605,12 @@ private static Headers ifNoneMatchHeader() {
562605 return headers ;
563606 }
564607
608+ private static Headers addETag (String eTag , Headers headers ) {
609+ final var newHeaders = new Headers (headers );
610+ newHeaders .add ("ETag" , eTag );
611+ return newHeaders ;
612+ }
613+
565614 private static class TestHttpExchange extends HttpExchange {
566615
567616 private static final Headers EMPTY_HEADERS = new Headers ();
0 commit comments