Search in sources :

Example 1 with EncodingType

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;
}
Also used : RestFilter(com.linkedin.r2.filter.message.rest.RestFilter) RestRequestBuilder(com.linkedin.r2.message.rest.RestRequestBuilder) TransportDispatcherBuilder(com.linkedin.r2.transport.common.bridge.server.TransportDispatcherBuilder) RestResponseBuilder(com.linkedin.r2.message.rest.RestResponseBuilder) EncodingType(com.linkedin.r2.filter.compression.EncodingType) TransportClientAdapter(com.linkedin.r2.transport.common.bridge.client.TransportClientAdapter) Client(com.linkedin.r2.transport.common.Client) TransportClientFactory(com.linkedin.r2.transport.common.TransportClientFactory) ClientCompressionFilter(com.linkedin.r2.filter.compression.ClientCompressionFilter) CompressionConfig(com.linkedin.r2.filter.CompressionConfig) DataProvider(org.testng.annotations.DataProvider)

Example 2 with EncodingType

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));
}
Also used : ByteArrayInputStream(java.io.ByteArrayInputStream) RestResponse(com.linkedin.r2.message.rest.RestResponse) RestResponseBuilder(com.linkedin.r2.message.rest.RestResponseBuilder) RequestContext(com.linkedin.r2.message.RequestContext) Test(org.testng.annotations.Test)

Example 3 with EncodingType

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;
    }
}
Also used : TransportClient(com.linkedin.r2.transport.common.bridge.client.TransportClient) FilterChain(com.linkedin.r2.filter.FilterChain) StreamEncodingType(com.linkedin.r2.filter.compression.streaming.StreamEncodingType) EncodingType(com.linkedin.r2.filter.compression.EncodingType) DisruptFilter(com.linkedin.r2.disruptor.DisruptFilter) StreamEncodingType(com.linkedin.r2.filter.compression.streaming.StreamEncodingType) ClientStreamCompressionFilter(com.linkedin.r2.filter.compression.ClientStreamCompressionFilter) FilterChainClient(com.linkedin.r2.filter.transport.FilterChainClient) ClientQueryTunnelFilter(com.linkedin.r2.filter.transport.ClientQueryTunnelFilter) ClientCompressionFilter(com.linkedin.r2.filter.compression.ClientCompressionFilter) CompressionConfig(com.linkedin.r2.filter.CompressionConfig)

Example 4 with EncodingType

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);
    }
}
Also used : HashMap(java.util.HashMap) RestResponse(com.linkedin.r2.message.rest.RestResponse) RestResponseBuilder(com.linkedin.r2.message.rest.RestResponseBuilder) RestException(com.linkedin.r2.message.rest.RestException)

Example 5 with EncodingType

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);
}
Also used : RestResponseBuilder(com.linkedin.r2.message.rest.RestResponseBuilder)

Aggregations

RestResponseBuilder (com.linkedin.r2.message.rest.RestResponseBuilder)4 CompressionConfig (com.linkedin.r2.filter.CompressionConfig)2 ClientCompressionFilter (com.linkedin.r2.filter.compression.ClientCompressionFilter)2 EncodingType (com.linkedin.r2.filter.compression.EncodingType)2 RestResponse (com.linkedin.r2.message.rest.RestResponse)2 DisruptFilter (com.linkedin.r2.disruptor.DisruptFilter)1 FilterChain (com.linkedin.r2.filter.FilterChain)1 ClientStreamCompressionFilter (com.linkedin.r2.filter.compression.ClientStreamCompressionFilter)1 StreamEncodingType (com.linkedin.r2.filter.compression.streaming.StreamEncodingType)1 RestFilter (com.linkedin.r2.filter.message.rest.RestFilter)1 ClientQueryTunnelFilter (com.linkedin.r2.filter.transport.ClientQueryTunnelFilter)1 FilterChainClient (com.linkedin.r2.filter.transport.FilterChainClient)1 RequestContext (com.linkedin.r2.message.RequestContext)1 RestException (com.linkedin.r2.message.rest.RestException)1 RestRequestBuilder (com.linkedin.r2.message.rest.RestRequestBuilder)1 Client (com.linkedin.r2.transport.common.Client)1 TransportClientFactory (com.linkedin.r2.transport.common.TransportClientFactory)1 TransportClient (com.linkedin.r2.transport.common.bridge.client.TransportClient)1 TransportClientAdapter (com.linkedin.r2.transport.common.bridge.client.TransportClientAdapter)1 TransportDispatcherBuilder (com.linkedin.r2.transport.common.bridge.server.TransportDispatcherBuilder)1