huruyosi’s blog

プログラミングとかインフラとかのメモです。

Play framework 1.2.7 で “Request exceeds 8192 bytes”が発生した

事象

Play framework 1.2.7 で実装した RestAPIに c# で実装した RestClient から GET /path/to/api?var1=hoge&var2=huga~以下省略 といった要領でQueryStringのパラメータを大量に設定すると logs/application.log に Request exceeds 8192 bytes と出力され処理が異常終了します。

原因

ログのメッセージを出力しているのは play.server.PlayHandler#exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)です。

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
        try {
            // If we get a TooLongFrameException, we got a request exceeding 8k.
            // Log this, we can't call serve500()
            Throwable t = e.getCause();
            if (t instanceof TooLongFrameException) {
                Logger.error(t, "Request exceeds 8192 bytes");
            }
            e.getChannel().close();
        } catch (Exception ex) {
        }
    }

ならばと、例外 TooLongFrameException を送出している箇所を探すと、org.jboss.netty.handler.codec.http.HttpMessageDecoder.readLine(ChannelBuffer buffer, int maxLineLength) でした。例外を送出する時に 変数 sb を確認すると、GET /path/to/api?var1=hoge&var2=huga~以下省略が設定されていました。

    private String readLine(ChannelBuffer buffer, int maxLineLength) throws TooLongFrameException {
        StringBuilder sb = new StringBuilder(64);
        int lineLength = 0;
        while (true) {
            byte nextByte = buffer.readByte();
            if (nextByte == HttpCodecUtil.CR) {
                nextByte = buffer.readByte();
                if (nextByte == HttpCodecUtil.LF) {
                    return sb.toString();
                }
            } else if (nextByte == HttpCodecUtil.LF) {
                return sb.toString();
            } else {
                if (lineLength >= maxLineLength) {
                    // TODO: Respond with Bad Request and discard the traffic
                    //    or close the connection.
                    //       No need to notify the upstream handlers - just log.
                    //       If decoding a response, just throw an exception.
                    throw new TooLongFrameException(
                            "An HTTP line is larger than " + maxLineLength +
                            " bytes.");
                }
                lineLength ++;
                sb.append((char) nextByte);
            }
        }
    }

上限値の maxLineLength は HttpMessageDecoderのデフォルトコンストラクタで決まっていました。

対策

今回は時間の関係でQueryStringのパラメータ名を短くして対応しました。

が、maxLineLength の上限を引き上げるには play.server.play.server#getPipeline()メソッドで行っている new HttpRequestDecoder()に引数を指定すれば上限を引き上げられます。たぶん。

play-1.2.7.jar を作り直す必要があるので、QueryStringのパラメータ名を短くすることにしました。