001    /*
002     * Copyright (c) 2009 The openGion Project.
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,
013     * either express or implied. See the License for the specific language
014     * governing permissions and limitations under the License.
015     */
016    package org.opengion.hayabusa.filter;
017    
018    import org.opengion.fukurou.util.StringUtil;
019    
020    import java.io.IOException;
021    import javax.servlet.ServletRequest;
022    import javax.servlet.ServletResponse;
023    import javax.servlet.Filter;
024    import javax.servlet.FilterChain;
025    import javax.servlet.FilterConfig;
026    import javax.servlet.ServletException;
027    import javax.servlet.http.HttpServletRequest;
028    import javax.servlet.http.HttpServletResponse;
029    
030    /**
031     * GZIPFilter は、Filter インターフェースを継承した ZIP圧縮クラスです?
032     * web.xml で filter 設定することにより、Webアプリケーションへのアクセスを制御できます?
033     * フィルタへのパラメータは、IPAddress と Debug を指定できます?
034     * IPAddress は、リモートユーザーのIPアドレスをカンマ区?で??できます?
035     * これは、カンマ区?で?した後?アクセス??アドレスの先???の???
036     * 判定して?す?
037     *
038     * フィルターに対してweb.xml でパラメータを設定します?
039     * <ul>
040     *   <li>IPAddress :フィルタするリモー?Pアドレス。無?時は、ZIP圧縮しません?/li>
041     *   <li>Debug     :フィルタ処??詳細??を?力します?(??モー?</li>
042     * </ul>
043     *
044     *<pre>
045     * 【WEB-INF/web.xml?
046     *     &lt;filter&gt;
047     *         &lt;filter-name&gt;GZIPFilter&lt;/filter-name&gt;
048     *         &lt;filter-class&gt;org.opengion.hayabusa.filter.GZIPFilter&lt;/filter-class&gt;
049     *         &lt;init-param&gt;
050     *             &lt;param-name&gt;ipAddress&lt;/param-name&gt;
051     *             &lt;param-value&gt;200.1&lt;/param-value&gt;
052     *         &lt;/init-param&gt;
053     *         &lt;init-param&gt;
054     *             &lt;param-name&gt;debug&lt;/param-name&gt;
055     *             &lt;param-value&gt;true&lt;/param-value&gt;
056     *         &lt;/init-param&gt;
057     *     &lt;/filter&gt;
058     *
059     *     &lt;filter-mapping&gt;
060     *         &lt;filter-name&gt;GZIPFilter&lt;/filter-name&gt;
061     *         &lt;url-pattern&gt;/jsp/*&lt;/url-pattern&gt;
062     *     &lt;/filter-mapping&gt;
063     *</pre>
064     *
065     * @version  4.0
066     * @author   Kazuhiko Hasegawa
067     * @since    JDK5.0,
068     */
069    public class GZIPFilter implements Filter {
070            private String[] ipaddrArray = null;
071            private boolean  isDebug         = false;
072    
073            /**
074             * Filter インターフェースの doFilter メソ?
075             *
076             * Filter クラスの doFilter メソ?はコン?により呼び出され???チェーンにおけ?
077             * リソースへのクライアントリクエスト?ために?毎回リクエスト?レスポンスのペアが?
078             * チェーンを?して渡されます? こ?メソ?に渡され?FilterChain を利用して、Filter ?
079             * リクエストやレスポンスをチェーン??次のエン??(Filter)にリクエストとレスポンス?
080             * 渡す事ができます?
081             * こ?メソ?の典型的な実??以下?ようなパターンとなるでしょ??
082             * 1. リクエスト?検査
083             * 2. オプションとして、?力フィルタリング用にコン??しくはヘッ?フィルタリング
084             *    するためにカスタ??よるリクエストオブジェクト?ラ??
085             * 3. オプションとして、?力フィルタリング用にコン??しくはヘッ?フィルタリング
086             *    するためにカスタ??よるレスポンスオブジェクトラ??
087             * 4. 以下? a)、b) のどちらか
088             *    a) FileterChain オブジェク?chain.doFilter()) を利用してチェーンの次のエン??を呼び出?
089             *    b) リクエスト??止めるために、リクエスト?レスポンスのペアをフィルタチェーンの次の
090             *       エン??に渡さな?
091             * 5. フィルタチェーンの次のエン??の呼び出した後?直接レスポンスのヘッ?セ?
092             *
093             * @param       req             ServletRequestオブジェク?
094             * @param       res             ServletResponseオブジェク?
095             * @param       chain   FilterChainオブジェク?
096             * @throws IOException 入出力エラーが発生したと?
097             * @throws ServletException サーブレ?関係?エラーが発生した?合?throw されます?
098             */
099            public void doFilter( final ServletRequest req,
100                                                            final ServletResponse res,
101                                                            final FilterChain chain )
102                                                                    throws IOException, ServletException {
103                    if( req instanceof HttpServletRequest && res instanceof HttpServletResponse ) {
104                            HttpServletRequest request = (HttpServletRequest) req;
105                            HttpServletResponse response = (HttpServletResponse) res;
106                            if( isFiltering( request ) ) {
107                                    GZIPResponseWrapper wrappedResponse = new GZIPResponseWrapper(response);
108                                    chain.doFilter(req, wrappedResponse);
109                                    wrappedResponse.finishResponse();
110                                    return;
111                            }
112                    }
113                    chain.doFilter(req, res);
114            }
115    
116            /**
117             * フィルター処??判?
118             *
119             * フィルター処?行うかど?を判定します?
120             * ipAddress と前方??するリモートクライアントから?アクセス時に?
121             * GZIPフィルタリング処?行います?
122             *
123             * @param       request HttpServletRequestオブジェク?
124             *
125             * @return      フィルター処?行うかど?[true:フィルタ対象/false:非対象]
126             */
127            private boolean isFiltering( final HttpServletRequest request ) {
128    
129                    boolean isFilter = false;
130    
131                    String ae   = request.getHeader("accept-encoding");
132                    String uri  = request.getRequestURI();
133                    String adrs = request.getRemoteAddr();
134    
135                    // ブラウザ?GZIP 対応かど?
136                    if( ae != null && ae.indexOf("gzip") >= 0 ) {
137                            // リクエストが、jsp,js,css かど?
138                            if( uri.endsWith(".jsp") || uri.endsWith(".js") || uri.endsWith(".css") ) {
139                                    // アドレスが???アドレス配?で先???して?かど?
140                                    for( int i=0; i<ipaddrArray.length; i++ ) {
141                                            if( adrs.startsWith( ipaddrArray[i] ) ) {
142                                                    isFilter = true;        // ??
143                                                    break;
144                                            }
145                                    }
146                            }
147                    }
148    
149                    if( isDebug ) {
150                            System.out.println("[Filtering " + isFilter + "]");
151                            System.out.println("  IP Address :"     + adrs );
152                            System.out.println("  Request URI:"     + uri );
153                    }
154    
155                    return isFilter;
156            }
157    
158            /**
159             * Filter インターフェースの init メソ? (何もしません)?
160             *
161             * Web コン?は、Filter をサービス状態にするために init メソ?を呼び出します?
162             * Servlet コン?は、Filter をインスタンス化したあと??? init メソ?を呼び出します?
163             * Filter がフィルタリングの仕事を依?される前に、init メソ?は正常に完?てなければ?ません?
164             *
165             * init メソ?が以下?ような状況になると、Web コン?は Filter をサービス状態にできません?
166             * 1. ServletException をスローした
167             * 2. Web コネクタで定義した時間?戻らな?
168             *
169             * @param       filterConfig    FilterConfigオブジェク?
170             */
171            public void init(final FilterConfig filterConfig) {
172                    ipaddrArray = StringUtil.csv2Array( filterConfig.getInitParameter("ipAddress") );
173                    isDebug = Boolean.valueOf( filterConfig.getInitParameter("debug") ).booleanValue();
174            }
175    
176            /**
177             * Filter インターフェースの destroy メソ? (何もしません)?
178             *
179             * サービス状態を終えた事を Filter に伝えるために Web コン?が呼び出します?
180             * Filter の doFilter メソ?が終?たか、タイ?ウトに達した?てのスレ?において?
181             * こ?メソ?を??呼び出されます? Web コン?がこのメソ?を呼び出した後??
182             * Filter のこ?インスタンスにおいて二度と doFilter メソ?を呼び出す事?ありません?
183             *
184             * こ?メソ?は、フィルタに保持されて?(例えば、メモリ、ファイルハンドル、スレ?)
185             * 様?なリソースを開放する機会を与え?あら?永続?の状態が、メモリ上におけ?Filter
186             * の現在の状態と同期して?ように注意してください?
187             */
188            public void destroy() {
189                    // noop
190            }
191    }