All Downloads are FREE. Search and download functionalities are using the official Maven repository.

uk.ac.starlink.util.ContentCoding Maven / Gradle / Ivy

package uk.ac.starlink.util;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.logging.Level;
import java.util.logging.Logger;
import uk.ac.starlink.util.CountInputStream;

/**
 * Defines a policy for Content Codings used in HTTP connections.
 * An instance of this class is used to prepare requests and decode responses
 * for some HTTP-based communications in this package.
 * It controls whether HTTP-level content codings are used;
 * typically this means transparent gzip compression of the HTTP
 * response stream where negotiation indicates it is allowed.
 *
 * 

Two static instances {@link #NONE} and {@link #GZIP} are provided. * It should be completely safe to use either of these in any context, * since an instance of this class represents only an indication to an * HTTP server that a particular coding scheme is supported by the client. * A service is therefore always at liberty to ignore this hint/request * and provide and unencoded response if, for instance, it does not support * the requested compression scheme. Good practice for use of this class * is therefore probably to use GZIP where the response is expected * to be large and reasonably compressible (a long VOTable is a good * example), and NONE where the response is expected to be short or, * especially, not very gzippable (for instance noisy binary floating * point data, or a byte stream that has already been compressed). * *

The provided instances also include some logging functionality; * information about how many bytes (and where applicable the level of * compression) is logged for bytestreams read through these instances. * The logging level for this information is currently CONFIG. * * * @see RFC 2616 secs 3.5, 14.3, 14.11 */ public abstract class ContentCoding { /** No encoding is requested. */ public static final ContentCoding NONE = createCoding( "NONE", false, Level.CONFIG ); /** Gzip encoding is requested. */ public static final ContentCoding GZIP = createCoding( "GZIP", true, Level.CONFIG ); /** Name of HTTP request header to request coded response ({@value}). */ public static final String ACCEPT_ENCODING = "Accept-Encoding"; /** Name of HTTP response header to mark coded response ({@value}). */ public static final String CONTENT_ENCODING = "Content-Encoding"; /** Name of HTTP content-coding token indicating GZIP ({@value}). */ private static final String GZIP_NAME = "gzip"; private static final Logger logger_ = Logger.getLogger( "uk.ac.starlink.util" ); /** * Constructor. */ protected ContentCoding() { } /** * Sets up request headers for the given connection. * The connection must not have yet been connected. * * @param conn unconnected connection */ public abstract void prepareRequest( URLConnection conn ); /** * Returns the input stream response from the given connection, * which was prepared using this object's prepareRequest * method. Any required decoding will have been done transparently. * * @param conn connection * @return stream decoded as required * @see java.net.URLConnection#getInputStream */ public abstract InputStream getInputStream( URLConnection conn ) throws IOException; /** * Returns the error stream response from the given connection, * which was prepared using this object's prepareRequest * method. Any required decoding will have been done transparently. * * @param conn connection * @return stream decoded as required * @see java.net.URLConnection#getInputStream */ public abstract InputStream getErrorStream( URLConnection conn ) throws IOException; /** * Convenience method to open a new connection prepared in accordance * with this object's encoding policy. * Opens the connection and calls prepareRequest. * * @param url target URL * @return prepared connection */ public URLConnection openConnection( URL url ) throws IOException { URLConnection conn = url.openConnection(); prepareRequest( conn ); return conn; } /** * Convenience method to return a byte stream from a given URL * in accordance with this object's encoding policy. * Opens the connection, prepares the request, and decodes the result. * * @param url target URL * @return unencoded stream from the URL */ public InputStream openStream( URL url ) throws IOException { URLConnection conn = url.openConnection(); prepareRequest( conn ); return getInputStream( conn ); } /** * Creates a ContentCoding instance that optionally requests compression. * * @param name user-readable name of the coding instance * @param useGzip if true, requests gzip encoding * @param countLevel logging level for byte count information * @return new ContentCoding */ private static ContentCoding createCoding( final String name, final boolean useGzip, final Level countLevel ) { return new ContentCoding() { public void prepareRequest( URLConnection conn ) { if ( conn instanceof HttpURLConnection && useGzip ) { ((HttpURLConnection) conn) .setRequestProperty( ACCEPT_ENCODING, GZIP_NAME ); } } public InputStream getInputStream( URLConnection conn ) throws IOException { InputStream in = conn.getInputStream(); final boolean isCounting = logger_.isLoggable( countLevel ); final boolean isGzip = isGzip( conn ); if ( isCounting ) { in = new CountInputStream( in ) { @Override public void close() throws IOException { super.close(); if ( ! isGzip ) { logger_.log( countLevel, "Bytes read: " + getReadCount() ); } } }; } final CountInputStream rawIn = isCounting ? (CountInputStream) in : null; if ( isGzip ) { in = Compression.GZIP.decompress( in ); if ( isCounting ) { in = new CountInputStream( in ) { @Override public void close() throws IOException { super.close(); long rawCount = rawIn.getReadCount(); long decodeCount = getReadCount(); String msg = new StringBuffer() .append( "Bytes read: " ) .append( decodeCount ) .append( " (compressed to " ) .append( rawCount ) .append( " = " ) .append( (int) ( rawCount * 100. / decodeCount ) ) .append( "% " ) .append( "using HTTP gzip content-coding)" ) .toString(); logger_.log( countLevel, msg ); } }; } } return in; } public InputStream getErrorStream( URLConnection conn ) throws IOException { if ( conn instanceof HttpURLConnection ) { InputStream in = ((HttpURLConnection) conn).getErrorStream(); if ( in != null && isGzip( conn ) ) { return Compression.GZIP.decompress( in ); } return in; } else { return null; } } /** * Determines whether a given connection has actually * declared gzip encoding. This makes the connection if it * hasn't already been done. * * @param conn connection * @return true iff the content-encoding field says gzip */ private boolean isGzip( URLConnection conn ) { if ( conn instanceof HttpURLConnection ) { String coding = ((HttpURLConnection) conn) .getHeaderField( CONTENT_ENCODING ); return coding != null && coding.trim().toLowerCase().equals( GZIP_NAME ); } else { return false; } } @Override public String toString() { return name; } }; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy