/*
* Copyright (C) 2014 Civilian Framework.
*
* Licensed under the Civilian License (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.civilian-framework.org/license.txt
*
* 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.
*/
package org.civilian.processor;
import java.io.IOException;
import java.io.OutputStream;
import org.civilian.Processor;
import org.civilian.Request;
import org.civilian.Response;
import org.civilian.content.CompressionScheme;
import org.civilian.internal.Logs;
import org.civilian.response.ResponseHeaders;
import org.civilian.response.ResponseStreamInterceptor;
/**
* Compressor is a processor which compresses response content.
* Put it into the processor chain before any processors which will write response content.
* Compressor uses the "Accept-Encoding" header to determine the compression scheme.
* It can use all schemes which are available via the {@link CompressionScheme} class.
*/
public class Compressor extends Processor
{
/**
* An constant to signal that no compression should be applied.
* If a Compression object was installed as interceptor on a Response,
* it will automatically wrap the response OutputStream into a compressing stream.
* To abandon this behavior set this attribute on the request (with some non-null value).
*/
public static final String NO_COMPRESSION = "compressor-none";
@Override public boolean process(Request request, ProcessorChain chain) throws Exception
{
String accepted = request.getHeaders().get("Accept-Encoding");
if (accepted != null)
addInterceptor(request, accepted);
return chain.next(request);
}
protected void addInterceptor(Request request, String accepted)
{
CompressionScheme scheme = CompressionScheme.match(accepted);
if (scheme != null)
{
if (!scheme.isIdentity())
{
Response response = request.getResponse();
response.getHeaders().add("Vary", "Accept-Encoding");
response.addInterceptor(new Interceptor(scheme));
}
}
else if (Logs.PROCESSOR.isWarnEnabled())
{
Logs.PROCESSOR.warn("unhandled response compression schemes " + accepted);
}
}
private static class Interceptor implements ResponseStreamInterceptor
{
public Interceptor(CompressionScheme scheme)
{
scheme_ = scheme;
}
@Override public ResponseStreamInterceptor prepareStreamIntercept(Response response)
{
if (response.getRequest().getAttribute(NO_COMPRESSION) != null)
return null;
ResponseHeaders headers = response.getHeaders();
// do not apply compression if some other content-encoding was applied
String encoding = headers.get("Content-Encoding");
if (encoding != null)
return null;
// clear content length
response.setContentLength(-1);
// set content-encoding
headers.set("Content-Encoding", scheme_.getName());
// enhance etag if set
String etag = headers.get("Etag");
if (etag != null)
headers.set("Etag", etag + '-' + scheme_.getName());
return this;
}
@Override public OutputStream intercept(OutputStream out) throws IOException
{
return scheme_.wrap(out);
}
private CompressionScheme scheme_;
}
}