All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.redkale.net.http.HttpResponse Maven / Gradle / Ivy

/*
 * 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.*;
import java.time.ZoneId;
import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.*;
import java.util.logging.Level;
import org.redkale.convert.*;
import org.redkale.convert.json.JsonConvert;
import org.redkale.net.*;
import org.redkale.util.AnyValue.DefaultAnyValue;
import org.redkale.util.AnyValue.Entry;
import org.redkale.util.*;

/**
 * Http响应包 与javax.servlet.http.HttpServletResponse 基本类似。  
* 同时提供发送json的系列接口: public void finishJson(Type type, Object obj)
* Redkale提倡http+json的接口风格, 所以主要输出的数据格式为json, 同时提供异步接口。
*

* 详情见: https://redkale.org * * @author zhangjx */ public class HttpResponse extends Response { private static final ByteBuffer buffer304 = ByteBuffer.wrap("HTTP/1.1 304 Not Modified\r\nContent-Length:0\r\n\r\n".getBytes()).asReadOnlyBuffer(); private static final ByteBuffer buffer404 = ByteBuffer.wrap("HTTP/1.1 404 Not Found\r\nContent-Length:0\r\n\r\n".getBytes()).asReadOnlyBuffer(); 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("http.response.header.server", "redkale" + "/" + Redkale.getDotedVersion()) + "\r\n").getBytes(); protected static final byte[] connectCloseBytes = "Connection: close\r\n".getBytes(); protected static final byte[] connectAliveBytes = "Connection: keep-alive\r\n".getBytes(); private static final Set options = new HashSet<>(); private static final Map httpCodes = new HashMap<>(); static { options.add(StandardOpenOption.READ); 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"); } private static final ZoneId ZONE_GMT = ZoneId.of("GMT"); private int status = 200; private String contentType = ""; private long contentLength = -1; private HttpCookie[] cookies; private boolean headsended = false; private BiFunction bufferHandler; //------------------------------------------------ private final String plainContentType; private final byte[] plainContentTypeBytes; private final String jsonContentType; private final byte[] jsonContentTypeBytes; private final DefaultAnyValue header = new DefaultAnyValue(); private final String[][] defaultAddHeaders; private final String[][] defaultSetHeaders; private final boolean autoOptions; private final Supplier dateSupplier; private final HttpCookie defaultCookie; private final List renders; private final boolean hasRender; private final HttpRender onlyoneHttpRender; public static ObjectPool createPool(AtomicLong creatCounter, AtomicLong cycleCounter, int max, Creator creator) { return new ObjectPool<>(creatCounter, cycleCounter, max, creator, (x) -> ((HttpResponse) x).prepare(), (x) -> ((HttpResponse) x).recycle()); } public HttpResponse(HttpContext context, HttpRequest request, HttpResponseConfig config) { super(context, request); this.plainContentType = config.plainContentType == null || config.plainContentType.isEmpty() ? "text/plain; charset=utf-8" : config.plainContentType; this.jsonContentType = config.jsonContentType == null || config.jsonContentType.isEmpty() ? "application/json; charset=utf-8" : config.jsonContentType; this.plainContentTypeBytes = ("Content-Type: " + this.plainContentType + "\r\n").getBytes(); this.jsonContentTypeBytes = ("Content-Type: " + this.jsonContentType + "\r\n").getBytes(); this.defaultAddHeaders = config.defaultAddHeaders; this.defaultSetHeaders = config.defaultSetHeaders; this.defaultCookie = config.defaultCookie; this.autoOptions = config.autoOptions; this.dateSupplier = config.dateSupplier; this.renders = config.renders; this.hasRender = renders != null && !renders.isEmpty(); this.onlyoneHttpRender = renders != null && renders.size() == 1 ? renders.get(0) : null; this.contentType = this.plainContentType; } @Override protected AsyncConnection removeChannel() { return super.removeChannel(); } protected AsyncConnection getChannel() { return channel; } @Override protected boolean recycle() { this.status = 200; this.contentLength = -1; this.contentType = null; this.cookies = null; this.headsended = false; this.header.clear(); this.bufferHandler = null; return super.recycle(); } @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; } @Override protected void offerBuffer(ByteBuffer... buffers) { super.offerBuffer(buffers); } /** * 增加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 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, forece to close channel. request = " + request + ", result is CompletionHandler", (Throwable) t); finish(500, null); }); } /** * 创建CompletionHandler子类的实例
* * 传入的CompletionHandler子类必须是public,且保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数。 * * @param 泛型 * @param handlerClass CompletionHandler子类 * * @return CompletionHandler */ @SuppressWarnings("unchecked") public H createAsyncHandler(Class handlerClass) { if (handlerClass == null || handlerClass == CompletionHandler.class) return (H) createAsyncHandler(); return context.loadAsyncHandlerCreator(handlerClass).create(createAsyncHandler()); } /** * 获取ByteBuffer生成器 * * @return ByteBuffer生成器 */ public Supplier getBufferSupplier() { return getBodyBufferSupplier(); } /** * 将对象以JSON格式输出 * * @param obj 输出对象 */ public void finishJson(final Object obj) { this.contentType = this.jsonContentType; if (this.recycleListener != null) this.output = obj; finish(request.getJsonConvert().convertTo(getBodyBufferSupplier(), obj)); } /** * 将对象数组用Map的形式以JSON格式输出
* 例如: finishMap("a",2,"b",3) 输出结果为 {"a":2,"b":3} * * @param objs 输出对象 */ public void finishMapJson(final Object... objs) { this.contentType = this.jsonContentType; if (this.recycleListener != null) this.output = objs; finish(request.getJsonConvert().convertMapTo(getBodyBufferSupplier(), objs)); } /** * 将对象以JSON格式输出 * * @param convert 指定的JsonConvert * @param obj 输出对象 */ public void finishJson(final JsonConvert convert, final Object obj) { this.contentType = this.jsonContentType; if (this.recycleListener != null) this.output = obj; finish(convert.convertTo(getBodyBufferSupplier(), obj)); } /** * 将对象数组用Map的形式以JSON格式输出
* 例如: finishMap("a",2,"b",3) 输出结果为 {"a":2,"b":3} * * @param convert 指定的JsonConvert * @param objs 输出对象 */ public void finishMapJson(final JsonConvert convert, final Object... objs) { this.contentType = this.jsonContentType; if (this.recycleListener != null) this.output = objs; finish(convert.convertMapTo(getBodyBufferSupplier(), objs)); } /** * 将对象以JSON格式输出 * * @param type 指定的类型 * @param obj 输出对象 */ public void finishJson(final Type type, final Object obj) { this.contentType = this.jsonContentType; this.output = obj; finish(request.getJsonConvert().convertTo(getBodyBufferSupplier(), type, obj)); } /** * 将对象以JSON格式输出 * * @param convert 指定的JsonConvert * @param type 指定的类型 * @param obj 输出对象 */ public void finishJson(final JsonConvert convert, final Type type, final Object obj) { this.contentType = this.jsonContentType; if (this.recycleListener != null) this.output = obj; finish(convert.convertTo(getBodyBufferSupplier(), type, obj)); } /** * 将对象以JSON格式输出 * * @param objs 输出对象 */ public void finishJson(final Object... objs) { this.contentType = this.jsonContentType; if (this.recycleListener != null) this.output = objs; finish(request.getJsonConvert().convertTo(getBodyBufferSupplier(), objs)); } /** * 将RetResult对象以JSON格式输出 * * @param ret RetResult输出对象 */ public void finishJson(final org.redkale.service.RetResult ret) { this.contentType = this.jsonContentType; 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()); } finish(request.getJsonConvert().convertTo(getBodyBufferSupplier(), ret)); } /** * 将RetResult对象以JSON格式输出 * * @param convert 指定的JsonConvert * @param ret RetResult输出对象 */ public void finishJson(final JsonConvert convert, final org.redkale.service.RetResult ret) { this.contentType = this.jsonContentType; 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()); } finish(convert.convertTo(getBodyBufferSupplier(), ret)); } /** * 将CompletableFuture的结果对象以JSON格式输出 * * @param future 输出对象的句柄 */ public void finishJson(final CompletableFuture future) { finish(request.getJsonConvert(), (Type) null, future); } /** * 将CompletableFuture的结果对象以JSON格式输出 * * @param convert 指定的JsonConvert * @param future 输出对象的句柄 */ @SuppressWarnings("unchecked") public void finishJson(final JsonConvert convert, final CompletableFuture future) { finish(convert, (Type) null, future); } /** * 将CompletableFuture的结果对象以JSON格式输出 * * @param convert 指定的JsonConvert * @param type 指定的类型 * @param future 输出对象的句柄 */ @SuppressWarnings("unchecked") public void finishJson(final JsonConvert convert, final Type type, final CompletableFuture future) { finish(convert, type, future); } /** * 将结果对象输出 * * @param obj 输出对象 */ @SuppressWarnings("unchecked") public void finish(final Object obj) { finish(request.getJsonConvert(), (Type) null, obj); } /** * 将结果对象输出 * * @param convert 指定的Convert * @param obj 输出对象 */ @SuppressWarnings("unchecked") public void finish(final Convert convert, final Object obj) { finish(convert, (Type) null, obj); } /** * 将结果对象输出 * * @param convert 指定的Convert * @param type 指定的类型 * @param obj 输出对象 */ @SuppressWarnings("unchecked") public void finish(final Convert convert, final Type type, final Object obj) { if (obj == null) { finish("null"); } else if (obj instanceof CompletableFuture) { ((CompletableFuture) obj).whenComplete((v, e) -> { if (e != null) { context.getLogger().log(Level.WARNING, "Servlet occur, forece to close channel. request = " + request + ", result is CompletableFuture", (Throwable) e); finish(500, null); return; } finish(convert, type, v); }); } else if (obj instanceof CharSequence) { finish((String) obj.toString()); } else if (obj instanceof byte[]) { finish((byte[]) obj); } else if (obj instanceof ByteBuffer) { finish((ByteBuffer) obj); } else if (obj instanceof ByteBuffer[]) { finish((ByteBuffer[]) obj); } else if (obj instanceof File) { try { finish((File) obj); } catch (IOException e) { context.getLogger().log(Level.WARNING, "HttpServlet finish File occur, forece to close channel. request = " + getRequest(), e); finish(500, null); } } else if (obj instanceof HttpResult) { HttpResult result = (HttpResult) obj; if (result.getContentType() != null) setContentType(result.getContentType()); addHeader(result.getHeaders()).addCookie(result.getCookies()).setStatus(result.getStatus() < 1 ? 200 : result.getStatus()); if (result.getResult() == null) { finish(""); } else if (result.getResult() instanceof CharSequence) { finish(result.getResult().toString()); } else { finish(convert, result.getResult()); } } else { if (hasRender) { if (onlyoneHttpRender != null) { if (onlyoneHttpRender.getType().isAssignableFrom(obj.getClass())) { onlyoneHttpRender.renderTo(this.request, this, convert, obj); return; } } else { Class objt = obj.getClass(); for (HttpRender render : this.renders) { if (render.getType().isAssignableFrom(objt)) { render.renderTo(this.request, this, convert, obj); return; } } } } if (convert instanceof JsonConvert) { this.contentType = this.jsonContentType; } else if (convert instanceof TextConvert) { this.contentType = this.plainContentType; } if (this.recycleListener != null) this.output = obj; if (obj instanceof org.redkale.service.RetResult) { org.redkale.service.RetResult ret = (org.redkale.service.RetResult) obj; if (!ret.isSuccess()) { this.header.addValue("retcode", String.valueOf(ret.getRetcode())).addValue("retinfo", ret.getRetinfo()); } } ByteBuffer[] buffers = type == null ? convert.convertTo(getBodyBufferSupplier(), obj) : convert.convertTo(getBodyBufferSupplier(), type, obj); finish(buffers); } } /** * 将指定字符串以响应结果输出 * * @param obj 输出内容 */ public void finish(String obj) { if (isClosed()) return; if (this.recycleListener != null) this.output = obj; if (obj == null || obj.isEmpty()) { this.contentLength = 0; final ByteBuffer headbuf = createHeader(); headbuf.flip(); super.finish(headbuf); return; } if (context.getCharset() == null) { if (bufferHandler != null) { bufferHandler.apply(this, new ByteBuffer[]{ByteBuffer.wrap(Utility.encodeUTF8(obj))}); } final char[] chars = Utility.charArray(obj); this.contentLength = Utility.encodeUTF8Length(chars); final ByteBuffer headbuf = createHeader(); ByteBuffer buf2 = Utility.encodeUTF8(headbuf, (int) this.contentLength, chars); headbuf.flip(); if (buf2 == null) { super.finish(headbuf); } else { super.finish(headbuf, buf2); } } else { ByteBuffer buffer = context.getCharset().encode(obj); if (bufferHandler != null) { ByteBuffer[] bufs = bufferHandler.apply(this, new ByteBuffer[]{buffer}); if (bufs != null) buffer = bufs[0]; } this.contentLength = buffer.remaining(); final ByteBuffer headbuf = createHeader(); headbuf.flip(); super.finish(headbuf, buffer); } } /** * 以指定响应码附带内容输出 * * @param status 响应码 * @param message 输出内容 */ public void finish(int status, String message) { if (isClosed()) return; this.status = status; if (status != 200) super.refuseAlive(); finish(message); } /** * 以304状态码输出 */ public void finish304() { super.finish(buffer304.duplicate()); } /** * 以404状态码输出 */ public void finish404() { super.finish(buffer404.duplicate()); } /** * 将指定byte[]按响应结果输出 * * @param bs 输出内容 */ @Override public void finish(final byte[] bs) { this.finish(this.contentType, bs); } /** * 将指定byte[]按响应结果输出 * * @param contentType ContentType * @param bs 输出内容 */ public void finish(final String contentType, final byte[] bs) { if (isClosed()) return; //避免重复关闭 final byte[] content = bs == null ? new byte[0] : bs; if (!this.headsended) { this.contentType = contentType; this.contentLength = content.length; ByteBuffer headbuf = createHeader(); if (headbuf.remaining() >= content.length) { headbuf.put(content); headbuf.flip(); super.finish(false, headbuf); } else { headbuf.flip(); super.finish(false, new ByteBuffer[]{headbuf, ByteBuffer.wrap(content)}); } } else { if (this.context.getBufferCapacity() >= content.length) { ByteBuffer buffer = getBodyBufferSupplier().get(); buffer.put(content); buffer.flip(); this.finish(false, buffer); } else { this.finish(false, ByteBuffer.wrap(content)); } } } /** * 将指定ByteBuffer按响应结果输出 * * @param buffer 输出内容 */ @Override public void finish(ByteBuffer buffer) { finish(false, buffer); } /** * 将指定ByteBuffer按响应结果输出 * * @param kill 输出后是否强制关闭连接 * @param buffer 输出内容 */ @Override public void finish(boolean kill, ByteBuffer buffer) { if (isClosed()) return; //避免重复关闭 if (!this.headsended) { this.contentLength = buffer == null ? 0 : buffer.remaining(); ByteBuffer headbuf = createHeader(); headbuf.flip(); if (buffer == null) { super.finish(kill, headbuf); } else { super.finish(kill, new ByteBuffer[]{headbuf, buffer}); } } else { super.finish(kill, buffer); } } /** * 将指定ByteBuffer数组按响应结果输出 * * @param buffers 输出内容 */ @Override public void finish(ByteBuffer... buffers) { finish(false, buffers); } /** * 将指定ByteBuffer数组按响应结果输出 * * @param kill 输出后是否强制关闭连接 * @param buffers 输出内容 */ @Override public void finish(boolean kill, ByteBuffer... buffers) { if (isClosed()) return; //避免重复关闭 if (bufferHandler != null) { ByteBuffer[] bufs = bufferHandler.apply(this, buffers); if (bufs != null) buffers = bufs; } if (kill) refuseAlive(); if (!this.headsended) { long len = 0; for (ByteBuffer buf : buffers) { len += buf.remaining(); } this.contentLength = len; ByteBuffer headbuf = createHeader(); headbuf.flip(); if (buffers == null) { super.finish(kill, headbuf); } else { ByteBuffer[] newbuffers = new ByteBuffer[buffers.length + 1]; newbuffers[0] = headbuf; System.arraycopy(buffers, 0, newbuffers, 1, buffers.length); super.finish(kill, newbuffers); } } else { super.finish(kill, buffers); } } /** * 异步输出指定内容 * * @param 泛型 * @param buffer 输出内容 * @param attachment 异步回调参数 * @param handler 异步回调函数 */ public void sendBody(ByteBuffer buffer, A attachment, CompletionHandler handler) { if (!this.headsended) { if (this.contentLength < 0) this.contentLength = buffer == null ? 0 : buffer.remaining(); ByteBuffer headbuf = createHeader(); headbuf.flip(); if (buffer == null) { super.send(headbuf, attachment, handler); } else { super.send(new ByteBuffer[]{headbuf, buffer}, attachment, handler); } } else { super.send(buffer, attachment, handler); } } /** * 异步输出指定内容 * * @param 泛型 * @param buffers 输出内容 * @param attachment 异步回调参数 * @param handler 异步回调函数 */ public void sendBody(ByteBuffer[] buffers, A attachment, CompletionHandler handler) { if (!this.headsended) { if (this.contentLength < 0) { int len = 0; if (buffers != null && buffers.length > 0) { for (ByteBuffer b : buffers) { len += b.remaining(); } } this.contentLength = len; } ByteBuffer headbuf = createHeader(); headbuf.flip(); if (buffers == null || buffers.length == 0) { super.send(headbuf, attachment, handler); } else { super.send(Utility.unshift(buffers, headbuf), attachment, handler); } } else { super.send(buffers, attachment, 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, ByteBuffer 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, ByteBuffer fileBody) throws IOException { if ((file == null || !file.isFile() || !file.canRead()) && fileBody == null) { finish404(); return; } if (fileBody != null) fileBody = fileBody.duplicate().asReadOnlyBuffer(); final long length = file == null ? fileBody.remaining() : 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 (filename != null && !filename.isEmpty() && file != null) { addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8")); } this.contentType = MimeType.getByFilename(filename == null || filename.isEmpty() ? 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); ByteBuffer hbuffer = createHeader(); hbuffer.flip(); if (fileBody == null) { if (this.recycleListener != null) this.output = file; finishFile(hbuffer, file, start, len); } else { if (start >= 0) { fileBody.position((int) start); if (len > 0) fileBody.limit((int) (fileBody.position() + len)); } if (this.recycleListener != null) this.output = fileBody; super.finish(hbuffer, fileBody); } } private void finishFile(ByteBuffer hbuffer, File file, long offset, long length) throws IOException { this.channel.write(hbuffer, hbuffer, new TransferFileHandler(file, offset, length)); } //Header大小不能超过一个ByteBuffer的容量 protected ByteBuffer createHeader() { this.headsended = true; ByteBuffer buffer = this.pollWriteReadBuffer(); if (this.status == 200) { buffer.put(status200Bytes); } else { buffer.put(("HTTP/1.1 " + this.status + " " + httpCodes.get(this.status) + "\r\n").getBytes()); } if (this.contentLength >= 0) buffer.put(("Content-Length: " + this.contentLength + "\r\n").getBytes()); if (!this.request.isWebSocket()) { if (this.contentType == this.jsonContentType) { buffer.put(this.jsonContentTypeBytes); } else if (this.contentType == null || this.contentType == this.plainContentType) { buffer.put(this.plainContentTypeBytes); } else { buffer.put(("Content-Type: " + (this.contentType == null ? this.plainContentType : this.contentType) + "\r\n").getBytes()); } } buffer.put(serverNameBytes); if (dateSupplier != null) buffer.put(dateSupplier.get()); if (this.header.getValue("Connection") == null) { buffer.put(this.request.isKeepAlive() ? connectAliveBytes : connectCloseBytes); } 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 en : this.header.getStringEntrys()) { buffer.put((en.name + ": " + en.getValue() + "\r\n").getBytes()); } if (request.newsessionid != null) { String domain = defaultCookie == null ? null : defaultCookie.getDomain(); if (domain == null) { domain = ""; } else { domain = "Domain=" + domain + "; "; } String path = defaultCookie == null ? null : defaultCookie.getPath(); if (path == null || path.isEmpty()) path = "/"; if (request.newsessionid.isEmpty()) { buffer.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=; " + domain + "Path=" + path + "; Max-Age=0; HttpOnly\r\n").getBytes()); } else { buffer.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=" + request.newsessionid + "; " + domain + "Path=" + 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()); } buffer.put(("Set-Cookie: " + genString(cookie) + "\r\n").getBytes()); } } buffer.put(LINE); return buffer; } private CharSequence genString(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; } /** * 跳过header的输出 * 通常应用场景是,调用者的输出内容里已经包含了HTTP的响应头信息,因此需要调用此方法避免重复输出HTTP响应头信息。 * * @return HttpResponse */ public HttpResponse skipHeader() { this.headsended = true; return this; } protected DefaultAnyValue duplicateHeader() { return this.header.duplicate(); } /** * 设置Header值 * * @param name header名 * @param value header值 * * @return HttpResponse */ public HttpResponse setHeader(String name, Object value) { this.header.setValue(name, String.valueOf(value)); 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)); return this; } /** * 添加Header值 * * @param map header值 * * @return HttpResponse */ public HttpResponse addHeader(Map map) { if (map == null || map.isEmpty()) return this; for (Map.Entry en : map.entrySet()) { this.header.addValue(en.getKey(), String.valueOf(en.getValue())); } 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 BiFunction getBufferHandler() { return bufferHandler; } /** * 设置输出时的拦截器 * * @param bufferHandler 拦截器 */ protected void setBufferHandler(BiFunction bufferHandler) { this.bufferHandler = bufferHandler; } protected final class TransferFileHandler implements CompletionHandler { 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, ((HttpContext) context).getExecutor()); 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, ((HttpContext) context).getExecutor()); this.readpos = offset <= 0 ? 0 : offset; this.max = len <= 0 ? file.length() : len; } @Override public void completed(Integer result, ByteBuffer 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, ByteBuffer attachment) { context.offerBuffer(attachment); finish(true); try { filechannel.close(); } catch (IOException e) { } } } public static class HttpResponseConfig { public String plainContentType; public String jsonContentType; public String[][] defaultAddHeaders; public String[][] defaultSetHeaders; public HttpCookie defaultCookie; public boolean autoOptions; public Supplier dateSupplier; public List< HttpRender> renders; @Override public String toString() { return JsonConvert.root().convertTo(this); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy