Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.cxf.transport.http.gzip;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPOutputStream;
import org.apache.cxf.common.i18n.BundleUtils;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.helpers.HttpHeaderHelper;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.interceptor.MessageSenderInterceptor;
import org.apache.cxf.io.AbstractThresholdOutputStream;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
/**
* CXF interceptor that compresses outgoing messages using gzip and sets the
* HTTP Content-Encoding header appropriately. An instance of this class should
* be added as an out interceptor on clients that need to talk to a service that
* accepts gzip-encoded requests or on a service that wants to be able to return
* compressed responses. In server mode, the interceptor only compresses
* responses if the client indicated (via an Accept-Encoding header on the
* request) that it can understand them. To handle gzip-encoded input messages,
* see {@link GZIPInInterceptor}. This interceptor supports a compression
* {@link #threshold} (default 1kB) - messages smaller than this threshold will
* not be compressed. To force compression of all messages, set the threshold to
* 0. This class was originally based on one of the CXF samples
* (configuration_interceptor).
*
* @author Ian Roberts ([email protected])
*/
public class GZIPOutInterceptor extends AbstractPhaseInterceptor {
/**
* Enum giving the possible values for whether we should gzip a particular
* message.
*/
public static enum UseGzip {
NO, YES, FORCE
}
/**
* Key under which we store the original output stream on the message, for
* use by the ending interceptor.
*/
public static final String ORIGINAL_OUTPUT_STREAM_KEY = GZIPOutInterceptor.class.getName()
+ ".originalOutputStream";
/**
* Key under which we store an indication of whether compression is
* permitted or required, for use by the ending interceptor.
*/
public static final String USE_GZIP_KEY = GZIPOutInterceptor.class.getName() + ".useGzip";
/**
* Key under which we store the name which should be used for the
* content-encoding of the outgoing message. Typically "gzip" but may be
* "x-gzip" if we are processing a response message and this is the name
* given by the client in Accept-Encoding.
*/
public static final String GZIP_ENCODING_KEY = GZIPOutInterceptor.class.getName() + ".gzipEncoding";
private static final ResourceBundle BUNDLE = BundleUtils.getBundle(GZIPOutInterceptor.class);
private static final Logger LOG = LogUtils.getL7dLogger(GZIPOutInterceptor.class);
/**
* Compression threshold in bytes - messages smaller than this will not be
* compressed.
*/
private int threshold = 1024;
public GZIPOutInterceptor() {
super(Phase.PREPARE_SEND);
addAfter(MessageSenderInterceptor.class.getName());
}
public void setThreshold(int threshold) {
this.threshold = threshold;
}
public int getThreshold() {
return threshold;
}
public void handleMessage(Message message) throws Fault {
UseGzip use = gzipPermitted(message);
if (use != UseGzip.NO) {
// remember the original output stream, we will write compressed
// data to this later
OutputStream os = message.getContent(OutputStream.class);
message.put(ORIGINAL_OUTPUT_STREAM_KEY, os);
message.put(USE_GZIP_KEY, use);
// new stream to cache the message
GZipThresholdOutputStream cs
= new GZipThresholdOutputStream(threshold,
os,
use == UseGzip.FORCE,
message);
message.setContent(OutputStream.class, cs);
}
}
/**
* Checks whether we can, cannot or must use gzip compression on this output
* message. Gzip is always permitted if the message is a client request. If
* the message is a server response we check the Accept-Encoding header of
* the corresponding request message - with no Accept-Encoding we assume
* that gzip is not permitted. For the full gory details, see section
* 14.3 of RFC 2616 (HTTP 1.1).
*
* @param message the outgoing message.
* @return whether to attempt gzip compression for this message.
* @throws Fault if the Accept-Encoding header does not allow any encoding
* that we can support (identity, gzip or x-gzip).
*/
private UseGzip gzipPermitted(Message message) throws Fault {
UseGzip permitted = UseGzip.NO;
if (Boolean.TRUE.equals(message.get(Message.REQUESTOR_ROLE))) {
LOG.fine("Requestor role, so gzip enabled");
permitted = UseGzip.YES;
message.put(GZIP_ENCODING_KEY, "gzip");
addHeader(message, "Accept-Encoding", "gzip;q=1.0, identity; q=0.5, *;q=0");
} else {
LOG.fine("Response role, checking accept-encoding");
Exchange exchange = message.getExchange();
Message request = exchange.getInMessage();
Map> requestHeaders = CastUtils.cast((Map)request
.get(Message.PROTOCOL_HEADERS));
if (requestHeaders != null) {
List acceptEncodingHeader = CastUtils.cast(HttpHeaderHelper
.getHeader(requestHeaders, HttpHeaderHelper.ACCEPT_ENCODING));
if (acceptEncodingHeader != null) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("Accept-Encoding header: " + acceptEncodingHeader);
}
// Accept-Encoding is a comma separated list of entries, so
// we split it into its component parts and build two
// lists, one with all the "q=0" encodings and the other
// with the rest (no q, or q=).
List zeros = new ArrayList(3);
List nonZeros = new ArrayList(3);
// regular expression that matches any encoding with a
// q-value of 0 (or 0.0, 0.00, etc.).
Pattern zeroQ = Pattern.compile(";\\s*q=0(?:\\.0+)?$");
for (String headerLine : acceptEncodingHeader) {
String[] encodings = headerLine.trim().split("[,\\s]*,\\s*");
for (String enc : encodings) {
Matcher m = zeroQ.matcher(enc);
if (m.find()) {
zeros.add(enc.substring(0, m.start()));
} else if (enc.indexOf(';') >= 0) {
nonZeros.add(enc.substring(0, enc.indexOf(';')));
} else {
nonZeros.add(enc);
}
}
}
// identity encoding is permitted if (a) it is not
// specifically disabled by an identity;q=0 and (b) if
// there is a *;q=0 then there is also an explicit
// identity[;q=]
//
// [x-]gzip is permitted if (a) there is an explicit
// [x-]gzip[;q=], or (b) there is a
// *[;q=] and no [x-]gzip;q=0 to disable it.
boolean identityEnabled = !zeros.contains("identity")
&& (!zeros.contains("*") || nonZeros.contains("identity"));
boolean gzipEnabled = nonZeros.contains("gzip")
|| (nonZeros.contains("*") && !zeros.contains("gzip"));
boolean xGzipEnabled = nonZeros.contains("x-gzip")
|| (nonZeros.contains("*") && !zeros.contains("x-gzip"));
if (identityEnabled && !gzipEnabled && !xGzipEnabled) {
permitted = UseGzip.NO;
} else if (identityEnabled && gzipEnabled) {
permitted = UseGzip.YES;
message.put(GZIP_ENCODING_KEY, "gzip");
} else if (identityEnabled && xGzipEnabled) {
permitted = UseGzip.YES;
message.put(GZIP_ENCODING_KEY, "x-gzip");
} else if (!identityEnabled && gzipEnabled) {
permitted = UseGzip.FORCE;
message.put(GZIP_ENCODING_KEY, "gzip");
} else if (!identityEnabled && xGzipEnabled) {
permitted = UseGzip.FORCE;
message.put(GZIP_ENCODING_KEY, "x-gzip");
} else {
throw new Fault(new org.apache.cxf.common.i18n.Message("NO_SUPPORTED_ENCODING",
BUNDLE));
}
} else {
LOG.fine("No accept-encoding header");
}
}
}
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("gzip permitted: " + permitted);
}
return permitted;
}
static class GZipThresholdOutputStream extends AbstractThresholdOutputStream {
Message message;
public GZipThresholdOutputStream(int t, OutputStream orig,
boolean force, Message msg) {
super(t);
super.wrappedStream = orig;
message = msg;
if (force) {
setupGZip();
}
}
private void setupGZip() {
}
@Override
public void thresholdNotReached() {
//nothing
LOG.fine("Message is smaller than compression threshold, not compressing.");
}
@Override
public void thresholdReached() throws IOException {
LOG.fine("Compressing message.");
// Set the Content-Encoding HTTP header
String enc = (String)message.get(GZIP_ENCODING_KEY);
addHeader(message, "Content-Encoding", enc);
// if this is a response message, add the Vary header
if (!Boolean.TRUE.equals(message.get(Message.REQUESTOR_ROLE))) {
addHeader(message, "Vary", "Accept-Encoding");
}
// gzip the result
GZIPOutputStream zipOutput = new GZIPOutputStream(wrappedStream);
wrappedStream = zipOutput;
}
}
/**
* Adds a value to a header. If the given header name is not currently
* set in the message, an entry is created with the given single value.
* If the header is already set, the value is appended to the first
* element of the list, following a comma.
*
* @param message the message
* @param name the header to set
* @param value the value to add
*/
private static void addHeader(Message message, String name, String value) {
Map> protocolHeaders = CastUtils.cast((Map)message
.get(Message.PROTOCOL_HEADERS));
if (protocolHeaders == null) {
protocolHeaders = new HashMap>();
message.put(Message.PROTOCOL_HEADERS, protocolHeaders);
}
List header = CastUtils.cast((List)protocolHeaders.get(name));
if (header == null) {
header = new ArrayList();
protocolHeaders.put(name, header);
}
if (header.size() == 0) {
header.add(value);
} else {
header.set(0, header.get(0) + "," + value);
}
}
}