/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.redkale.net.http;

import java.io.*;
import java.lang.reflect.Type;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.file.StandardOpenOption;
import java.time.ZoneId;
import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.logging.*;
import org.redkale.convert.*;
import org.redkale.convert.json.*;
import org.redkale.net.*;
import org.redkale.service.RetResult;
import org.redkale.util.*;
import org.redkale.util.AnyValue.Entry;
import static org.redkale.util.Utility.append;

/**
 * Http响应包 与javax.servlet.http.HttpServletResponse 基本类似。  <br>
 * 同时提供发送json的系列接口: public void finishJson(Type type, Object obj)  <br>
 * Redkale提倡http+json的接口风格， 所以主要输出的数据格式为json， 同时提供异步接口。  <br>
 * <p>
 * 详情见: https://redkale.org
 *
 * @author zhangjx
 */
public class HttpResponse extends Response<HttpContext, HttpRequest> {

    protected static final byte[] EMPTY_BTYES = new byte[0];

    protected static final byte[] bytes304 = "HTTP/1.1 304 Not Modified\r\nContent-Length:0\r\n\r\n".getBytes();

    protected static final byte[] bytes404 = "HTTP/1.1 404 Not Found\r\nContent-Length:0\r\n\r\n".getBytes();

    protected static final byte[] bytes405 = "HTTP/1.1 405 Method Not Allowed\r\nContent-Length:0\r\n\r\n".getBytes();

    protected static final byte[] bytes500 = "HTTP/1.1 500 Internal Server Error\r\nContent-Length:0\r\n\r\n".getBytes();

    protected static final byte[] bytes504 = "HTTP/1.1 504 Gateway Timeout\r\nContent-Length:0\r\n\r\n".getBytes();

    protected static final byte[] status200Bytes = "HTTP/1.1 200 OK\r\n".getBytes();

    protected static final byte[] LINE = new byte[]{'\r', '\n'};

    protected static final byte[] serverNameBytes = ("Server: " + System.getProperty("redkale.http.response.header.server", "redkale" + "/" + Redkale.getDotedVersion()) + "\r\n").getBytes();

    protected static final byte[] connectCloseBytes = "none".equalsIgnoreCase(System.getProperty("redkale.http.response.header.connection")) ? new byte[0] : "Connection: close\r\n".getBytes();

    protected static final byte[] connectAliveBytes = "none".equalsIgnoreCase(System.getProperty("redkale.http.response.header.connection")) ? new byte[0] : "Connection: keep-alive\r\n".getBytes();

    protected static final String CONTENT_TYPE_HTML_UTF8 = "text/html; charset=utf-8";

    private static final int CACHE_MAX_CONTENT_LENGTH = 1000;

    private static final byte[] status200_server_live_Bytes = append(append(status200Bytes, serverNameBytes), connectAliveBytes);

    private static final byte[] status200_server_close_Bytes = append(append(status200Bytes, serverNameBytes), connectCloseBytes);

    private static final ZoneId ZONE_GMT = ZoneId.of("GMT");

    private static final Map<Integer, String> httpCodes = new HashMap<>();

    private static final byte[][] contentLengthArray = new byte[CACHE_MAX_CONTENT_LENGTH][];

    private static final JsonConvert jsonRootConvert = JsonConvert.root();

    static {

        httpCodes.put(100, "Continue");
        httpCodes.put(101, "Switching Protocols");

        httpCodes.put(200, "OK");
        httpCodes.put(201, "Created");
        httpCodes.put(202, "Accepted");
        httpCodes.put(203, "Non-Authoritative Information");
        httpCodes.put(204, "No Content");
        httpCodes.put(205, "Reset Content");
        httpCodes.put(206, "Partial Content");

        httpCodes.put(300, "Multiple Choices");
        httpCodes.put(301, "Moved Permanently");
        httpCodes.put(302, "Found");
        httpCodes.put(303, "See Other");
        httpCodes.put(304, "Not Modified");
        httpCodes.put(305, "Use Proxy");
        httpCodes.put(307, "Temporary Redirect");

        httpCodes.put(400, "Bad Request");
        httpCodes.put(401, "Unauthorized");
        httpCodes.put(402, "Payment Required");
        httpCodes.put(403, "Forbidden");
        httpCodes.put(404, "Not Found");
        httpCodes.put(405, "Method Not Allowed");
        httpCodes.put(406, "Not Acceptable");
        httpCodes.put(407, "Proxy Authentication Required");
        httpCodes.put(408, "Request Timeout");
        httpCodes.put(409, "Conflict");
        httpCodes.put(410, "Gone");
        httpCodes.put(411, "Length Required");
        httpCodes.put(412, "Precondition Failed");
        httpCodes.put(413, "Request Entity Too Large");
        httpCodes.put(414, "Request URI Too Long");
        httpCodes.put(415, "Unsupported Media Type");
        httpCodes.put(416, "Requested Range Not Satisfiable");
        httpCodes.put(417, "Expectation Failed");

        httpCodes.put(500, "Internal Server Error");
        httpCodes.put(501, "Not Implemented");
        httpCodes.put(502, "Bad Gateway");
        httpCodes.put(503, "Service Unavailable");
        httpCodes.put(504, "Gateway Timeout");
        httpCodes.put(505, "HTTP Version Not Supported");

        for (int i = 0; i < CACHE_MAX_CONTENT_LENGTH; i++) {
            contentLengthArray[i] = ("Content-Length: " + i + "\r\n").getBytes();
        }
    }

    private int status = 200;

    private String contentType = "";

    private long contentLength = -1;

    private HttpCookie[] cookies;

    private boolean respHeadContainsConnection;

    private int headWritedSize = -1; //0表示跳过header，正数表示header的字节长度。

    private BiConsumer<HttpResponse, byte[]> cacheHandler;

    private BiFunction<HttpRequest, RetResult, RetResult> retResultHandler;

    //------------------------------------------------
    private final String plainContentType;

    private final byte[] plainContentTypeBytes;

    private final String jsonContentType;

    private final byte[] jsonContentTypeBytes;

    private final AnyValueWriter header = new AnyValueWriter();

    private final String[][] defaultAddHeaders;

    private final String[][] defaultSetHeaders;

    private final boolean autoOptions;

    private final Supplier<byte[]> dateSupplier;

    private final HttpCookie defaultCookie;

    private final HttpRender httpRender;

    private final ByteArray headerArray = new ByteArray();

    private final byte[][] plainLiveContentLengthArray;

    private final byte[][] jsonLiveContentLengthArray;

    private final byte[][] plainCloseContentLengthArray;

    private final byte[][] jsonCloseContentLengthArray;

    private final JsonBytesWriter jsonWriter = new JsonBytesWriter();

    @SuppressWarnings("Convert2Lambda")
    protected final ConvertBytesHandler convertHandler = new ConvertBytesHandler() {
        @Override
        public <A> void completed(byte[] bs, int offset, int length, Consumer<A> callback, A attachment) {
            finish(bs, offset, length, callback, attachment);
        }
    };

    public HttpResponse(HttpContext context, HttpRequest request, HttpResponseConfig config) {
        super(context, request);
        this.defaultAddHeaders = config == null ? null : config.defaultAddHeaders;
        this.defaultSetHeaders = config == null ? null : config.defaultSetHeaders;
        this.defaultCookie = config == null ? null : config.defaultCookie;
        this.autoOptions = config != null && config.autoOptions;
        this.dateSupplier = config == null ? null : config.dateSupplier;
        this.httpRender = config == null ? null : config.httpRender;

        this.plainContentType = config == null ? "text/plain; charset=utf-8" : config.plainContentType;
        this.jsonContentType = config == null ? "application/json; charset=utf-8" : config.jsonContentType;
        this.plainContentTypeBytes = config == null ? ("Content-Type: " + this.plainContentType + "\r\n").getBytes() : config.plainContentTypeBytes;
        this.jsonContentTypeBytes = config == null ? ("Content-Type: " + this.jsonContentType + "\r\n").getBytes() : config.jsonContentTypeBytes;
        this.plainLiveContentLengthArray = config == null ? null : config.plainLiveContentLengthArray;
        this.plainCloseContentLengthArray = config == null ? null : config.plainCloseContentLengthArray;
        this.jsonLiveContentLengthArray = config == null ? null : config.jsonLiveContentLengthArray;
        this.jsonCloseContentLengthArray = config == null ? null : config.jsonCloseContentLengthArray;
        this.contentType = this.plainContentType;
    }

    @Override
    protected AsyncConnection removeChannel() {
        return super.removeChannel();
    }

    protected AsyncConnection getChannel() {
        return channel;
    }

    @Override
    protected void prepare() {
        super.prepare();
    }

    @Override
    protected boolean recycle() {
        this.status = 200;
        this.contentLength = -1;
        this.contentType = null;
        this.cookies = null;
        this.headWritedSize = -1;
        //this.headBuffer = null;
        this.header.clear();
        this.headerArray.clear();
        this.cacheHandler = null;
        this.retResultHandler = null;
        this.respHeadContainsConnection = false;
        this.jsonWriter.recycle();
        return super.recycle();
    }

    @Override
    protected ExecutorService getWorkExecutor() {
        return super.getWorkExecutor();
    }

    @Override
    protected void updateNonBlocking(boolean nonBlocking) {
        super.updateNonBlocking(nonBlocking);
    }

    @Override
    protected boolean inNonBlocking() {
        return super.inNonBlocking();
    }

//    protected Supplier<ByteBuffer> getBodyBufferSupplier() {
//        return bodyBufferSupplier;
//    }
    @Override
    protected void init(AsyncConnection channel) {
        super.init(channel);
    }

    /**
     * 获取状态码对应的状态描述
     *
     * @param status 状态码
     *
     * @return 状态描述
     */
    protected String getHttpCode(int status) {
        return httpCodes.get(status);
    }

    protected HttpRequest getRequest() {
        return request;
    }

    protected String getHttpCode(int status, String defValue) {
        String v = httpCodes.get(status);
        return v == null ? defValue : v;
    }

    @Override
    @SuppressWarnings("unchecked")
    protected void thenEvent(Servlet servlet) {
        this.servlet = servlet;
    }

    protected boolean isAutoOptions() {
        return this.autoOptions;
    }

    /**
     * 增加Cookie值
     *
     * @param cookies cookie
     *
     * @return HttpResponse
     */
    public HttpResponse addCookie(HttpCookie... cookies) {
        this.cookies = Utility.append(this.cookies, cookies);
        return this;
    }

    /**
     * 增加Cookie值
     *
     * @param cookies cookie
     *
     * @return HttpResponse
     */
    public HttpResponse addCookie(Collection<HttpCookie> cookies) {
        this.cookies = Utility.append(this.cookies, cookies);
        return this;
    }

    /**
     * 创建CompletionHandler实例
     *
     * @return CompletionHandler
     */
    public CompletionHandler createAsyncHandler() {
        return Utility.createAsyncHandler((v, a) -> {
            finish(v);
        }, (t, a) -> {
            context.getLogger().log(Level.WARNING, "Servlet occur, force to close channel. request = " + request + ", result is CompletionHandler", (Throwable) t);
            finishError(t);
        });
    }

    @Override
    protected void defaultError(Throwable t) {
        finish(500, null);
    }

    /**
     * 创建CompletionHandler子类的实例 <br>
     *
     * 传入的CompletionHandler子类必须是public，且保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数。
     *
     * @param <H>          泛型
     * @param handlerClass CompletionHandler子类
     *
     * @return CompletionHandler
     */
    @SuppressWarnings("unchecked")
    public <H extends CompletionHandler> H createAsyncHandler(Class<H> handlerClass) {
        if (handlerClass == null || handlerClass == CompletionHandler.class) {
            return (H) createAsyncHandler();
        }
        return context.loadAsyncHandlerCreator(handlerClass).create(createAsyncHandler());
    }

    /**
     * 将对象以JSON格式输出
     *
     * @param obj 输出对象
     */
    public final void finishJson(final Object obj) {
        finishJson((Convert) null, (Type) null, obj);
    }

    /**
     * 将对象以JSON格式输出
     *
     * @param convert 指定的JsonConvert
     * @param obj     输出对象
     */
    public final void finishJson(final Convert convert, final Object obj) {
        finishJson(convert, (Type) null, obj);
    }

    /**
     * 将对象以JSON格式输出
     *
     * @param type 指定的类型
     * @param obj  输出对象
     */
    public final void finishJson(final Type type, final Object obj) {
        finishJson((Convert) null, type, obj);
    }

    /**
     * 将对象以JSON格式输出
     *
     * @param convert 指定的Convert
     * @param type    指定的类型
     * @param obj     输出对象
     */
    public void finishJson(final Convert convert, final Type type, final Object obj) {
        this.contentType = this.jsonContentType;
        if (this.recycleListener != null) {
            this.output = obj;
        }
        Convert c = convert == null ? request.getRespConvert() : convert;
        Type t = type == null ? (obj == null ? null : obj.getClass()) : type;
        if (c == jsonRootConvert) {
            JsonBytesWriter writer = jsonWriter;
            c.convertTo(writer.clear(), t, obj);
            finish(false, (String) null, writer.content(), writer.offset(), writer.length(), null, null);
        } else {
            c.convertToBytes(t, obj, convertHandler);
        }
    }

    /**
     * 将对象以JSON格式输出
     *
     * @param objs 输出对象
     */
//    @Deprecated  //@since 2.3.0
//    void finishJson(final Object... objs) {
//        this.contentType = this.jsonContentType;
//        if (this.recycleListener != null) this.output = objs;
//        request.getRespConvert().convertToBytes(objs, convertHandler);
//    }
    /**
     * 将RetResult对象以JSON格式输出
     *
     * @param type 指定的RetResult泛型类型
     * @param ret  RetResult输出对象
     */
//    @Deprecated //@since 2.5.0
//    public void finishJson(Type type, RetResult ret) {
//        this.contentType = this.jsonContentType;
//        if (this.retResultHandler != null) {
//            ret = this.retResultHandler.apply(this.request, ret);
//        }
//        if (this.recycleListener != null) this.output = ret;
//        if (ret != null && !ret.isSuccess()) {
//            this.header.addValue("retcode", String.valueOf(ret.getRetcode()));
//            this.header.addValue("retinfo", ret.getRetinfo());
//        }
//        Convert convert = ret == null ? null : ret.convert();
//        if (convert == null) convert = request.getRespConvert();
//        if (convert == jsonRootConvert) {
//            JsonBytesWriter writer = jsonWriter;
//            convert.convertTo(writer.clear(), type, ret);
//            finishFuture(false, (String) null, writer.content(), writer.offset(), writer.length(), null, null);
//        } else {
//            convert.convertToBytes(type, ret, convertHandler);
//        }
//    }
    /**
     * 将RetResult对象以JSON格式输出
     *
     * @param convert 指定的Convert
     * @param type    指定的RetResult泛型类型
     * @param ret     RetResult输出对象
     */
//    @Deprecated //@since 2.5.0
//    public void finishJson(final Convert convert, Type type, RetResult ret) {
//        this.contentType = this.jsonContentType;
//        if (this.retResultHandler != null) {
//            ret = this.retResultHandler.apply(this.request, ret);
//        }
//        if (this.recycleListener != null) this.output = ret;
//        if (ret != null && !ret.isSuccess()) {
//            this.header.addValue("retcode", String.valueOf(ret.getRetcode()));
//            this.header.addValue("retinfo", ret.getRetinfo());
//        }
//        if (convert == jsonRootConvert) {
//            JsonBytesWriter writer = jsonWriter;
//            convert.convertTo(writer.clear(), type, ret);
//            finishFuture(false, (String) null, writer.content(), writer.offset(), writer.length(), null, null);
//        } else {
//            convert.convertToBytes(type, ret, convertHandler);
//        }
//    }
    /**
     * 将CompletableFuture的结果对象以JSON格式输出
     *
     * @param convert   指定的Convert
     * @param valueType 指定CompletableFuture.value的泛型类型
     * @param future    输出对象的句柄
     */
//    @Deprecated //@since 2.5.0
//    @SuppressWarnings("unchecked")
//    public void finishJson(final Convert convert, final Type valueType, final CompletableFuture future) {
//        finishFuture(convert, valueType, future);
//    }
    /**
     * 将RetResult对象输出
     *
     * @param type 指定的RetResult泛型类型
     * @param ret  RetResult输出对象
     */
    public final void finish(Type type, RetResult ret) {
        finish((Convert) null, type, ret);
    }

    /**
     * 将RetResult对象输出
     *
     * @param convert 指定的Convert
     * @param type    指定的RetResult泛型类型
     * @param ret     RetResult输出对象
     */
    @SuppressWarnings("null")
    public void finish(final Convert convert, Type type, RetResult ret) {
        Objects.requireNonNull(type);
        if (this.retResultHandler != null) {
            ret = this.retResultHandler.apply(this.request, ret);
        }
        if (this.recycleListener != null) {
            this.output = ret;
        }
        if (ret != null && !ret.isSuccess()) {
            this.header.addValue("retcode", String.valueOf(ret.getRetcode()));
            this.header.addValue("retinfo", ret.getRetinfo());
        }
        Convert cc = convert;
        if (cc == null && ret != null) {
            cc = ret.convert();
        }
        if (cc == null) {
            cc = request.getRespConvert();
        }
        if (cc instanceof JsonConvert) {
            this.contentType = this.jsonContentType;
        } else if (cc instanceof TextConvert) {
            this.contentType = this.plainContentType;
        }
        if (cc == jsonRootConvert) {
            JsonBytesWriter writer = jsonWriter;
            cc.convertTo(writer.clear(), type, ret);
            finish(false, (String) null, writer.content(), writer.offset(), writer.length(), null, null);
        } else {
            cc.convertToBytes(type, ret, convertHandler);
        }
    }

    /**
     * 将HttpResult对象输出
     *
     * @param resultType HttpResult.result的泛型类型
     * @param result     HttpResult输出对象
     */
    public final void finish(Type resultType, HttpResult result) {
        finish((Convert) null, resultType, result);
    }

    /**
     * 将HttpResult对象输出
     *
     * @param convert    指定的Convert
     * @param resultType HttpResult.result的泛型类型
     * @param result     HttpResult输出对象
     */
    public void finish(final Convert convert, Type resultType, HttpResult result) {
        if (result.getContentType() != null) {
            setContentType(result.getContentType());
        }
        addHeader(result.getHeaders()).addCookie(result.getCookies()).setStatus(result.getStatus() < 1 ? 200 : result.getStatus());
        Object val = result.getResult();
        if (val == null) {
            finish((String) null, EMPTY_BTYES);
        } else if (val instanceof CharSequence) {
            finish(getStatus(), val.toString());
        } else {
            Convert cc = result.convert();
            if (cc == null) {
                cc = convert;
            }
            finish(cc, resultType, val);
        }
    }

    /**
     * 将CompletionStage对象输出
     *
     * @param valueType CompletionFuture.value的泛型类型
     * @param future    CompletionStage输出对象
     */
    public final void finishFuture(Type valueType, CompletionStage future) {
        finishFuture((Convert) null, valueType, future);
    }

    /**
     * 将CompletionStage对象输出
     *
     * @param convert   指定的Convert
     * @param valueType CompletionFuture.value的泛型类型
     * @param future    CompletionStage输出对象
     */
    public void finishFuture(final Convert convert, Type valueType, CompletionStage future) {
        future.whenComplete((v, e) -> {
            Traces.currentTraceid(request.getTraceid());
            if (e != null) {
                context.getLogger().log(Level.WARNING, "Servlet occur exception. request = " + request + ", result is CompletionStage", (Throwable) e);
                if (e instanceof TimeoutException) {
                    finish504();
                } else {
                    finish500();
                }
                return;
            }
            finish(convert, valueType, v);
            Traces.removeTraceid();
        });
    }

    /**
     * 将CompletionStage对象输出
     *
     * @param valueType CompletionFuture.value的泛型类型
     * @param future    CompletionStage输出对象
     */
    public final void finishJsonFuture(Type valueType, CompletionStage future) {
        finishJsonFuture(request.getRespConvert(), valueType, future);
    }

    /**
     * 将CompletionStage对象输出
     *
     * @param convert   指定的Convert
     * @param valueType CompletionFuture.value的泛型类型
     * @param future    CompletionStage输出对象
     */
    public void finishJsonFuture(final Convert convert, Type valueType, CompletionStage future) {
        future.whenComplete((v, e) -> {
            Traces.currentTraceid(request.getTraceid());
            if (e != null) {
                context.getLogger().log(Level.WARNING, "Servlet occur exception. request = " + request + ", result is CompletionStage", (Throwable) e);
                if (e instanceof TimeoutException) {
                    finish504();
                } else {
                    finish500();
                }
                return;
            }
            finishJson(convert, valueType, v);
            Traces.removeTraceid();
        });
    }

    /**
     * 将Flow.Publisher对象输出
     *
     * @param <T>       泛型
     * @param valueType Publisher的泛型类型
     * @param publisher Publisher输出对象
     */
    public final <T> void finishPublisher(Type valueType, Flow.Publisher<T> publisher) {
        finishPublisher(request.getRespConvert(), valueType, publisher);
    }

    /**
     * 将Flow.Publisher对象输出
     *
     * @param <T>       泛型
     * @param convert   指定的Convert
     * @param valueType Publisher的泛型类型
     * @param publisher Publisher输出对象
     */
    public final <T> void finishPublisher(final Convert convert, Type valueType, Flow.Publisher<T> publisher) {
        finishFuture(convert, valueType, (CompletionStage) Flows.createMonoFuture(publisher));
    }

    /**
     * 将第三方类Flow.Publisher对象(如: Mono/Flux)输出
     *
     * @param valueType Publisher的泛型类型
     * @param publisher Publisher输出对象
     */
    public final void finishPublisher(Type valueType, Object publisher) {
        finishPublisher((Convert) null, valueType, publisher);
    }

    /**
     * 将第三方类Flow.Publisher对象(如: Mono/Flux)输出
     *
     * @param convert   指定的Convert
     * @param valueType Publisher的泛型类型
     * @param publisher Publisher输出对象
     */
    public final void finishPublisher(final Convert convert, Type valueType, Object publisher) {
        finishFuture(convert, valueType, (CompletionStage) Flows.maybePublisherToFuture(publisher));
    }

    /**
     * 将HttpScope对象输出
     *
     * @param future HttpScope输出异步对象
     */
    public final void finishScopeFuture(CompletionStage<HttpScope> future) {
        finishScopeFuture((Convert) null, future);
    }

    /**
     * 将HttpScope对象输出
     *
     * @param convert 指定的Convert
     * @param future  HttpScope输出异步对象
     */
    public void finishScopeFuture(final Convert convert, CompletionStage<HttpScope> future) {
        future.whenComplete((v, e) -> {
            if (e != null) {
                context.getLogger().log(Level.WARNING, "Servlet occur, force to close channel. request = " + request + ", result is CompletionStage", (Throwable) e);
                if (e instanceof TimeoutException) {
                    finish504();
                } else {
                    finish500();
                }
                return;
            }
            finish(convert, v);
        });
    }

    /**
     * 将HttpScope对象输出
     *
     * @param result HttpScope输出对象
     */
    public final void finish(HttpScope result) {
        finish((Convert) null, result);
    }

    /**
     * 将HttpScope对象输出
     *
     * @param convert 指定的Convert
     * @param result  HttpScope输出对象
     */
    public void finish(final Convert convert, HttpScope result) {
        if (result == null) {
            finish("null");
            return;
        }
        if (httpRender != null) {
            setContentType(CONTENT_TYPE_HTML_UTF8);
            if (result.getHeaders() != null) {
                addHeader(result.getHeaders());
            }
            if (result.getCookies() != null) {
                addCookie(result.getCookies());
            }
            httpRender.renderTo(this.request, this, convert, result);
            return;
        }
        finish("");
    }

    /**
     * 将结果对象输出
     *
     * @param obj 输出对象
     */
    public final void finish(final Object obj) {
        finish((Convert) null, (Type) null, obj);
    }

    /**
     * 将结果对象输出
     *
     * @param convert 指定的Convert
     * @param obj     输出对象
     */
    public final void finish(final Convert convert, final Object obj) {
        finish(convert, (Type) null, obj);
    }

    /**
     * 将结果对象输出
     *
     * @param type 指定的类型, 不一定是obj的数据类型，必然obj为CompletableFuture， type应该为Future的元素类型
     * @param obj  输出对象
     */
    public final void finish(final Type type, Object obj) {
        finish((Convert) null, type, obj);
    }

    /**
     * 将结果对象输出
     *
     * @param convert 指定的Convert
     * @param type    指定的类型, 不一定是obj的数据类型，必然obj为CompletionStage， type应该为Future的元素类型
     * @param obj     输出对象
     */
    @SuppressWarnings({"unchecked", "null"})
    public void finish(final Convert convert, final Type type, Object obj) {
        Object val = obj;
        //以下if条件会被Rest类第2440行左右的地方用到
        if (val == null) {
            Convert cc = convert;
            if (cc == null) {
                cc = request.getRespConvert();
            }
            if (cc instanceof JsonConvert) {
                this.contentType = this.jsonContentType;
            } else if (cc instanceof TextConvert) {
                this.contentType = this.plainContentType;
            }
            cc.convertToBytes(val, convertHandler);
        } else if (val instanceof CompletionStage) {
            finishFuture(convert, val == obj ? type : null, (CompletionStage) val);
        } else if (val instanceof CharSequence) {
            finish((String) val.toString());
        } else if (val instanceof byte[]) {
            finish((byte[]) val);
        } else if (val instanceof File) {
            try {
                finish((File) val);
            } catch (IOException e) {
                context.getLogger().log(Level.WARNING, "HttpServlet finish File occur, force to close channel. request = " + getRequest(), e);
                finish(500, null);
            }
        } else if (val instanceof RetResult) {
            finish(convert, type, (RetResult) val);
        } else if (val instanceof HttpResult) {
            finish(convert, type, (HttpResult) val);
        } else if (val instanceof HttpScope) {
            finish(convert, (HttpScope) val);
        } else {
            Convert cc = convert;
            if (cc == null) {
                cc = request.getRespConvert();
            }
            if (cc instanceof JsonConvert) {
                this.contentType = this.jsonContentType;
            } else if (cc instanceof TextConvert) {
                this.contentType = this.plainContentType;
            }
            if (this.recycleListener != null) {
                this.output = val;
            }
            //this.channel == null为虚拟的HttpResponse
            if (type == null) {
                if (cc == jsonRootConvert) {
                    JsonBytesWriter writer = jsonWriter;
                    cc.convertTo(writer.clear(), val);
                    finish(false, (String) null, writer.content(), writer.offset(), writer.length(), null, null);
                } else {
                    cc.convertToBytes(val, convertHandler);
                }
            } else {
                if (cc == jsonRootConvert) {
                    JsonBytesWriter writer = jsonWriter;
                    cc.convertTo(writer.clear(), type, val);
                    finish(false, (String) null, writer.content(), writer.offset(), writer.length(), null, null);
                } else {
                    cc.convertToBytes(type, val, convertHandler);
                }
            }
        }
    }

    /**
     * 将指定字符串以响应结果输出
     *
     * @param obj 输出内容
     */
    public void finish(String obj) {
        finish(200, obj);
    }

    /**
     * 以指定响应码附带内容输出
     *
     * @param status  响应码
     * @param message 输出内容
     */
    public void finish(int status, String message) {
        if (isClosed()) {
            return;
        }
        this.status = status;
        //if (status != 200) super.refuseAlive();
        final byte[] val = message == null ? HttpRequest.EMPTY_BYTES : (context.getCharset() == null ? Utility.encodeUTF8(message) : message.getBytes(context.getCharset()));
        finish(false, null, val, 0, val.length, null, null);
    }

    @Override
    public void finish(boolean kill, final byte[] bs, int offset, int length) {
        finish(false, null, bs, offset, length, null, null);
    }

    public <A> void finish(final byte[] bs, int offset, int length, Consumer<A> callback, A attachment) {
        finish(false, null, bs, offset, length, callback, attachment);
    }

    /**
     * 将指定byte[]按响应结果输出
     *
     * @param contentType ContentType
     * @param bs          输出内容
     */
    public void finish(final String contentType, final byte[] bs) {
        finish(false, contentType, bs, 0, bs == null ? 0 : bs.length, null, null);
    }

    /**
     * 将ByteTuple按响应结果输出
     *
     * @param contentType ContentType
     * @param array       输出内容
     */
    public void finish(final String contentType, final ByteTuple array) {
        finish(false, contentType, array.content(), array.offset(), array.length(), null, null);
    }

    /**
     * 将指定byte[]按响应结果输出
     *
     * @param kill        kill
     * @param contentType ContentType
     * @param bs          输出内容
     * @param offset      偏移量
     * @param length      长度
     */
    protected void finish(boolean kill, final String contentType, final byte[] bs, int offset, int length) {
        finish(kill, contentType, bs, offset, length, null, null);
    }

    /**
     * 将指定byte[]按响应结果输出
     *
     * @param kill        kill
     * @param contentType ContentType
     * @param bs          输出内容
     * @param offset      偏移量
     * @param length      长度
     * @param callback    Consumer
     * @param attachment  ConvertWriter
     * @param <A>         A
     */
    protected <A> void finish(boolean kill, final String contentType, final byte[] bs, int offset, int length, Consumer<A> callback, A attachment) {
        if (isClosed()) {
            return; //避免重复关闭
        }
        if (this.headWritedSize < 0) {
            if (contentType != null) {
                this.contentType = contentType;
            }
            this.contentLength = length;
            createHeader();
        }
        ByteArray data = headerArray;
        data.put(bs, offset, length);
        if (callback != null) {
            callback.accept(attachment);
        }
        if (cacheHandler != null) {
            cacheHandler.accept(this, data.getBytes());
        }
        //不能用finish(boolean kill, final ByteTuple array) 否则会调this.finishFuture
        super.finish(false, data.content(), 0, data.length());
    }

    /**
     * 以304状态码输出
     */
    public void finish304() {
        skipHeader();
        super.finish(false, bytes304);
    }

    /**
     * 以404状态码输出
     */
    public void finish404() {
        skipHeader();
        super.finish(false, bytes404);
    }

    /**
     * 以405状态码输出
     */
    public void finish405() {
        skipHeader();
        super.finish(false, bytes405);
    }

    /**
     * 以500状态码输出
     */
    public void finish500() {
        skipHeader();
        super.finish(false, bytes500);
    }

    /**
     * 以504状态码输出
     */
    public void finish504() {
        skipHeader();
        super.finish(false, bytes504);
    }

    //Header大小
    protected void createHeader() {
        if (this.status == 200 && !this.respHeadContainsConnection && !this.request.isWebSocket()
            && (this.contentType == null || this.contentType == this.jsonContentType || this.contentType == this.plainContentType)
            && (this.contentLength >= 0 && this.contentLength < jsonLiveContentLengthArray.length)) {
            byte[][] lengthArray = this.plainLiveContentLengthArray;
            if (this.request.isKeepAlive()) {
                if (this.contentType == this.jsonContentType) {
                    lengthArray = this.jsonLiveContentLengthArray;
                }
            } else {
                if (this.contentType == this.jsonContentType) {
                    lengthArray = this.jsonCloseContentLengthArray;
                } else {
                    lengthArray = this.plainCloseContentLengthArray;
                }
            }
            headerArray.put(lengthArray[(int) this.contentLength]);
        } else {
            if (this.status == 200 && !this.respHeadContainsConnection && !this.request.isWebSocket()) {
                if (this.request.isKeepAlive()) {
                    headerArray.put(status200_server_live_Bytes);
                } else {
                    headerArray.put(status200_server_close_Bytes);
                }
            } else {
                if (this.status == 200) {
                    headerArray.put(status200Bytes);
                } else {
                    headerArray.put(("HTTP/1.1 " + this.status + " " + httpCodes.get(this.status) + "\r\n").getBytes());
                }
                headerArray.put(serverNameBytes);
                if (!this.respHeadContainsConnection) {
                    byte[] bs = this.request.isKeepAlive() ? connectAliveBytes : connectCloseBytes;
                    if (bs.length > 0) {
                        headerArray.put(bs);
                    }
                }
            }
            if (!this.request.isWebSocket()) {
                if (this.contentType == this.jsonContentType) {
                    headerArray.put(this.jsonContentTypeBytes);
                } else if (this.contentType == null || this.contentType == this.plainContentType) {
                    headerArray.put(this.plainContentTypeBytes);
                } else {
                    headerArray.put(("Content-Type: " + this.contentType + "\r\n").getBytes());
                }
            }
            if (this.contentLength >= 0) {
                if (this.contentLength < contentLengthArray.length) {
                    headerArray.put(contentLengthArray[(int) this.contentLength]);
                } else {
                    headerArray.put(("Content-Length: " + this.contentLength + "\r\n").getBytes());
                }
            }
        }
        if (dateSupplier != null) {
            headerArray.put(dateSupplier.get());
        }

        if (this.defaultAddHeaders != null) {
            for (String[] headers : this.defaultAddHeaders) {
                if (headers.length > 3) {
                    String v = request.getParameter(headers[2]);
                    if (v != null) {
                        this.header.addValue(headers[0], v);
                    }
                } else if (headers.length > 2) {
                    String v = request.getHeader(headers[2]);
                    if (v != null) {
                        this.header.addValue(headers[0], v);
                    }
                } else {
                    this.header.addValue(headers[0], headers[1]);
                }
            }
        }
        if (this.defaultSetHeaders != null) {
            for (String[] headers : this.defaultSetHeaders) {
                if (headers.length > 3) {
                    String v = request.getParameter(headers[2]);
                    if (v != null) {
                        this.header.setValue(headers[0], v);
                    }
                } else if (headers.length > 2) {
                    String v = request.getHeader(headers[2]);
                    if (v != null) {
                        this.header.setValue(headers[0], v);
                    }
                } else {
                    this.header.setValue(headers[0], headers[1]);
                }
            }
        }
        for (Entry<String> en : this.header.getStringEntrys()) {
            headerArray.put((en.name + ": " + en.getValue() + "\r\n").getBytes());
        }
        if (request.newSessionid != null) {
            String domain = defaultCookie == null ? null : defaultCookie.getDomain();
            if (domain == null || domain.isEmpty()) {
                domain = "";
            } else {
                domain = "Domain=" + domain + "; ";
            }
            String path = defaultCookie == null ? null : defaultCookie.getPath();
            if (path == null || path.isEmpty()) {
                path = "/";
            }
            if (request.newSessionid.isEmpty()) {
                headerArray.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=; " + domain + "Path=/; Max-Age=0; HttpOnly\r\n").getBytes());
            } else {
                headerArray.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=" + request.newSessionid + "; " + domain + "Path=/; HttpOnly\r\n").getBytes());
            }
        }
        if (this.cookies != null) {
            for (HttpCookie cookie : this.cookies) {
                if (cookie == null) {
                    continue;
                }
                if (defaultCookie != null) {
                    if (defaultCookie.getDomain() != null && cookie.getDomain() == null) {
                        cookie.setDomain(defaultCookie.getDomain());
                    }
                    if (defaultCookie.getPath() != null && cookie.getPath() == null) {
                        cookie.setPath(defaultCookie.getPath());
                    }
                }
                headerArray.put(("Set-Cookie: " + cookieString(cookie) + "\r\n").getBytes());
            }
        }
        headerArray.put(LINE);
        this.headWritedSize = headerArray.length();
    }

    private CharSequence cookieString(HttpCookie cookie) {
        StringBuilder sb = new StringBuilder();
        sb.append(cookie.getName()).append("=").append(cookie.getValue()).append("; Version=1");
        if (cookie.getDomain() != null) {
            sb.append("; Domain=").append(cookie.getDomain());
        }
        if (cookie.getPath() != null) {
            sb.append("; Path=").append(cookie.getPath());
        }
        if (cookie.getPortlist() != null) {
            sb.append("; Port=").append(cookie.getPortlist());
        }
        if (cookie.getMaxAge() > 0) {
            sb.append("; Max-Age=").append(cookie.getMaxAge());
            sb.append("; Expires=").append(RFC_1123_DATE_TIME.format(java.time.ZonedDateTime.now(ZONE_GMT).plusSeconds(cookie.getMaxAge())));
        }
        if (cookie.getSecure()) {
            sb.append("; Secure");
        }
        if (cookie.isHttpOnly()) {
            sb.append("; HttpOnly");
        }
        return sb;
    }

    /**
     * 异步输出指定内容
     *
     * @param <A>     泛型
     * @param buffer  输出内容
     * @param handler 异步回调函数
     */
    protected <A> void sendBody(ByteBuffer buffer, CompletionHandler<Integer, Void> handler) {
        if (this.headWritedSize < 0) {
            if (this.contentLength < 0) {
                this.contentLength = buffer == null ? 0 : buffer.remaining();
            }
            createHeader();
            if (buffer == null) { //只发header
                super.send(headerArray, handler);
            } else {
                ByteBuffer headbuf = channel.pollWriteBuffer();
                headbuf.put(headerArray.content(), 0, headerArray.length());
                headbuf.flip();
                super.send(new ByteBuffer[]{headbuf, buffer}, null, handler);
            }
        } else {
            super.send(buffer, null, handler);
        }
    }

    /**
     * 将指定文件按响应结果输出
     *
     * @param file 输出文件
     *
     * @throws IOException IO异常
     */
    public void finish(File file) throws IOException {
        finishFile(null, file, null);
    }

    /**
     * 将文件按指定文件名输出
     *
     * @param fileName 输出文件名
     * @param file     输出文件
     *
     * @throws IOException IO异常
     */
    public void finish(final String fileName, File file) throws IOException {
        finishFile(fileName, file, null);
    }

    /**
     * 将指定文件句柄或文件内容按响应结果输出，若fileBody不为null则只输出fileBody内容
     *
     * @param file     输出文件
     * @param fileBody 文件内容， 没有则输出file
     *
     * @throws IOException IO异常
     */
    protected void finishFile(final File file, ByteArray fileBody) throws IOException {
        finishFile(null, file, fileBody);
    }

    /**
     * 将指定文件句柄或文件内容按指定文件名输出，若fileBody不为null则只输出fileBody内容
     * file 与 fileBody 不能同时为空
     * file 与 fileName 也不能同时为空
     *
     * @param fileName 输出文件名
     * @param file     输出文件
     * @param fileBody 文件内容， 没有则输出file
     *
     * @throws IOException IO异常
     */
    protected void finishFile(final String fileName, final File file, ByteArray fileBody) throws IOException {
        if ((file == null || !file.isFile() || !file.canRead()) && fileBody == null) {
            finish404();
            return;
        }
        final long length = file == null ? fileBody.length() : file.length();
        final String match = request.getHeader("If-None-Match");
        final String etag = (file == null ? 0L : file.lastModified()) + "-" + length;
        if (match != null && etag.equals(match)) {
            //finish304();
            //return;
        }
        this.contentLength = length;
        if (Utility.isNotEmpty(fileName) && file != null) {
            if (this.header.getValue("Content-Disposition") == null) {
                addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            }
        }
        this.contentType = MimeType.getByFilename(Utility.isEmpty(fileName) && file != null ? file.getName() : fileName);
        if (this.contentType == null) {
            this.contentType = "application/octet-stream";
        }
        String range = request.getHeader("Range");
        if (range != null && (!range.startsWith("bytes=") || range.indexOf(',') >= 0)) {
            range = null;
        }
        long start = -1;
        long len = -1;
        if (range != null) {
            range = range.substring("bytes=".length());
            int pos = range.indexOf('-');
            start = pos == 0 ? 0 : Integer.parseInt(range.substring(0, pos));
            long end = (pos == range.length() - 1) ? -1 : Long.parseLong(range.substring(pos + 1));
            long clen = end > 0 ? (end - start + 1) : (length - start);
            this.status = 206;
            addHeader("Accept-Ranges", "bytes");
            addHeader("Content-Range", "bytes " + start + "-" + (end > 0 ? end : length - 1) + "/" + length);
            this.contentLength = clen;
            len = end > 0 ? clen : end;
        }
        this.addHeader("ETag", etag);
        createHeader();
        ByteArray data = headerArray;
        if (fileBody == null) {
            if (this.recycleListener != null) {
                this.output = file;
            }
            finishFile(data, file, start, len);
        } else { //一般HttpResourceServlet缓存file内容时fileBody不为空
            if (start >= 0) {
                data.put(fileBody, (int) start, (int) ((len > 0) ? len : fileBody.length() - start));
            }
            super.finish(false, data.content(), 0, data.length());
        }
    }

    //offset、length 为 -1 表示输出整个文件
    private void finishFile(ByteArray headerData, File file, long offset, long length) throws IOException {
        //this.channel.write(headerData,  new TransferFileHandler(file, offset, length));
        final Logger logger = context.getLogger();
        this.channel.write(headerData, new CompletionHandler<Integer, Void>() {

            FileChannel fileChannel;

            long limit;

            long sends;

            ByteBuffer buffer;

            @Override
            public void completed(Integer result, Void attachment) {
                try {
                    if (fileChannel != null && sends >= limit) {
                        if (buffer != null) {
                            channel.offerWriteBuffer(buffer);
                        }
                        try {
                            fileChannel.close();
                        } catch (IOException ie) {
                            //do nothing
                        }
                        completeFinishBytes(result, attachment);
                        return;
                    }
                    if (fileChannel == null) {
                        fileChannel = FileChannel.open(file.toPath(), StandardOpenOption.READ);
                        if (offset > 0) {
                            fileChannel = fileChannel.position(offset);
                        }
                        limit = length > 0 ? length : (file.length() - (offset > 0 ? offset : 0));
                        sends = 0;
                        buffer = channel.ssl() ? channel.pollWriteSSLBuffer() : channel.pollWriteBuffer();
                    }

                    buffer.clear();
                    int len = fileChannel.read(buffer);
                    if (len < 1) {
                        throw new IOException("read " + file + " error: " + len);
                    }
                    buffer.flip();
                    if (sends + len > limit) {
                        buffer.limit((int) (len - limit + sends));
                        sends = limit;
                    } else {
                        sends += len;
                    }
                    channel.write(buffer, attachment, this);
                } catch (Exception e) {
                    if (fileChannel != null) {
                        try {
                            fileChannel.close();
                        } catch (IOException ie) {
                            //do nothing
                        }
                    }
                    failed(e, attachment);
                }
            }

            @Override
            public void failed(Throwable exc, Void attachment) {
                if (buffer != null) {
                    channel.offerWriteBuffer(buffer);
                }
                if (logger.isLoggable(Level.FINER)) {
                    logger.log(Level.FINER, "finishFile error", exc);
                }
                finish(true);
            }
        });
    }

    /**
     * 跳过header的输出
     * 通常应用场景是，调用者的输出内容里已经包含了HTTP的响应头信息，因此需要调用此方法避免重复输出HTTP响应头信息。
     *
     * @return HttpResponse
     */
    public HttpResponse skipHeader() {
        this.headWritedSize = 0;
        return this;
    }

    protected AnyValueWriter duplicateHeader() {
        return this.header.duplicate();
    }

    /**
     * 判断是否存在Header值
     *
     * @param name header-name
     *
     * @return 是否存在
     */
    public boolean existsHeader(String name) {
        return this.header.getValue(name) != null;
    }

    /**
     * 设置Header值
     *
     * @param name  header名
     * @param value header值
     *
     * @return HttpResponse
     */
    public HttpResponse setHeader(String name, Object value) {
        this.header.setValue(name, String.valueOf(value));
        if ("Connection".equalsIgnoreCase(name)) {
            this.respHeadContainsConnection = true;
        }
        return this;
    }

    /**
     * 添加Header值
     *
     * @param name  header名
     * @param value header值
     *
     * @return HttpResponse
     */
    public HttpResponse addHeader(String name, Object value) {
        this.header.addValue(name, String.valueOf(value));
        if ("Connection".equalsIgnoreCase(name)) {
            this.respHeadContainsConnection = true;
        }
        return this;
    }

    /**
     * 添加Header值
     *
     * @param map header值
     *
     * @return HttpResponse
     */
    public HttpResponse addHeader(Map<String, ?> map) {
        if (map == null || map.isEmpty()) {
            return this;
        }
        for (Map.Entry<String, ?> en : map.entrySet()) {
            this.header.addValue(en.getKey(), String.valueOf(en.getValue()));
            if (!respHeadContainsConnection && "Connection".equalsIgnoreCase(en.getKey())) {
                this.respHeadContainsConnection = true;
            }
        }
        return this;
    }

    /**
     * 设置状态码
     *
     * @param status 状态码
     *
     * @return HttpResponse
     */
    public HttpResponse setStatus(int status) {
        this.status = status;
        return this;
    }

    /**
     * 获取状态码
     *
     * @return 状态码
     */
    public int getStatus() {
        return this.status;
    }

    /**
     * 获取 ContentType
     *
     * @return ContentType
     */
    public String getContentType() {
        return contentType;
    }

    /**
     * 设置 ContentType
     *
     * @param contentType ContentType
     *
     * @return HttpResponse
     */
    public HttpResponse setContentType(String contentType) {
        this.contentType = contentType;
        return this;
    }

    /**
     * 获取内容长度
     *
     * @return 内容长度
     */
    public long getContentLength() {
        return contentLength;
    }

    /**
     * 设置内容长度
     *
     * @param contentLength 内容长度
     *
     * @return HttpResponse
     */
    public HttpResponse setContentLength(long contentLength) {
        this.contentLength = contentLength;
        return this;
    }

    /**
     * 获取输出时的拦截器
     *
     * @return 拦截器
     */
    protected BiConsumer<HttpResponse, byte[]> getCacheHandler() {
        return cacheHandler;
    }

    /**
     * 设置输出时的拦截器
     *
     * @param cacheHandler 拦截器
     */
    protected void setCacheHandler(BiConsumer<HttpResponse, byte[]> cacheHandler) {
        this.cacheHandler = cacheHandler;
    }

    /**
     * 获取输出RetResult时的拦截器
     *
     * @return 拦截器
     */
    protected BiFunction<HttpRequest, RetResult, RetResult> getRetResultHandler() {
        return retResultHandler;
    }

    /**
     * 设置输出RetResult时的拦截器
     *
     * @param retResultHandler 拦截器
     */
    public void retResultHandler(BiFunction<HttpRequest, RetResult, RetResult> retResultHandler) {
        this.retResultHandler = retResultHandler;
    }

//    protected final class TransferFileHandler implements CompletionHandler<Integer, Void> {
//
//        private final File file;
//
//        private final AsynchronousFileChannel filechannel;
//
//        private final long max; //需要读取的字节数， -1表示读到文件结尾
//
//        private long count;//读取文件的字节数
//
//        private long readpos = 0;
//
//        private boolean hdwrite = true; //写入Header
//
//        private boolean read = false;
//
//        public TransferFileHandler(File file) throws IOException {
//            this.file = file;
//            this.filechannel = AsynchronousFileChannel.open(file.toPath(), options);
//            this.readpos = 0;
//            this.max = file.length();
//        }
//
//        public TransferFileHandler(File file, long offset, long len) throws IOException {
//            this.file = file;
//            this.filechannel = AsynchronousFileChannel.open(file.toPath(), options);
//            this.readpos = offset <= 0 ? 0 : offset;
//            this.max = len <= 0 ? file.length() : len;
//        }
//
//        @Override
//        public void completed(Integer result, Void attachment) {
//            //(Utility.now() + "---" + Thread.currentThread().getName() + "-----------" + file + "-------------------result: " + result + ", max = " + max + ", readpos = " + readpos + ", count = " + count + ", " + (hdwrite ? "正在写Header" : (read ? "准备读" : "准备写")));
//            if (result < 0 || count >= max) {
//                failed(null, attachment);
//                return;
//            }
//            if (hdwrite && attachment.hasRemaining()) { //Header还没写完
//                channel.write(attachment, attachment, this);
//                return;
//            }
//            if (hdwrite) {
//                //(Utility.now() + "---" + Thread.currentThread().getName() + "-----------" + file + "-------------------Header写入完毕， 准备读取文件.");
//                hdwrite = false;
//                read = true;
//                result = 0;
//            }
//            if (read) {
//                count += result;
//            } else {
//                readpos += result;
//            }
//            if (read && attachment.hasRemaining()) { //Buffer还没写完
//                channel.write(attachment, attachment, this);
//                return;
//            }
//
//            if (read) {
//                read = false;
//                attachment.clear();
//                filechannel.read(attachment, readpos, attachment, this);
//            } else {
//                read = true;
//                if (count > max) {
//                    attachment.limit((int) (attachment.position() + max - count));
//                }
//                attachment.flip();
//                if (attachment.hasRemaining()) {
//                    channel.write(attachment, attachment, this);
//                } else {
//                    failed(null, attachment);
//                }
//            }
//        }
//
//        @Override
//        public void failed(Throwable exc, Void attachment) {
//            finishFuture(true);
//            try {
//                filechannel.close();
//            } catch (IOException e) {
//            }
//        }
//
//    }
    public static class HttpResponseConfig {

        public String plainContentType;

        public String jsonContentType;

        public byte[] plainContentTypeBytes;

        public byte[] jsonContentTypeBytes;

        public String[][] defaultAddHeaders;

        public String[][] defaultSetHeaders;

        public HttpCookie defaultCookie;

        public boolean autoOptions;

        public Supplier<byte[]> dateSupplier;

        public HttpRender httpRender;

        public AnyValue renderConfig;

        public final byte[][] plainLiveContentLengthArray = new byte[CACHE_MAX_CONTENT_LENGTH][];

        public final byte[][] jsonLiveContentLengthArray = new byte[CACHE_MAX_CONTENT_LENGTH][];

        public final byte[][] plainCloseContentLengthArray = new byte[CACHE_MAX_CONTENT_LENGTH][];

        public final byte[][] jsonCloseContentLengthArray = new byte[CACHE_MAX_CONTENT_LENGTH][];

        public HttpResponseConfig init(AnyValue config) {
            if (this.plainContentTypeBytes == null) {
                String plainct = plainContentType == null || plainContentType.isEmpty() ? "text/plain; charset=utf-8" : plainContentType;
                String jsonct = jsonContentType == null || jsonContentType.isEmpty() ? "application/json; charset=utf-8" : jsonContentType;
                this.plainContentType = plainct;
                this.jsonContentType = jsonct;
                this.plainContentTypeBytes = ("Content-Type: " + plainct + "\r\n").getBytes();
                this.jsonContentTypeBytes = ("Content-Type: " + jsonct + "\r\n").getBytes();
                for (int i = 0; i < CACHE_MAX_CONTENT_LENGTH; i++) {
                    byte[] lenbytes = ("Content-Length: " + i + "\r\n").getBytes();
                    plainLiveContentLengthArray[i] = append(append(status200_server_live_Bytes, plainContentTypeBytes), lenbytes);
                    plainCloseContentLengthArray[i] = append(append(status200_server_close_Bytes, plainContentTypeBytes), lenbytes);
                    jsonLiveContentLengthArray[i] = append(append(status200_server_live_Bytes, jsonContentTypeBytes), lenbytes);
                    jsonCloseContentLengthArray[i] = append(append(status200_server_close_Bytes, jsonContentTypeBytes), lenbytes);
                }
            }
            return this;
        }

        @Override
        public String toString() {
            return JsonConvert.root().convertTo(this);
        }
    }
}
