Merge remote-tracking branch 'remotes/origin/master' into expunge-resource-hook
This commit is contained in:
commit
84f5c7d467
|
@ -213,6 +213,7 @@ public class Constants {
|
|||
public static final String CASCADE_DELETE = "delete";
|
||||
public static final int MAX_RESOURCE_NAME_LENGTH = 100;
|
||||
public static final String CACHE_CONTROL_PRIVATE = "private";
|
||||
public static final int STATUS_HTTP_412_PAYLOAD_TOO_LARGE = 413;
|
||||
|
||||
static {
|
||||
CHARSET_UTF8 = Charset.forName(CHARSET_NAME_UTF8);
|
||||
|
|
|
@ -49,6 +49,7 @@ public abstract class BaseServerResponseException extends RuntimeException {
|
|||
private static final long serialVersionUID = 1L;
|
||||
|
||||
static {
|
||||
registerExceptionType(PayloadTooLargeException.STATUS_CODE, PayloadTooLargeException.class);
|
||||
registerExceptionType(AuthenticationException.STATUS_CODE, AuthenticationException.class);
|
||||
registerExceptionType(InternalErrorException.STATUS_CODE, InternalErrorException.class);
|
||||
registerExceptionType(InvalidRequestException.STATUS_CODE, InvalidRequestException.class);
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package ca.uhn.fhir.rest.server.exceptions;
|
||||
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2019 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
/**
|
||||
* This Represents an <b>HTTP 413 Payload Too Large</b> response, which means the request body
|
||||
* was too big for the server to accept
|
||||
*
|
||||
* <p>
|
||||
* Note that a complete list of RESTful exceptions is available in the <a href="./package-summary.html">Package
|
||||
* Summary</a>.
|
||||
* </p>
|
||||
*/
|
||||
@CoverageIgnore
|
||||
public class PayloadTooLargeException extends BaseServerResponseException {
|
||||
|
||||
public static final int STATUS_CODE = Constants.STATUS_HTTP_412_PAYLOAD_TOO_LARGE;
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public PayloadTooLargeException(String theMessage) {
|
||||
this(theMessage, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param theMessage
|
||||
* The message
|
||||
* @param theOperationOutcome
|
||||
* The OperationOutcome resource to return to the client
|
||||
*/
|
||||
public PayloadTooLargeException(String theMessage, IBaseOperationOutcome theOperationOutcome) {
|
||||
super(STATUS_CODE, theMessage, theOperationOutcome);
|
||||
}
|
||||
|
||||
}
|
|
@ -20,11 +20,13 @@ package ca.uhn.fhir.jpa.binstore;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PayloadTooLargeException;
|
||||
import com.google.common.hash.HashFunction;
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.common.hash.HashingInputStream;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.io.CountingInputStream;
|
||||
import org.apache.commons.io.input.CountingInputStream;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
|
@ -48,6 +50,12 @@ abstract class BaseBinaryStorageSvcImpl implements IBinaryStorageSvc {
|
|||
return myMaximumBinarySize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaximumBinarySize(int theMaximumBinarySize) {
|
||||
Validate.inclusiveBetween(1, Integer.MAX_VALUE, theMaximumBinarySize);
|
||||
myMaximumBinarySize = theMaximumBinarySize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinimumBinarySize() {
|
||||
return myMinimumBinarySize;
|
||||
|
@ -58,12 +66,6 @@ abstract class BaseBinaryStorageSvcImpl implements IBinaryStorageSvc {
|
|||
myMinimumBinarySize = theMinimumBinarySize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaximumBinarySize(int theMaximumBinarySize) {
|
||||
Validate.inclusiveBetween(1, Integer.MAX_VALUE, theMaximumBinarySize);
|
||||
myMaximumBinarySize = theMaximumBinarySize;
|
||||
}
|
||||
|
||||
String newRandomId() {
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (int i = 0; i < ID_LENGTH; i++) {
|
||||
|
@ -85,11 +87,19 @@ abstract class BaseBinaryStorageSvcImpl implements IBinaryStorageSvc {
|
|||
return new HashingInputStream(hash, theInputStream);
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
@Nonnull
|
||||
CountingInputStream createCountingInputStream(InputStream theInputStream) {
|
||||
InputStream stream = ByteStreams.limit(theInputStream, myMaximumBinarySize);
|
||||
return new CountingInputStream(stream);
|
||||
InputStream is = ByteStreams.limit(theInputStream, myMaximumBinarySize + 1L);
|
||||
return new CountingInputStream(is) {
|
||||
@Override
|
||||
public int getCount() {
|
||||
int retVal = super.getCount();
|
||||
if (retVal > myMaximumBinarySize) {
|
||||
throw new PayloadTooLargeException("Binary size exceeds maximum: " + myMaximumBinarySize);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -41,6 +41,8 @@ import org.apache.commons.io.IOUtils;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
@ -59,6 +61,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||
*/
|
||||
public class BinaryAccessProvider {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(BinaryAccessProvider.class);
|
||||
@Autowired
|
||||
private FhirContext myCtx;
|
||||
@Autowired
|
||||
|
@ -169,6 +172,8 @@ public class BinaryAccessProvider {
|
|||
}
|
||||
|
||||
long size = theServletRequest.getContentLength();
|
||||
ourLog.trace("Request specified content length: {}", size);
|
||||
|
||||
String blobId = null;
|
||||
|
||||
if (size > 0) {
|
||||
|
|
|
@ -23,8 +23,8 @@ package ca.uhn.fhir.jpa.binstore;
|
|||
import ca.uhn.fhir.jpa.dao.data.IBinaryStorageEntityDao;
|
||||
import ca.uhn.fhir.jpa.model.entity.BinaryStorageEntity;
|
||||
import com.google.common.hash.HashingInputStream;
|
||||
import com.google.common.io.CountingInputStream;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.input.CountingInputStream;
|
||||
import org.hibernate.LobHelper;
|
||||
import org.hibernate.Session;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
@ -57,7 +57,7 @@ public class DatabaseBlobBinaryStorageSvcImpl extends BaseBinaryStorageSvcImpl {
|
|||
|
||||
@Override
|
||||
@Transactional(Transactional.TxType.SUPPORTS)
|
||||
public StoredDetails storeBlob(IIdType theResourceId, String theContentType, InputStream theInputStream) throws IOException {
|
||||
public StoredDetails storeBlob(IIdType theResourceId, String theContentType, InputStream theInputStream) {
|
||||
Date publishedDate = new Date();
|
||||
|
||||
HashingInputStream hashingInputStream = createHashingInputStream(theInputStream);
|
||||
|
@ -103,7 +103,7 @@ public class DatabaseBlobBinaryStorageSvcImpl extends BaseBinaryStorageSvcImpl {
|
|||
}
|
||||
|
||||
@Override
|
||||
public StoredDetails fetchBlobDetails(IIdType theResourceId, String theBlobId) throws IOException {
|
||||
public StoredDetails fetchBlobDetails(IIdType theResourceId, String theBlobId) {
|
||||
|
||||
Optional<BinaryStorageEntity> entityOpt = myBinaryStorageEntityDao.findByIdAndResourceId(theBlobId, theResourceId.toUnqualifiedVersionless().getValue());
|
||||
if (entityOpt.isPresent() == false) {
|
||||
|
|
|
@ -26,9 +26,9 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.hash.HashingInputStream;
|
||||
import com.google.common.io.CountingInputStream;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.input.CountingInputStream;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.slf4j.Logger;
|
||||
|
|
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.binstore;
|
|||
|
||||
import com.google.common.hash.HashingInputStream;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.input.CountingInputStream;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -52,12 +53,13 @@ public class MemoryBinaryStorageSvcImpl extends BaseBinaryStorageSvcImpl impleme
|
|||
String id = newRandomId();
|
||||
String key = toKey(theResourceId, id);
|
||||
|
||||
HashingInputStream is = createHashingInputStream(theInputStream);
|
||||
HashingInputStream hashingIs = createHashingInputStream(theInputStream);
|
||||
CountingInputStream countingIs = createCountingInputStream(hashingIs);
|
||||
|
||||
byte[] bytes = IOUtils.toByteArray(is);
|
||||
byte[] bytes = IOUtils.toByteArray(countingIs);
|
||||
theInputStream.close();
|
||||
myDataMap.put(key, bytes);
|
||||
StoredDetails storedDetails = new StoredDetails(id, bytes.length, theContentType, is, new Date());
|
||||
StoredDetails storedDetails = new StoredDetails(id, countingIs.getCount(), theContentType, hashingIs, new Date());
|
||||
myDetailsMap.put(key, storedDetails);
|
||||
return storedDetails;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package ca.uhn.fhir.jpa.binstore;
|
||||
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PayloadTooLargeException;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
|
@ -78,4 +81,22 @@ public class FilesystemBinaryStorageSvcImplTest {
|
|||
assertEquals(0, capture.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRejectOversized() throws IOException {
|
||||
mySvc.setMinimumBinarySize(0);
|
||||
mySvc.setMaximumBinarySize(5);
|
||||
|
||||
IIdType id = new IdType("Patient/123");
|
||||
String contentType = "image/png";
|
||||
try {
|
||||
mySvc.storeBlob(id, contentType, new ByteArrayInputStream(SOME_BYTES));
|
||||
fail();
|
||||
} catch (PayloadTooLargeException e) {
|
||||
assertEquals("Binary size exceeds maximum: 5", e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue