001    /*
002     * Copyright 2003-2008 the original author or authors.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *     http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     *
016     * You are receiving this code free of charge, which represents many hours of
017     * effort from other individuals and corporations.  As a responsible member 
018     * of the community, you are asked (but not required) to donate any 
019     * enhancements or improvements back to the community under a similar open 
020     * source license.  Thank you. -TMN
021     */
022    package groovyx.net.http;
023    
024    import java.io.IOException;
025    
026    import org.apache.http.Header;
027    import org.apache.http.HeaderElement;
028    import org.apache.http.HttpEntity;
029    import org.apache.http.HttpException;
030    import org.apache.http.HttpRequest;
031    import org.apache.http.HttpRequestInterceptor;
032    import org.apache.http.HttpResponse;
033    import org.apache.http.HttpResponseInterceptor;
034    import org.apache.http.protocol.HttpContext;
035    
036    /**
037     * Base class for handing content-encoding.  
038     * @author <a href='mailto:tnichols@enernoc.com'>Tom Nichols</a>
039     */
040    public abstract class ContentEncoding {
041    
042            public static final String ACCEPT_ENC_HDR = "Accept-Encoding";
043            public static final String CONTENT_ENC_HDR = "Content-Encoding";
044    
045            protected abstract String getContentEncoding();
046            protected abstract HttpEntity wrapResponseEntity( HttpEntity raw );
047    
048            public HttpRequestInterceptor getRequestInterceptor() {
049                    return new RequestInterceptor();
050            }
051            
052            public HttpResponseInterceptor getResponseInterceptor() {
053                    return new ResponseInterceptor();
054            }
055            
056            /**
057             * Enumeration of common content-encodings.
058             */
059            public static enum Type {
060                    GZIP,
061                    COMPRESS,
062                    DEFLATE;
063    
064                    /** Prints the value as it should appear in an HTTP header */
065                    @Override public String toString() {
066                            return this.name().toLowerCase();
067                    }
068            }
069            
070            /**
071             * Request interceptor that adds the correct <code>Accept</code> header
072             * to the outgoing request.
073             * @author <a href='mailto:tnichols@enernoc.com'>Tom Nichols</a>
074             */
075            protected class RequestInterceptor implements HttpRequestInterceptor {
076                    public void process( final HttpRequest req,
077                                    final HttpContext context ) throws HttpException, IOException {
078                            
079                            // set the Accept-Encoding header:
080                            String encoding = getContentEncoding();                 
081                            if ( !req.containsHeader( ACCEPT_ENC_HDR ) )
082                                    req.addHeader( ACCEPT_ENC_HDR, encoding );
083    
084                            else {
085                                    StringBuilder values = new StringBuilder();
086                                    for ( Header h : req.getHeaders( ACCEPT_ENC_HDR ) )
087                                            values.append( h.getValue() ).append( "," );
088    
089                                    String encList = (!values.toString().contains( encoding )) ? values
090                                                    .append( encoding ).toString()
091                                                    : values.toString().substring( 0, values.lastIndexOf( "," ) );
092                                                    
093                                    req.setHeader( ACCEPT_ENC_HDR, encList );
094                            }
095    
096                            //TODO compress request and add content-encoding header.
097                    }
098            }
099    
100            /**
101             * Response interceptor that filters the response stream to decode the 
102             * compressed content before it is passed on to the parser.
103             * @author <a href='mailto:tnichols@enernoc.com'>Tom Nichols</a>
104             */
105            protected class ResponseInterceptor implements HttpResponseInterceptor {
106                    public void process( final HttpResponse response, final HttpContext context ) 
107                                    throws HttpException, IOException {
108    
109                            if ( hasEncoding( response, getContentEncoding() ) )
110                                    response.setEntity( wrapResponseEntity( response.getEntity() ) );
111                    }
112                    
113                    protected boolean hasEncoding( final HttpResponse response, final String encoding ) {
114                            HttpEntity entity = response.getEntity();
115                            if ( entity == null ) return false;
116                            Header ceHeader = entity.getContentEncoding();
117                            if ( ceHeader == null ) return false;
118    
119                            HeaderElement[] codecs = ceHeader.getElements();
120                            for ( int i = 0; i < codecs.length; i++ )
121                                    if ( encoding.equalsIgnoreCase( codecs[i].getName() ) )
122                                            return true;
123                            
124                            return false;
125                    }
126            }
127    }