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