|
25 | 25 | import static com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_ACL;
|
26 | 26 | import static com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_COPY_SOURCE;
|
27 | 27 | import static com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_COPY_SOURCE_IF_MATCH;
|
| 28 | +import static com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_COPY_SOURCE_IF_MODIFIED_SINCE; |
28 | 29 | import static com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_COPY_SOURCE_IF_NONE_MATCH;
|
| 30 | +import static com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_COPY_SOURCE_IF_UNMODIFIED_SINCE; |
29 | 31 | import static com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_DELETE_MARKER;
|
30 | 32 | import static com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_METADATA_DIRECTIVE;
|
31 | 33 | import static com.adobe.testing.s3mock.util.AwsHttpHeaders.X_AMZ_OBJECT_ATTRIBUTES;
|
|
58 | 60 | import static com.adobe.testing.s3mock.util.HeaderUtil.userMetadataHeadersFrom;
|
59 | 61 | import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
|
60 | 62 | import static org.springframework.http.HttpHeaders.IF_MATCH;
|
| 63 | +import static org.springframework.http.HttpHeaders.IF_MODIFIED_SINCE; |
61 | 64 | import static org.springframework.http.HttpHeaders.IF_NONE_MATCH;
|
| 65 | +import static org.springframework.http.HttpHeaders.IF_UNMODIFIED_SINCE; |
62 | 66 | import static org.springframework.http.HttpStatus.NOT_FOUND;
|
63 | 67 | import static org.springframework.http.HttpStatus.PARTIAL_CONTENT;
|
64 | 68 | import static org.springframework.http.HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE;
|
|
90 | 94 | import java.io.OutputStream;
|
91 | 95 | import java.nio.file.Files;
|
92 | 96 | import java.nio.file.Path;
|
| 97 | +import java.time.Instant; |
93 | 98 | import java.util.Collections;
|
94 | 99 | import java.util.List;
|
95 | 100 | import java.util.Map;
|
@@ -183,15 +188,18 @@ public ResponseEntity<DeleteResult> deleteObjects(
|
183 | 188 | public ResponseEntity<Void> headObject(@PathVariable String bucketName,
|
184 | 189 | @PathVariable ObjectKey key,
|
185 | 190 | @RequestHeader(value = IF_MATCH, required = false) List<String> match,
|
186 |
| - @RequestHeader(value = IF_NONE_MATCH, required = false) List<String> noneMatch) { |
187 |
| - //TODO: needs modified-since handling, see API |
| 191 | + @RequestHeader(value = IF_NONE_MATCH, required = false) List<String> noneMatch, |
| 192 | + @RequestHeader(value = IF_MODIFIED_SINCE, required = false) List<Instant> ifModifiedSince, |
| 193 | + @RequestHeader(value = IF_UNMODIFIED_SINCE, required = false) List<Instant> ifUnmodifiedSince |
| 194 | + ) { |
188 | 195 | bucketService.verifyBucketExists(bucketName);
|
189 | 196 |
|
190 | 197 | var s3ObjectMetadata = objectService.verifyObjectExists(bucketName, key.key());
|
191 | 198 | //return version id
|
192 | 199 |
|
193 | 200 | if (s3ObjectMetadata != null) {
|
194 |
| - objectService.verifyObjectMatching(match, noneMatch, s3ObjectMetadata); |
| 201 | + objectService.verifyObjectMatching(match, noneMatch, |
| 202 | + ifModifiedSince, ifUnmodifiedSince, s3ObjectMetadata); |
195 | 203 | return ResponseEntity.ok()
|
196 | 204 | .eTag(s3ObjectMetadata.etag())
|
197 | 205 | .lastModified(s3ObjectMetadata.lastModified())
|
@@ -260,12 +268,14 @@ public ResponseEntity<StreamingResponseBody> getObject(@PathVariable String buck
|
260 | 268 | @RequestHeader(value = RANGE, required = false) HttpRange range,
|
261 | 269 | @RequestHeader(value = IF_MATCH, required = false) List<String> match,
|
262 | 270 | @RequestHeader(value = IF_NONE_MATCH, required = false) List<String> noneMatch,
|
| 271 | + @RequestHeader(value = IF_MODIFIED_SINCE, required = false) List<Instant> ifModifiedSince, |
| 272 | + @RequestHeader(value = IF_UNMODIFIED_SINCE, required = false) List<Instant> ifUnmodifiedSince, |
263 | 273 | @RequestParam Map<String, String> queryParams) {
|
264 |
| - //TODO: needs modified-since handling, see API |
265 | 274 | bucketService.verifyBucketExists(bucketName);
|
266 | 275 |
|
267 | 276 | var s3ObjectMetadata = objectService.verifyObjectExists(bucketName, key.key());
|
268 |
| - objectService.verifyObjectMatching(match, noneMatch, s3ObjectMetadata); |
| 277 | + objectService.verifyObjectMatching(match, noneMatch, |
| 278 | + ifModifiedSince, ifUnmodifiedSince, s3ObjectMetadata); |
269 | 279 |
|
270 | 280 | if (range != null) {
|
271 | 281 | return getObjectWithRange(range, s3ObjectMetadata);
|
@@ -535,14 +545,16 @@ public ResponseEntity<GetObjectAttributesOutput> getObjectAttributes(
|
535 | 545 | @PathVariable ObjectKey key,
|
536 | 546 | @RequestHeader(value = IF_MATCH, required = false) List<String> match,
|
537 | 547 | @RequestHeader(value = IF_NONE_MATCH, required = false) List<String> noneMatch,
|
| 548 | + @RequestHeader(value = IF_MODIFIED_SINCE, required = false) List<Instant> ifModifiedSince, |
| 549 | + @RequestHeader(value = IF_UNMODIFIED_SINCE, required = false) List<Instant> ifUnmodifiedSince, |
538 | 550 | @RequestHeader(value = X_AMZ_OBJECT_ATTRIBUTES) List<String> objectAttributes) {
|
539 |
| - //TODO: needs modified-since handling, see API |
540 | 551 | bucketService.verifyBucketExists(bucketName);
|
541 | 552 |
|
542 | 553 | //this is for either an object request, or a parts request.
|
543 | 554 |
|
544 | 555 | S3ObjectMetadata s3ObjectMetadata = objectService.verifyObjectExists(bucketName, key.key());
|
545 |
| - objectService.verifyObjectMatching(match, noneMatch, s3ObjectMetadata); |
| 556 | + objectService.verifyObjectMatching(match, noneMatch, |
| 557 | + ifModifiedSince, ifUnmodifiedSince, s3ObjectMetadata); |
546 | 558 | //S3Mock stores the etag with the additional quotation marks needed in the headers. This
|
547 | 559 | // response does not use eTag as a header, so it must not contain the quotation marks.
|
548 | 560 | String etag = s3ObjectMetadata.etag().replace("\"", "");
|
@@ -688,13 +700,16 @@ public ResponseEntity<CopyObjectResult> copyObject(@PathVariable String bucketNa
|
688 | 700 | @RequestHeader(value = X_AMZ_COPY_SOURCE_IF_MATCH, required = false) List<String> match,
|
689 | 701 | @RequestHeader(value = X_AMZ_COPY_SOURCE_IF_NONE_MATCH,
|
690 | 702 | required = false) List<String> noneMatch,
|
| 703 | + @RequestHeader(value = X_AMZ_COPY_SOURCE_IF_MODIFIED_SINCE, |
| 704 | + required = false) List<Instant> ifModifiedSince, |
| 705 | + @RequestHeader(value = X_AMZ_COPY_SOURCE_IF_UNMODIFIED_SINCE, |
| 706 | + required = false) List<Instant> ifUnmodifiedSince, |
691 | 707 | @RequestHeader(value = X_AMZ_STORAGE_CLASS, required = false) StorageClass storageClass,
|
692 | 708 | @RequestHeader HttpHeaders httpHeaders) {
|
693 |
| - //TODO: needs modified-since handling, see API |
694 |
| - |
695 | 709 | bucketService.verifyBucketExists(bucketName);
|
696 | 710 | var s3ObjectMetadata = objectService.verifyObjectExists(copySource.bucket(), copySource.key());
|
697 |
| - objectService.verifyObjectMatchingForCopy(match, noneMatch, s3ObjectMetadata); |
| 711 | + objectService.verifyObjectMatchingForCopy(match, noneMatch, |
| 712 | + ifModifiedSince, ifUnmodifiedSince, s3ObjectMetadata); |
698 | 713 |
|
699 | 714 | Map<String, String> userMetadata = Collections.emptyMap();
|
700 | 715 | Map<String, String> storeHeaders = Collections.emptyMap();
|
|
0 commit comments