Skip to content

Commit

Permalink
ISPN-8229 Add Max Content Length configuration to the REST endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
tristantarrant authored and wburns committed Sep 26, 2017
1 parent 68a91f7 commit a9879df
Show file tree
Hide file tree
Showing 34 changed files with 225 additions and 105 deletions.
Expand Up @@ -113,7 +113,7 @@ protected void initChannel(SocketChannel ch) {
b.option(ChannelOption.SO_RCVBUF, pool.bufferSize());
b.option(ChannelOption.TCP_NODELAY, pool.tcpNoDelay());
bootstrap = b;
maxContentLength = 10 * 1024 * 1024; // TODO make this part of configuration options.
maxContentLength = configuration.maxContentLength();

this.key2StringMapper = Util.getInstance(configuration.key2StringMapper(), ctx.getCache().getAdvancedCache().getClassLoader());
this.key2StringMapper.setMarshaller(ctx.getMarshaller());
Expand Down
Expand Up @@ -70,4 +70,9 @@ public RestStoreConfigurationBuilder appendCacheNameToPath(boolean appendCacheNa
public RestStoreConfigurationBuilder rawValues(boolean rawValues) {
return builder.rawValues(rawValues);
}

@Override
public RestStoreConfigurationBuilder maxContentLength(int maxContentLength) {
return builder.maxContentLength(maxContentLength);
}
}
Expand Up @@ -25,7 +25,8 @@ public enum Attribute {
PORT("port"),
RAW_VALUES("raw-values"),
SOCKET_TIMEOUT("socket-timeout"),
TCP_NO_DELAY("tcp-no-delay"), ;
TCP_NO_DELAY("tcp-no-delay"),
MAX_CONTENT_LENGTH("max-content-length");

private final String name;

Expand Down
Expand Up @@ -12,12 +12,12 @@
*/
@BuiltBy(ConnectionPoolConfigurationBuilder.class)
public class ConnectionPoolConfiguration {
static final AttributeDefinition<Integer> CONNECTION_TIMEOUT = AttributeDefinition.builder("connectionTimeout", 60000).immutable().build();
static final AttributeDefinition<Integer> MAX_CONNECTIONS_PER_HOST = AttributeDefinition.builder("maxConnectionsPerHostTimeout", 4).immutable().build();
static final AttributeDefinition<Integer> MAX_TOTAL_CONNECTIONS = AttributeDefinition.builder("maxTotalConnections", 20).immutable().build();
static final AttributeDefinition<Integer> BUFFER_SIZE = AttributeDefinition.builder("bufferSize", 8192).immutable().build();
static final AttributeDefinition<Integer> SOCKET_TIMEOUT = AttributeDefinition.builder("socketTimeout", 60000).immutable().build();
static final AttributeDefinition<Boolean> TCP_NO_DELAY = AttributeDefinition.builder("tcpNoDelay", true).immutable().build();
public static final AttributeDefinition<Integer> CONNECTION_TIMEOUT = AttributeDefinition.builder("connectionTimeout", 60000).immutable().build();
public static final AttributeDefinition<Integer> MAX_CONNECTIONS_PER_HOST = AttributeDefinition.builder("maxConnectionsPerHostTimeout", 4).immutable().build();
public static final AttributeDefinition<Integer> MAX_TOTAL_CONNECTIONS = AttributeDefinition.builder("maxTotalConnections", 20).immutable().build();
public static final AttributeDefinition<Integer> BUFFER_SIZE = AttributeDefinition.builder("bufferSize", 8192).immutable().build();
public static final AttributeDefinition<Integer> SOCKET_TIMEOUT = AttributeDefinition.builder("socketTimeout", 60000).immutable().build();
public static final AttributeDefinition<Boolean> TCP_NO_DELAY = AttributeDefinition.builder("tcpNoDelay", true).immutable().build();

static AttributeSet attributeDefinitionSet() {
return new AttributeSet(ConnectionPoolConfiguration.class, CONNECTION_TIMEOUT, MAX_CONNECTIONS_PER_HOST,
Expand Down
Expand Up @@ -23,16 +23,17 @@
@ConfigurationFor(RestStore.class)
@SerializedWith(RestStoreConfigurationSerializer.class)
public class RestStoreConfiguration extends AbstractStoreConfiguration {
static final AttributeDefinition<String> KEY2STRING_MAPPER = AttributeDefinition.builder("key2StringMapper", WrappedByteArrayOrPrimitiveMapper.class.getName()).immutable().xmlName("key-to-string-mapper").build();
static final AttributeDefinition<String> METADATA_HELPER = AttributeDefinition.builder("metadataHelper", EmbeddedMetadataHelper.class.getName()).immutable().build();
static final AttributeDefinition<String> HOST = AttributeDefinition.builder("host", null, String.class).immutable().autoPersist(false).build();
static final AttributeDefinition<Integer> PORT = AttributeDefinition.builder("port", 80).immutable().autoPersist(false).build();
static final AttributeDefinition<String> PATH = AttributeDefinition.builder("path", "/").immutable().build();
static final AttributeDefinition<Boolean> APPEND_CACHE_NAME_TO_PATH = AttributeDefinition.builder("appendCacheNameToPath", false).immutable().build();
static final AttributeDefinition<Boolean> RAW_VALUES = AttributeDefinition.builder("rawValues", false).immutable().build();
public static final AttributeDefinition<String> KEY2STRING_MAPPER = AttributeDefinition.builder("key2StringMapper", WrappedByteArrayOrPrimitiveMapper.class.getName()).immutable().xmlName("key-to-string-mapper").build();
public static final AttributeDefinition<String> METADATA_HELPER = AttributeDefinition.builder("metadataHelper", EmbeddedMetadataHelper.class.getName()).immutable().build();
public static final AttributeDefinition<String> HOST = AttributeDefinition.builder("host", null, String.class).immutable().autoPersist(false).build();
public static final AttributeDefinition<Integer> PORT = AttributeDefinition.builder("port", 80).immutable().autoPersist(false).build();
public static final AttributeDefinition<String> PATH = AttributeDefinition.builder("path", "/").immutable().build();
public static final AttributeDefinition<Boolean> APPEND_CACHE_NAME_TO_PATH = AttributeDefinition.builder("appendCacheNameToPath", false).immutable().build();
public static final AttributeDefinition<Boolean> RAW_VALUES = AttributeDefinition.builder("rawValues", false).immutable().build();
public static final AttributeDefinition<Integer> MAX_CONTENT_LENGTH = AttributeDefinition.builder("maxContentLength", 10 * 1024 * 1024).immutable().build();

public static AttributeSet attributeDefinitionSet() {
return new AttributeSet(RestStoreConfiguration.class, AbstractStoreConfiguration.attributeDefinitionSet(), KEY2STRING_MAPPER, METADATA_HELPER, HOST, PORT, PATH, APPEND_CACHE_NAME_TO_PATH, RAW_VALUES);
return new AttributeSet(RestStoreConfiguration.class, AbstractStoreConfiguration.attributeDefinitionSet(), KEY2STRING_MAPPER, METADATA_HELPER, HOST, PORT, PATH, APPEND_CACHE_NAME_TO_PATH, RAW_VALUES, MAX_CONTENT_LENGTH);
}

private final Attribute<String> key2StringMapper;
Expand All @@ -42,6 +43,7 @@ public static AttributeSet attributeDefinitionSet() {
private final Attribute<String> path;
private final Attribute<Boolean> appendCacheNameToPath;
private final Attribute<Boolean> rawValues;
private final Attribute<Integer> maxContentLength;
private final ConnectionPoolConfiguration connectionPool;

public RestStoreConfiguration(AttributeSet attributes,
Expand All @@ -54,6 +56,7 @@ public RestStoreConfiguration(AttributeSet attributes,
path = attributes.attribute(PATH);
appendCacheNameToPath = attributes.attribute(APPEND_CACHE_NAME_TO_PATH);
rawValues = attributes.attribute(RAW_VALUES);
maxContentLength = attributes.attribute(MAX_CONTENT_LENGTH);
this.connectionPool = connectionPool;
}

Expand Down Expand Up @@ -89,6 +92,10 @@ public boolean rawValues() {
return rawValues.get();
}

public int maxContentLength() {
return maxContentLength.get();
}

@Override
public String toString() {
return "RestStoreConfiguration [connectionPool=" + connectionPool + ", attributes=" + attributes + "]";
Expand Down
Expand Up @@ -3,6 +3,7 @@
import static org.infinispan.persistence.rest.configuration.RestStoreConfiguration.APPEND_CACHE_NAME_TO_PATH;
import static org.infinispan.persistence.rest.configuration.RestStoreConfiguration.HOST;
import static org.infinispan.persistence.rest.configuration.RestStoreConfiguration.KEY2STRING_MAPPER;
import static org.infinispan.persistence.rest.configuration.RestStoreConfiguration.MAX_CONTENT_LENGTH;
import static org.infinispan.persistence.rest.configuration.RestStoreConfiguration.METADATA_HELPER;
import static org.infinispan.persistence.rest.configuration.RestStoreConfiguration.PATH;
import static org.infinispan.persistence.rest.configuration.RestStoreConfiguration.PORT;
Expand Down Expand Up @@ -52,7 +53,6 @@ public RestStoreConfigurationBuilder key2StringMapper(String key2StringMapper) {
return this;
}


@Override
public RestStoreConfigurationBuilder key2StringMapper(Class<? extends MarshallingTwoWayKey2StringMapper> klass) {
attributes.attribute(KEY2STRING_MAPPER).set(klass.getName());
Expand Down Expand Up @@ -95,6 +95,12 @@ public RestStoreConfigurationBuilder rawValues(boolean rawValues) {
return this;
}

@Override
public RestStoreConfigurationBuilder maxContentLength(int maxContentLength) {
attributes.attribute(MAX_CONTENT_LENGTH).set(maxContentLength);
return this;
}

@Override
public RestStoreConfiguration create() {
return new RestStoreConfiguration(attributes.protect(), async.create(),
Expand Down
Expand Up @@ -67,4 +67,9 @@ public interface RestStoreConfigurationChildBuilder<S> extends StoreConfiguratio
* Reads/writes "raw" values to the REST server instead of marshalling (used by the rolling upgrades feature)
*/
RestStoreConfigurationBuilder rawValues(boolean rawValues);

/**
* Sets the maximum content length. Defaults to 10M.
*/
RestStoreConfigurationBuilder maxContentLength(int maxContentLength);
}
Expand Up @@ -168,6 +168,10 @@ private void parseRestStoreAttributes(XMLExtendedStreamReader reader, RestStoreC
builder.rawValues(Boolean.parseBoolean(value));
break;
}
case MAX_CONTENT_LENGTH: {
builder.maxContentLength(Integer.parseInt(value));
break;
}
default: {
Parser.parseStoreAttribute(reader, i, builder);
break;
Expand Down
Expand Up @@ -42,6 +42,11 @@
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="max-content-length" type="xs:int" default="${RestStore.maxContentLength}">
<xs:annotation>
<xs:documentation>The maximum allowed content length of a POST/PUT request.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:extension>
</xs:complexContent>
</xs:complexType>
Expand Down
Expand Up @@ -33,6 +33,7 @@ public void testRemoteCacheStore() throws Exception {
assertEquals("localhost", store.host());
assertEquals("/rest/___defaultcache/", store.path());
assertEquals(18212, store.port());
assertEquals(15000000, store.maxContentLength());
ConnectionPoolConfiguration connectionPool = store.connectionPool();
assertEquals(10000, connectionPool.connectionTimeout());
assertEquals(10, connectionPool.maxConnectionsPerHost());
Expand Down
8 changes: 4 additions & 4 deletions persistence/rest/src/test/resources/rest-cl-config.xml
Expand Up @@ -3,18 +3,18 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:infinispan:config:9.2 http://www.infinispan.org/schemas/infinispan-config-9.2.xsd
urn:infinispan:config:store:rest:9.2 http://www.infinispan.org/schemas/infinispan-cachestore-rest-config-9.2.xsd"
xmlns="urn:infinispan:config:9.2"
xmlns:remote="urn:infinispan:config:store:rest:9.2" >
xmlns="urn:infinispan:config:9.2">

<cache-container default-cache="default">
<local-cache name="default">
<persistence passivation="false">
<rest-store xmlns="urn:infinispan:config:store:rest:9.2"
fetch-state="false" read-only="false" preload="false" purge="false" shared="true" raw-values="false"
path="/rest/___defaultcache" append-cache-name-to-path="false"
key-to-string-mapper="org.infinispan.persistence.keymappers.WrappedByteArrayOrPrimitiveMapper">
<connection-pool connection-timeout="10000" max-connections-per-host="10" max-total-connections="10" buffer-size="20000" socket-timeout="10000" tcp-no-delay="true" />
key-to-string-mapper="org.infinispan.persistence.keymappers.WrappedByteArrayOrPrimitiveMapper"
max-content-length="15000000">
<remote-server host="localhost" port="18212" outbound-socket-binding="rest-server-1" />
<connection-pool connection-timeout="10000" max-connections-per-host="10" max-total-connections="10" buffer-size="20000" socket-timeout="10000" tcp-no-delay="true" />
</rest-store>
</persistence>
</local-cache>
Expand Down
Expand Up @@ -51,6 +51,7 @@ public enum Attribute {
LAZY_RETRIEVAL(ModelKeys.LAZY_RETRIEVAL),
LOCK_TIMEOUT(ModelKeys.LOCK_TIMEOUT),
REPLICATION_TIMEOUT(ModelKeys.REPLICATION_TIMEOUT),
MAX_CONTENT_LENGTH(ModelKeys.MAX_CONTENT_LENGTH),
NAME(ModelKeys.NAME),
QOP(ModelKeys.QOP),
RECEIVE_BUFFER_SIZE(ModelKeys.RECEIVE_BUFFER_SIZE),
Expand Down
Expand Up @@ -295,6 +295,10 @@ private void parseRestConnector(XMLExtendedStreamReader reader, PathAddress subs
name = value;
break;
}
case MAX_CONTENT_LENGTH: {
RestConnectorResource.MAX_CONTENT_LENGTH.parseAndSetParameter(value, connector, reader);
break;
}
case SECURITY_DOMAIN: {
if (namespace.since(Namespace.INFINISPAN_ENDPOINT_9_0)) {
throw ParseUtils.unexpectedAttribute(reader, i);
Expand Down
Expand Up @@ -51,6 +51,7 @@ public class ModelKeys {
public static final String AUTH_METHOD = "auth-method"; // string
public static final String SECURITY_MODE = "security-mode"; // string
public static final String EXTENDED_HEADERS = "extended-headers"; //enum
public static final String MAX_CONTENT_LENGTH = "max-content-length"; //int

public static final String TOPOLOGY_STATE_TRANSFER_NAME = "TOPOLOGY_STATE_TRANSFER";
public static final String TOPOLOGY_STATE_TRANSFER = "topology-state-transfer";
Expand Down
Expand Up @@ -65,7 +65,15 @@ public class RestConnectorResource extends CommonConnectorResource {
.setRestartAllServices()
.build();

static final SimpleAttributeDefinition[] REST_ATTRIBUTES = { SOCKET_BINDING, CONTEXT_PATH, EXTENDED_HEADERS };
static final SimpleAttributeDefinition MAX_CONTENT_LENGTH =
new SimpleAttributeDefinitionBuilder(ModelKeys.MAX_CONTENT_LENGTH, ModelType.INT, true)
.setAllowExpression(true)
.setXmlName(ModelKeys.MAX_CONTENT_LENGTH)
.setRestartAllServices()
.setDefaultValue(new ModelNode().set(RestServerConfigurationBuilder.DEFAULT_MAX_CONTENT_LENGTH))
.build();

static final SimpleAttributeDefinition[] REST_ATTRIBUTES = { SOCKET_BINDING, CONTEXT_PATH, EXTENDED_HEADERS, MAX_CONTENT_LENGTH };

public RestConnectorResource(boolean isRuntimeRegistration) {
super(REST_CONNECTOR_PATH, EndpointExtension.getResourceDescriptionResolver(ModelKeys.REST_CONNECTOR), RestSubsystemAdd.INSTANCE, RestSubsystemRemove.INSTANCE, isRuntimeRegistration);
Expand Down
Expand Up @@ -66,20 +66,24 @@ public class RestService implements Service<RestServer>, EncryptableService {
private final Set<String> ignoredCaches;
private RestServer restServer;
private boolean clientAuth;
private final int maxContentLength;

public RestService(String serverName, RestAuthMethod authMethod, String contextPath, ExtendedHeaders extendedHeaders, Set<String> ignoredCaches) {
public RestService(String serverName, RestAuthMethod authMethod, String contextPath, ExtendedHeaders extendedHeaders, Set<String> ignoredCaches,
int maxContentLength) {
this.serverName = serverName;
this.authMethod = authMethod;
this.contextPath = contextPath;
this.extendedHeaders = extendedHeaders;
this.ignoredCaches = ignoredCaches;
this.maxContentLength = maxContentLength;
}

/** {@inheritDoc} */
@Override
public synchronized void start(StartContext startContext) throws StartException {
RestServerConfigurationBuilder builder = new RestServerConfigurationBuilder();
builder.name(serverName).extendedHeaders(extendedHeaders).ignoredCaches(ignoredCaches).contextPath(contextPath);
builder.name(serverName).extendedHeaders(extendedHeaders).ignoredCaches(ignoredCaches).contextPath(contextPath)
.maxContentLength(maxContentLength);

EncryptableServiceHelper.fillSecurityConfiguration(this, builder.ssl());

Expand Down
Expand Up @@ -64,8 +64,9 @@ protected void performRuntime(OperationContext context, ModelNode operation, Mod
ignoredCaches = config.get(ModelKeys.IGNORED_CACHES).asList()
.stream().map(ModelNode::asString).collect(Collectors.toSet());
}
int maxContentLength = RestConnectorResource.MAX_CONTENT_LENGTH.resolveModelAttribute(context, config).asInt();
// Create the service
final RestService service = new RestService(getServiceName(config), restAuthMethod, cleanContextPath(contextPath), extendedHeaders, ignoredCaches);
final RestService service = new RestService(getServiceName(config), restAuthMethod, cleanContextPath(contextPath), extendedHeaders, ignoredCaches, maxContentLength);

// Setup the various dependencies with injectors and install the service
ServiceBuilder<?> builder = context.getServiceTarget().addService(EndpointUtils.getServiceName(operation, "rest"), service);
Expand Down
Expand Up @@ -45,6 +45,7 @@ rest-connector.cache-container=The cache container to use
rest-connector.context-path=The context path on which the REST connector should be published
rest-connector.socket-binding=The socket binding to use for this connector
rest-connector.extended-headers=Allow retrieval of extended information about entries (NEVER, ON_DEMAND)
rest-connector.max-content-length=Maximum allowed content length
websocket-connector=A WebSocket connector
websocket-connector.add=Adds a WebSocket connector
websocket-connector.remove=Removes a WebSocket connector
Expand Down
Expand Up @@ -171,6 +171,11 @@
<xs:documentation>The list of ignored caches for this connector</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="max-content-length" type="xs:int" use="optional">
<xs:annotation>
<xs:documentation>Sets the maximum allowed content length.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>

<xs:complexType name="websocket-connector">
Expand Down
Expand Up @@ -71,7 +71,7 @@ public static Collection<Object[]> data() {
Object[][] data = new Object[][] {
{ "endpoint-7.2.xml", 16, "schema/jboss-infinispan-endpoint_7_2.xsd", null },
{ "endpoint-8.0.xml", 16, "schema/jboss-infinispan-endpoint_8_0.xsd", null },
{ "endpoint-9.0.xml", 38, "schema/jboss-infinispan-endpoint_9_0.xsd", new String[] { "/subsystem-templates/infinispan-endpoint.xml"} },
{ "endpoint-9.0.xml", 38, "schema/jboss-infinispan-endpoint_9_0.xsd", null },
{ "endpoint-9.2.xml", 38, "schema/jboss-infinispan-endpoint_9_2.xsd", new String[] { "/subsystem-templates/infinispan-endpoint.xml"} },
};
return Arrays.asList(data);
Expand Down

0 comments on commit a9879df

Please sign in to comment.