Add an "excessively large object" safeguard.
This commit is contained in:
parent
fbaf4a09e2
commit
28e3b23e8c
|
@ -25,6 +25,9 @@ public class TorExitNodeConfiguration {
|
|||
@NotBlank
|
||||
private String objectKey;
|
||||
|
||||
@JsonProperty
|
||||
private long maxSize = 16 * 1024 * 1024;
|
||||
|
||||
@JsonProperty
|
||||
private Duration refreshInterval = Duration.ofMinutes(5);
|
||||
|
||||
|
@ -45,6 +48,15 @@ public class TorExitNodeConfiguration {
|
|||
return objectKey;
|
||||
}
|
||||
|
||||
public long getMaxSize() {
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setMaxSize(final long maxSize) {
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
public Duration getRefreshInterval() {
|
||||
return refreshInterval;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ public class S3ObjectMonitor implements Managed {
|
|||
|
||||
private final String s3Bucket;
|
||||
private final String objectKey;
|
||||
private final long maxObjectSize;
|
||||
|
||||
private final ScheduledExecutorService refreshExecutorService;
|
||||
private final Duration refreshInterval;
|
||||
|
@ -46,6 +47,7 @@ public class S3ObjectMonitor implements Managed {
|
|||
final String s3Region,
|
||||
final String s3Bucket,
|
||||
final String objectKey,
|
||||
final long maxObjectSize,
|
||||
final ScheduledExecutorService refreshExecutorService,
|
||||
final Duration refreshInterval,
|
||||
final Consumer<S3Object> changeListener) {
|
||||
|
@ -56,6 +58,7 @@ public class S3ObjectMonitor implements Managed {
|
|||
.build(),
|
||||
s3Bucket,
|
||||
objectKey,
|
||||
maxObjectSize,
|
||||
refreshExecutorService,
|
||||
refreshInterval,
|
||||
changeListener);
|
||||
|
@ -66,6 +69,7 @@ public class S3ObjectMonitor implements Managed {
|
|||
final AmazonS3 s3Client,
|
||||
final String s3Bucket,
|
||||
final String objectKey,
|
||||
final long maxObjectSize,
|
||||
final ScheduledExecutorService refreshExecutorService,
|
||||
final Duration refreshInterval,
|
||||
final Consumer<S3Object> changeListener) {
|
||||
|
@ -73,6 +77,7 @@ public class S3ObjectMonitor implements Managed {
|
|||
this.s3Client = s3Client;
|
||||
this.s3Bucket = s3Bucket;
|
||||
this.objectKey = objectKey;
|
||||
this.maxObjectSize = maxObjectSize;
|
||||
|
||||
this.refreshExecutorService = refreshExecutorService;
|
||||
this.refreshInterval = refreshInterval;
|
||||
|
@ -111,7 +116,17 @@ public class S3ObjectMonitor implements Managed {
|
|||
final String refreshedETag = objectMetadata.getETag();
|
||||
|
||||
if (!StringUtils.equals(initialETag, refreshedETag) && lastETag.compareAndSet(initialETag, refreshedETag)) {
|
||||
changeListener.accept(s3Client.getObject(s3Bucket, objectKey));
|
||||
final S3Object s3Object = s3Client.getObject(s3Bucket, objectKey);
|
||||
|
||||
log.info("Object at s3://{}/{} has changed; new eTag is {} and object size is {} bytes",
|
||||
s3Bucket, objectKey, s3Object.getObjectMetadata().getETag(), s3Object.getObjectMetadata().getContentLength());
|
||||
|
||||
if (s3Object.getObjectMetadata().getContentLength() <= maxObjectSize) {
|
||||
changeListener.accept(s3Object);
|
||||
} else {
|
||||
log.warn("Object at s3://{}/{} has a size of {} bytes, which exceeds the maximum allowed size of {} bytes",
|
||||
s3Bucket, objectKey, s3Object.getObjectMetadata().getContentLength(), maxObjectSize);
|
||||
}
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
log.warn("Failed to refresh monitored object", e);
|
||||
|
|
|
@ -49,6 +49,7 @@ public class TorExitNodeManager implements Managed {
|
|||
configuration.getS3Region(),
|
||||
configuration.getS3Bucket(),
|
||||
configuration.getObjectKey(),
|
||||
configuration.getMaxSize(),
|
||||
scheduledExecutorService,
|
||||
configuration.getRefreshInterval(),
|
||||
this::handleExitListChanged);
|
||||
|
|
|
@ -10,8 +10,9 @@ import java.util.UUID;
|
|||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
|
@ -32,7 +33,9 @@ class S3ObjectMonitorTest {
|
|||
final S3ObjectMonitor objectMonitor = new S3ObjectMonitor(
|
||||
s3Client,
|
||||
bucket,
|
||||
objectKey, mock(ScheduledExecutorService.class),
|
||||
objectKey,
|
||||
16 * 1024 * 1024,
|
||||
mock(ScheduledExecutorService.class),
|
||||
Duration.ofMinutes(1),
|
||||
listener);
|
||||
|
||||
|
@ -46,4 +49,37 @@ class S3ObjectMonitorTest {
|
|||
|
||||
verify(listener).accept(s3Object);
|
||||
}
|
||||
|
||||
@Test
|
||||
void refreshOversizedObject() {
|
||||
final AmazonS3 s3Client = mock(AmazonS3.class);
|
||||
final ObjectMetadata metadata = mock(ObjectMetadata.class);
|
||||
final S3Object s3Object = mock(S3Object.class);
|
||||
|
||||
final String bucket = "s3bucket";
|
||||
final String objectKey = "greatest-smooth-jazz-hits-of-all-time.zip";
|
||||
final long maxObjectSize = 16 * 1024 * 1024;
|
||||
|
||||
//noinspection unchecked
|
||||
final Consumer<S3Object> listener = mock(Consumer.class);
|
||||
|
||||
final S3ObjectMonitor objectMonitor = new S3ObjectMonitor(
|
||||
s3Client,
|
||||
bucket,
|
||||
objectKey,
|
||||
maxObjectSize,
|
||||
mock(ScheduledExecutorService.class),
|
||||
Duration.ofMinutes(1),
|
||||
listener);
|
||||
|
||||
when(metadata.getETag()).thenReturn(UUID.randomUUID().toString());
|
||||
when(metadata.getContentLength()).thenReturn(maxObjectSize + 1);
|
||||
when(s3Object.getObjectMetadata()).thenReturn(metadata);
|
||||
when(s3Client.getObjectMetadata(bucket, objectKey)).thenReturn(metadata);
|
||||
when(s3Client.getObject(bucket, objectKey)).thenReturn(s3Object);
|
||||
|
||||
objectMonitor.refresh();
|
||||
|
||||
verify(listener, never()).accept(any());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue