use of com.linkedin.r2.filter.compression.EncodingType in project rest.li by linkedin.
the class TestRestCompressionEcho method compressionEchoData.
@DataProvider
public Object[][] compressionEchoData() {
EncodingType[] encodings = new EncodingType[] { EncodingType.GZIP, EncodingType.SNAPPY, EncodingType.IDENTITY };
Object[][] args = new Object[4 * encodings.length * encodings.length][2];
int cur = 0;
for (EncodingType requestEncoding : encodings) {
for (EncodingType acceptEncoding : encodings) {
RestFilter clientCompressionFilter = new ClientCompressionFilter(requestEncoding, new CompressionConfig(THRESHOLD), new EncodingType[] { acceptEncoding }, new CompressionConfig(THRESHOLD), Arrays.asList(new String[] { "*" }));
TransportClientFactory factory = new HttpClientFactory.Builder().setFilterChain(FilterChains.createRestChain(clientCompressionFilter)).build();
Client http1Client = new TransportClientAdapter(factory.getClient(getHttp1ClientProperties()), REST_OVER_STREAM);
Client http2Client = new TransportClientAdapter(factory.getClient(getHttp2ClientProperties()), REST_OVER_STREAM);
args[cur][0] = http1Client;
args[cur][1] = LARGE_BYTES_NUM;
args[cur + 1][0] = http2Client;
args[cur + 1][1] = LARGE_BYTES_NUM;
cur += 2;
_clientFactories.add(factory);
_clients.add(http1Client);
_clients.add(http2Client);
}
}
// test data that won't trigger compression
for (EncodingType requestEncoding : encodings) {
for (EncodingType acceptEncoding : encodings) {
RestFilter clientCompressionFilter = new ClientCompressionFilter(requestEncoding, new CompressionConfig(THRESHOLD), new EncodingType[] { acceptEncoding }, new CompressionConfig(THRESHOLD), Arrays.asList(new String[] { "*" }));
TransportClientFactory factory = new HttpClientFactory.Builder().setFilterChain(FilterChains.createRestChain(clientCompressionFilter)).build();
Client http1Client = new TransportClientAdapter(factory.getClient(getHttp1ClientProperties()), REST_OVER_STREAM);
Client http2Client = new TransportClientAdapter(factory.getClient(getHttp2ClientProperties()), REST_OVER_STREAM);
args[cur][0] = http1Client;
args[cur][1] = SMALL_BYTES_NUM;
args[cur + 1][0] = http2Client;
args[cur + 1][1] = SMALL_BYTES_NUM;
cur += 2;
_clientFactories.add(factory);
_clients.add(http1Client);
_clients.add(http2Client);
}
}
return args;
}
use of com.linkedin.r2.filter.compression.EncodingType in project rest.li by linkedin.
the class TestServerCompressionFilter method testResponseCompressionRules.
// Test response compression rules where the server has a default threshold of Integer.MAX_VALUE.
@Test(dataProvider = "headersData")
public void testResponseCompressionRules(String acceptEncoding, int compressionThreshold, EncodingType expectedContentEncoding) throws CompressionException, URISyntaxException {
ServerCompressionFilter serverCompressionFilter = new ServerCompressionFilter(ACCEPT_COMPRESSIONS);
RequestContext context = new RequestContext();
context.putLocalAttr(HttpConstants.ACCEPT_ENCODING, acceptEncoding);
context.putLocalAttr(HttpConstants.HEADER_RESPONSE_COMPRESSION_THRESHOLD, compressionThreshold);
int originalLength = 100;
byte[] entity = new byte[originalLength];
Arrays.fill(entity, (byte) 'A');
int compressedLength = (expectedContentEncoding == null) ? originalLength : expectedContentEncoding.getCompressor().deflate(new ByteArrayInputStream(entity)).length;
String expectedContentEncodingName = (expectedContentEncoding == null) ? null : expectedContentEncoding.getHttpName();
RestResponse restResponse = new RestResponseBuilder().setEntity(entity).build();
serverCompressionFilter.onRestResponse(restResponse, context, Collections.<String, String>emptyMap(), new HeaderCaptureFilter(HttpConstants.CONTENT_ENCODING, expectedContentEncodingName, compressedLength));
}
use of com.linkedin.r2.filter.compression.EncodingType in project rest.li by linkedin.
the class HttpClientFactory method getClient.
/**
* Create a new {@link TransportClient} with the specified properties,
* {@link SSLContext} and {@link SSLParameters}
*
* @param properties map of properties for the {@link TransportClient}
* @param sslContext {@link SSLContext} to be used for requests over SSL/TLS.
* @param sslParameters {@link SSLParameters} to configure secure connections.
* @return an appropriate {@link TransportClient} instance, as specified by the properties.
*/
private TransportClient getClient(Map<String, ? extends Object> properties, SSLContext sslContext, SSLParameters sslParameters) {
LOG.info("Getting a client with configuration {} and SSLContext {}", properties, sslContext);
TransportClient client = getRawClient(properties, sslContext, sslParameters);
List<String> httpRequestServerSupportedEncodings = ConfigValueExtractor.buildList(properties.remove(HTTP_REQUEST_CONTENT_ENCODINGS), LIST_SEPARATOR);
// In the old model, responses were compressed according to the type of method, so clients would send
// the Accept-Encoding header if the method was in HTTP_RESPONSE_COMPRESSION_OPERATIONS.
// In the new model, responses are compressed according to its size, so clients send the Accept-Encoding header
// if the server enabled response compression by setting HTTP_USE_RESPONSE_COMPRESSION to true.
// Until all servers migrate to the new model, clients will understand both models,
// and send the Accept-Encoding header if either the old or the new criterion is satisfied.
List<String> httpResponseCompressionOperations = ConfigValueExtractor.buildList(properties.remove(HTTP_RESPONSE_COMPRESSION_OPERATIONS), LIST_SEPARATOR);
String useResponseCompressionProperty = (String) properties.get(HTTP_USE_RESPONSE_COMPRESSION);
if (useResponseCompressionProperty != null && Boolean.parseBoolean(useResponseCompressionProperty)) {
httpResponseCompressionOperations.add(ClientCompressionHelper.COMPRESS_ALL_RESPONSES_INDICATOR);
}
FilterChain filters = _filters;
if (_useClientCompression) {
List<String> responseEncodings = null;
if (properties.containsKey(HTTP_RESPONSE_CONTENT_ENCODINGS)) {
responseEncodings = ConfigValueExtractor.buildList(properties.remove(HTTP_RESPONSE_CONTENT_ENCODINGS), LIST_SEPARATOR);
}
String httpServiceName = (String) properties.get(HTTP_SERVICE_NAME);
EncodingType restRequestContentEncoding = getRestRequestContentEncoding(httpRequestServerSupportedEncodings);
StreamEncodingType streamRequestContentEncoding = getStreamRequestContentEncoding(httpRequestServerSupportedEncodings);
if (restRequestContentEncoding != EncodingType.IDENTITY || !httpResponseCompressionOperations.isEmpty()) {
filters = filters.addLastRest(new ClientCompressionFilter(restRequestContentEncoding, getRestRequestCompressionConfig(httpServiceName, restRequestContentEncoding), buildRestAcceptEncodingSchemaNames(responseEncodings), _responseCompressionConfigs.get(httpServiceName), httpResponseCompressionOperations));
}
if (streamRequestContentEncoding != StreamEncodingType.IDENTITY || !httpResponseCompressionOperations.isEmpty()) {
CompressionConfig compressionConfig = getStreamRequestCompressionConfig(httpServiceName, streamRequestContentEncoding);
filters = filters.addLast(new ClientStreamCompressionFilter(streamRequestContentEncoding, compressionConfig, buildStreamAcceptEncodingSchemas(responseEncodings), _responseCompressionConfigs.get(httpServiceName), httpResponseCompressionOperations, _compressionExecutor));
}
}
Integer queryPostThreshold = chooseNewOverDefault(getIntValue(properties, HTTP_QUERY_POST_THRESHOLD), Integer.MAX_VALUE);
ClientQueryTunnelFilter clientQueryTunnelFilter = new ClientQueryTunnelFilter(queryPostThreshold);
filters = filters.addLastRest(clientQueryTunnelFilter);
filters = filters.addLast(clientQueryTunnelFilter);
// Add the disruptor filter to the end of the filter chain to get the most accurate simulation of disrupt
Integer requestTimeout = chooseNewOverDefault(getIntValue(properties, HTTP_REQUEST_TIMEOUT), DEFAULT_REQUEST_TIMEOUT);
DisruptFilter disruptFilter = new DisruptFilter(_executor, _eventLoopGroup, requestTimeout);
filters = filters.addLastRest(disruptFilter);
filters = filters.addLast(disruptFilter);
client = new FilterChainClient(client, filters);
client = new FactoryClient(client);
synchronized (_mutex) {
if (!_running) {
throw new IllegalStateException("Factory is shutting down");
}
_clientsOutstanding++;
return client;
}
}
use of com.linkedin.r2.filter.compression.EncodingType in project rest.li by linkedin.
the class ServerCompressionFilter method onRestRequest.
/**
* Handles compression tasks for incoming requests
*/
@Override
public void onRestRequest(RestRequest req, RequestContext requestContext, Map<String, String> wireAttrs, NextFilter<RestRequest, RestResponse> nextFilter) {
try {
//Check if the request is compressed, if so, decompress
String requestContentEncoding = req.getHeader(HttpConstants.CONTENT_ENCODING);
if (requestContentEncoding != null) {
//This must be a specific compression type other than *
EncodingType encoding;
try {
encoding = EncodingType.get(requestContentEncoding.trim().toLowerCase());
} catch (IllegalArgumentException ex) {
throw new CompressionException(CompressionConstants.UNSUPPORTED_ENCODING + requestContentEncoding);
}
if (encoding == EncodingType.ANY) {
throw new CompressionException(CompressionConstants.REQUEST_ANY_ERROR + requestContentEncoding);
}
//Process the correct compression types only
if (encoding.hasCompressor()) {
byte[] decompressedContent = encoding.getCompressor().inflate(req.getEntity().asInputStream());
Map<String, String> headers = new HashMap<String, String>(req.getHeaders());
headers.remove(HttpConstants.CONTENT_ENCODING);
headers.put(HttpConstants.CONTENT_LENGTH, Integer.toString(decompressedContent.length));
req = req.builder().setEntity(decompressedContent).setHeaders(headers).build();
}
}
//Get client support for compression and flag compress if need be
String responseAcceptedEncodings = req.getHeader(HttpConstants.ACCEPT_ENCODING);
if (responseAcceptedEncodings == null) {
//Only permit identity
responseAcceptedEncodings = EMPTY;
}
requestContext.putLocalAttr(HttpConstants.ACCEPT_ENCODING, responseAcceptedEncodings);
if (!responseAcceptedEncodings.isEmpty()) {
requestContext.putLocalAttr(HttpConstants.HEADER_RESPONSE_COMPRESSION_THRESHOLD, _serverCompressionHelper.getResponseCompressionThreshold(req));
}
nextFilter.onRequest(req, requestContext, wireAttrs);
} catch (CompressionException e) {
//If we can't decompress the client's request, we can't do much more with it
LOG.error(e.getMessage(), e.getCause());
RestResponse restResponse = new RestResponseBuilder().setStatus(HttpConstants.UNSUPPORTED_MEDIA_TYPE).build();
nextFilter.onError(new RestException(restResponse, e), requestContext, wireAttrs);
}
}
use of com.linkedin.r2.filter.compression.EncodingType in project rest.li by linkedin.
the class ServerCompressionFilter method onRestResponse.
/**
* Optionally compresses outgoing response
* */
@Override
public void onRestResponse(RestResponse res, RequestContext requestContext, Map<String, String> wireAttrs, NextFilter<RestRequest, RestResponse> nextFilter) {
try {
if (res.getEntity().length() > 0) {
String responseAcceptedEncodings = (String) requestContext.getLocalAttr(HttpConstants.ACCEPT_ENCODING);
if (responseAcceptedEncodings == null) {
throw new CompressionException(HttpConstants.ACCEPT_ENCODING + " not in local attribute.");
}
List<AcceptEncoding> parsedEncodings = AcceptEncoding.parseAcceptEncodingHeader(responseAcceptedEncodings, _supportedEncoding);
EncodingType selectedEncoding = AcceptEncoding.chooseBest(parsedEncodings);
//Check if there exists an acceptable encoding
if (selectedEncoding != null) {
if (selectedEncoding.hasCompressor() && res.getEntity().length() > (Integer) requestContext.getLocalAttr(HttpConstants.HEADER_RESPONSE_COMPRESSION_THRESHOLD)) {
Compressor compressor = selectedEncoding.getCompressor();
byte[] compressed = compressor.deflate(res.getEntity().asInputStream());
if (compressed.length < res.getEntity().length()) {
RestResponseBuilder resCompress = res.builder();
resCompress.addHeaderValue(HttpConstants.CONTENT_ENCODING, compressor.getContentEncodingName());
resCompress.setEntity(compressed);
res = resCompress.build();
}
}
} else {
//Not acceptable encoding status
res = res.builder().setStatus(HttpConstants.NOT_ACCEPTABLE).setEntity(new byte[0]).build();
}
}
} catch (CompressionException e) {
LOG.error(e.getMessage(), e.getCause());
}
nextFilter.onResponse(res, requestContext, wireAttrs);
}
Aggregations