ServletInputStreamを2回使う

個々のアクションとかの前処理段階で、HTTPリクエストに怪しいデータが入っていないかチェックするFilterを用意したりしていたんですが(・ω・)
これがmultipartのPOST(ファイルアップロードのある画面)の時に対応していないっていうんで、multipartの時の処理(内部的にはcommons-uploadを使用)を追加してみたところ、今度はアクションにパラメータが渡らないという状況に(;´Д`)


理由は、multipartのリクエストを解析するために使用しているServletInputStream(の実装)が再利用を考慮していないため、Filter内でServletInputStreamを使用してしまうと、アクションにパラメータを設定する際にはServletInputStreamのポインタがEOFになっているためですた(・ω・)
Tomcatの実装もWebLogicの実装も駄目ですね、reset()に対応していないし。


つーこって、下記の様なラッパーを用意して対応。

public class BufferedServletInputStream extends ServletInputStream {

    private ByteArrayInputStream inputStream;

    public BufferedServletInputStream(byte[] buffer) {
        this.inputStream = new ByteArrayInputStream( buffer );
    }

    @Override
    public int available() throws IOException {
        return inputStream.available();
    }

    @Override
    public int read() throws IOException {
        return inputStream.read();
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        return inputStream.read( b, off, len );
    }
}
public class BufferedServletRequestWrapper extends HttpServletRequestWrapper {

    private byte[] buffer;

    public BufferedServletRequestWrapper(HttpServletRequest request) throws IOException {
        super( request );

        InputStream is = request.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte buff[] = new byte[ 1024 ];
        int read;
        while( ( read = is.read( buff ) ) > 0 ) {
            baos.write( buff, 0, read );
        }

        this.buffer = baos.toByteArray();
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new BufferedServletInputStream( this.buffer );
    }
}

元のInputStreamからあらかじめバッファにデータを読み込んでおいて、それを元に都度InputStreamを作成するカンジですね。


使い方はこんなん(・ω・)

public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException {

    HttpServletRequest req = (HttpServletRequest)request;
    request = new BufferedRequestWrapper( req );

    InputStream is = request.getInputStream();

...

    chain.doFilter(request, response);
}

逆の処理として、レスポンスの書き換え(携帯用の絵文字変換、encodeURL()とか)に使用するBufferdServletOutputStream、BufferedServletResponseWrapperとかも用意したでよ(・∀・)