/**
 * Copyright (c) 2017 Baidu, Inc. All Rights Reserved.
 * <p>
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * <p>
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * <p>
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 * <p>
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 * <p>
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package com.fuxi.javaagent.response;

import com.fuxi.javaagent.HookHandler;
import com.fuxi.javaagent.config.Config;
import com.fuxi.javaagent.tool.Reflection;

/**
 * Created by tyy on 9/5/17.
 * javax.servlet.http.HttpServletResponse类型响应的统一接口
 */
public class HttpServletResponse {

    public static final int BLOCK_STATUS_CODE = 400;
    private static final String CONTENT_TYPE_HEADER_KEY = "Content-Type";
    private static final String CONTENT_LENGTH_HEADER_KEY = "Content-Length";
    private static final String TRANSFER_ENCODING_HEADER_KEY = "Transfer-Encoding";
    private static final String CONTENT_TYPE_HTML_VALUE = "text/html";
    private static final String TRANSFER_ENCODING_HEADER_VALUE = "chunked";

    private Object response;

    /**
     * constructor
     *
     * @param response http响应实体
     */
    public HttpServletResponse(Object response) {
        this.response = response;
    }

    /**
     * 获取http相应实体
     *
     * @return http响应实体
     */
    public Object getResponse() {
        return response;
    }

    /**
     * 设置响应头,覆盖原值
     *
     * @param key   响应头名称
     * @param value 响应头值
     */
    public void setHeader(String key, String value) {
        if (response != null) {
            Reflection.invokeMethod(response, "setHeader", new Class[]{String.class, String.class}, key, value);
        }
    }

    /**
     * 设置数字响应头,覆盖原值
     *
     * @param key   响应头名称
     * @param value 响应头值
     */
    public void setIntHeader(String key, int value) {
        if (response != null) {
            Reflection.invokeMethod(response, "setIntHeader", new Class[]{String.class, int.class}, key, value);
        }
    }

    /**
     * 设置响应头，不覆盖
     *
     * @param key   响应头名称
     * @param value 响应头值
     */
    public void addHeader(String key, String value) {
        if (response != null) {
            Reflection.invokeMethod(response, "addHeader", new Class[]{String.class, String.class}, key, value);
        }
    }

    /**
     * 获取响应头
     *
     * @param key 响应头名称
     * @return 响应头值值
     */
    public Object getHeader(String key) {
        if (response != null) {
            return Reflection.invokeMethod(response, "getHeader", new Class[]{String.class}, key);
        }
        return null;
    }

    /**
     * 返回异常信息
     */
    public void sendError() {
        if (response != null) {
            try {
                setHeader(CONTENT_TYPE_HEADER_KEY, CONTENT_TYPE_HTML_VALUE);
                Reflection.invokeMethod(response, "setStatus", new Class[]{int.class}, BLOCK_STATUS_CODE);

                String blockUrl = Config.getConfig().getBlockUrl();
                if (!blockUrl.contains("?")) {
                    String blockParam = "?request_id=" + HookHandler.requestCache.get().getRequestId();
                    blockUrl += blockParam;
                }
                String script = "</script><script>location.href=\"" + blockUrl + "\"</script>";

                if (getHeader(CONTENT_LENGTH_HEADER_KEY) != null) {
                    setIntHeader(CONTENT_LENGTH_HEADER_KEY, script.getBytes().length);
                }
                sendErrorScript(script);
            } catch (Exception e) {
                //ignore
            }
        }
    }

    /**
     * 发送自定义错误处理脚本
     */
    private void sendErrorScript(String script) {
        Object printer = Reflection.invokeMethod(response, "getWriter", new Class[]{});
        Reflection.invokeMethod(printer, "print", new Class[]{String.class}, script);
        Reflection.invokeMethod(printer, "flush", new Class[]{});
        Reflection.invokeMethod(printer, "close", new Class[]{});
    }

}

@@ -3837,32 +3837,775 @@
 rn null;%0A    %7D%0A%0A
+    /**%0A     * %E6%B8%85%E9%99%A4%E6%89%80%E6%9C%89 body buffer %E7%BC%93%E5%AD%98%0A     *%0A     * @return %E6%98%AF%E5%90%A6%E6%88%90%E5%8A%9F%0A     */%0A    public boolean resetBuffer() %7B%0A        if (response != null) %7B%0A            try %7B%0A                Reflection.invokeMethod(response, %22resetBuffer%22, new Class%5B%5D%7B%7D);%0A            %7D catch (Exception e) %7B%0A                return false;%0A            %7D%0A            return true;%0A        %7D%0A        return false;%0A    %7D%0A%0A    /**%0A     * %E6%B8%85%E9%99%A4%E6%89%80%E6%9C%89 buffer %E7%BC%93%E5%AD%98%0A     *%0A     * @return %E6%98%AF%E5%90%A6%E6%88%90%E5%8A%9F%0A     */%0A    public boolean reset() %7B%0A        if (response != null) %7B%0A            try %7B%0A                Reflection.invokeMethod(response, %22reset%22, new Class%5B%5D%7B%7D);%0A            %7D catch (Exception e) %7B%0A                return false;%0A            %7D%0A            return true;%0A        %7D%0A        return false;%0A    %7D%0A%0A
     /**%0A     * %E8%BF%94
@@ -5257,24 +5257,55 @@
 /script%3E%22;%0A%0A
+                resetBuffer();%0A
             

reset the buffer when blocking