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

org.voovan.http.message.Response Maven / Gradle / Ivy

package org.voovan.http.message;

import org.voovan.http.message.packet.Body;
import org.voovan.http.message.packet.Cookie;
import org.voovan.http.message.packet.Header;
import org.voovan.http.message.packet.ResponseProtocol;
import org.voovan.http.server.context.WebContext;
import org.voovan.network.IoSession;
import org.voovan.tools.FastThreadLocal;
import org.voovan.tools.TByte;
import org.voovan.tools.buffer.ByteBufferChannel;
import org.voovan.tools.buffer.TByteBuffer;
import org.voovan.tools.TString;
import org.voovan.tools.exception.MemoryReleasedException;
import org.voovan.tools.log.Logger;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

/**
 * HTTP 响应对象
 *
 * @author helyho
 *
 * Voovan Framework.
 * WebSite: https://github.com/helyho/Voovan
 * Licence: Apache v2 License
 */
public class Response {
	private static FastThreadLocal THREAD_STRING_BUILDER = FastThreadLocal.withInitial(()->new StringBuilder(512));

	private ResponseProtocol 	protocol;
	private Header				header;
	private List		cookies;
	private Body 				body;
	private boolean				isCompress;
	private boolean         	hasBody;
	protected boolean 			basicSend = false;
	private boolean 			async = false;
	private boolean         	cookieParsed = false;
	private Long                mark;

	/**
	 * 构造函数
	 *
	 * @param response 响应对象
	 */
	public Response(Response response) {
		init(response);
	}

	public void init(Response response){
		this.protocol = response.protocol;
		this.header = response.header;
		this.body = response.body;
		this.cookies = response.cookies;
		this.isCompress = response.isCompress;
		this.basicSend = false;
		this.mark = response.mark;
		this.hasBody = response.hasBody;
	}

	/**
	 * 构造函数
	 */
	public Response() {
		protocol = new ResponseProtocol();
		header = new Header();
		cookies = new ArrayList();
		body = new Body();
		isCompress = false;
		this.basicSend = false;
	}

	public Long getMark() {
		return mark;
	}

	public void setMark(Long mark) {
		this.mark = mark;
	}

	/**
	 * 是否压缩 默认为 true
	 *
	 * @return 是否启用个压缩
	 */
	public boolean isCompress() {
		return isCompress;
	}

	/**
	 * 设置压缩属性
	 *
	 * @param isCompress 是否启用个压缩
	 */
	public void setCompress(boolean isCompress) {
		this.isCompress = isCompress;
	}

	/**
	 * 是否在路由异步响应
	 * @return true: 异步响应 false: 同步响应
	 */
	public boolean isAsync() {
		return async;
	}

	/**
	 * 是否在路由异步响应
	 * @param async true: 异步响应, false: 同步响应
	 */
	public void setAsync(boolean async) {
		this.async = async;
	}

	/**
	 * 获取协议对象
	 *
	 * @return 返回响应协议对象
	 */
	public ResponseProtocol protocol() {
		return protocol;
	}

	/**
	 * 获取 Header 对象
	 *
	 * @return HTTP-Header 对象
	 */
	public Header header() {
		return header;
	}

	/**
	 * 获取所有的Cookies对象,返回一个 List
	 *
	 * @return Cookie 对象集合
	 */
	public synchronized List cookies() {
		if(!cookieParsed) {
			String cookieStr = header.get("Cookie");
			if(cookieStr!=null) {
				HttpParser.parseCookie(cookies, 0, cookieStr);
			}
			cookieParsed = true;
		}
		return cookies;
	}
	/**
	 * 根据 Cookie 名称取 Cookie
	 *
	 * @param name  Cookie 名称
	 * @return Cookie
	 */
	public Cookie getCookie(String name){
		for(Cookie cookie : this.cookies()){
			if(cookie !=null && name !=null && name.equals(cookie.getName())){
				return cookie;
			}
		}
		return null;
	}

	/**
	 * 获取 Body 对象
	 *
	 * @return Body 对象
	 */
	public Body body() {
		return body;
	}

	public boolean isHasBody() {
		return hasBody;
	}

	public void setHasBody(boolean hasBody) {
		this.hasBody = hasBody;
	}

	/**
	 * 根据内容构造一写必要的 Header 属性
	 */
	private void initHeader() {

		// 根据压缩属性确定 Header 的一些属性内容
		if (body.size()!=0 && isCompress) {
			header.put(HttpStatic.TRANSFER_ENCODING_STRING, HttpStatic.CHUNKED_STRING);
			header.put(HttpStatic.CONTENT_ENCODING_STRING, HttpStatic.GZIP_STRING);
		} else {
			header.put(HttpStatic.CONTENT_LENGTH_STRING, Integer.toString((int)body.size()));
		}

		if (!header.contain(HttpStatic.CONTENT_TYPE_STRING)) {
			header.put(HttpStatic.CONTENT_TYPE_STRING, HttpStatic.TEXT_HTML_STRING  + WebContext.getWebServerConfig().getResponseCharacterSet());
		} else {
			header.put(HttpStatic.CONTENT_TYPE_STRING, header.get(HttpStatic.CONTENT_TYPE_STRING) + WebContext.getWebServerConfig().getResponseCharacterSet());
		}
	}

	/**
	 * 根据 Cookie 对象,生成 HTTP 响应中的 Cookie 字符串 用于报文拼装
	 *
	 * @return Cookie 字符串
	 */
	private String genCookie() {
		StringBuilder cookieString = new StringBuilder();
		for (Cookie cookie : cookies) {
			cookieString.append("Set-Cookie: ");
			cookieString.append(cookie.toString());
			cookieString.append(HttpStatic.LINE_MARK_STRING);
		}
		return cookieString.toString();
	}

	/**
	 * 根据对象的内容,构造 Http 响应报头
	 *
	 * @return ByteBuffer 响应报文的报头
	 */
	private byte[] readHead() {

		StringBuilder stringBuilder = THREAD_STRING_BUILDER.get();
		stringBuilder.setLength(0);

		initHeader();

		stringBuilder.append(protocol.toString());

		stringBuilder.append(header.toString());

		stringBuilder.append(genCookie());

		return TString.toAsciiBytes(stringBuilder.toString());
	}


	private byte[] readEnd(){
		if (isCompress) {
			return TString.toAsciiBytes("0" + HttpStatic.BODY_MARK_STRING);
		}else{
			return TByte.EMPTY_BYTES;
		}
	}

	/**
	 * 发送数据
	 * @param session socket 会话对象
	 * @throws IOException IO异常
	 */
	public void send(IoSession session) throws IOException {

		try {
			ByteBufferChannel byteBufferChannel = session.getSendByteBufferChannel();

			//发送报文头
			ByteBuffer byteBuffer = byteBufferChannel.getByteBuffer(); //THREAD_BYTE_BUFFER.get();

			//Socket 已断开
			if(byteBuffer==null) {
				return;
			}

			//自动扩容
			if(byteBufferChannel.available() == 0) {
				byteBufferChannel.reallocate(byteBufferChannel.capacity() + 4 * 1024);
			}

			//如果有历史数据则从历史数据尾部开始写入
			byteBuffer.position(byteBuffer.limit());
			byteBuffer.limit(byteBuffer.capacity());

			try {
				byteBuffer.put(readHead());
				byteBuffer.put(WebContext.RESPONSE_COMMON_HEADER);
			} catch (Throwable e) {
				if (!(e instanceof MemoryReleasedException)) {
					Logger.error("Response writeToChannel error: ", (Exception) e);
				}
			}

			//是否需要压缩
			if (isCompress) {
				body.compress();
			}

			//发送报文主体
			int avaliableSize = 0;
			try {
				int totalBodySize = (int) body.size();

				while ( totalBodySize > 0) {
					//预留写入 chunked 结束符的位置
					byteBuffer.limit(byteBuffer.capacity() - 10); //预留协议字节, 换行符确认4个,长度描述符1-6个
					avaliableSize = byteBuffer.remaining() > totalBodySize ? totalBodySize : byteBuffer.remaining();
					totalBodySize = totalBodySize - avaliableSize;

					//判断是否需要发送 chunked 段长度
					if (isCompress() && avaliableSize != 0) {
						String chunkedLengthLine = Integer.toHexString(avaliableSize) + HttpStatic.LINE_MARK_STRING;
						byteBuffer.put(chunkedLengthLine.getBytes());
					}

					//重置 Bytebuffer 可用字节数为 readSize
					byteBuffer.limit(byteBuffer.position() + avaliableSize);
					int bodyReadSize = body.read(byteBuffer);

					if (bodyReadSize == -1) {
						break;
					}

					//重置写入位置
					byteBuffer.position(byteBuffer.limit());
					byteBuffer.limit(byteBuffer.capacity());

					//判断是否需要发送 chunked 结束符号
					if (isCompress() && avaliableSize != 0 &&  byteBuffer.remaining() > 0) {
						byteBuffer.put(HttpStatic.LINE_MARK.getBytes());
					}

					if(byteBuffer.remaining() <= 10) {
						byteBuffer.flip();
						byteBufferChannel.compact();
						session.flush();
					}
				}

				//发送报文结束符
				byteBuffer.put(readEnd());
				byteBuffer.flip();
				byteBufferChannel.compact();
			} catch (Throwable e) {
				if (!(e instanceof MemoryReleasedException)) {
					Logger.error("Response writeToChannel error: ", (Exception) e);
				}
				return;
			}

			basicSend = true;
		} finally {
			if(async) {
				session.flush();
			}
			clear();
		}
	}

	public void release(){
		body.release();
	}

	/**
	 * 从其他 Response 复制数据到当前对象
	 * 	用于非发送目的
	 * @param response 原对象
	 * @return 赋值的对象
	 */
	public Response copyFrom(Response response) {
	    return copyFrom(response, false);
	}

	/**
	 * 从其他 Response 复制数据到当前对象
	 * @param otherResponse 原对象
	 * @param useForSend 是否用户发送
	 * @return 赋值的对象
	 */
	public Response copyFrom(Response otherResponse, boolean useForSend) {
		this.protocol().setStatus(otherResponse.protocol().getStatus());
		this.protocol().setStatusCode(otherResponse.protocol().getStatusCode());
		this.header().copyFrom(otherResponse.header());
		this.body().write(otherResponse.body().getBodyBytes());
		this.cookies().addAll(otherResponse.cookies());
		this.setCompress(otherResponse.isCompress);
		this.setMark(otherResponse.getMark());
		this.setHasBody(otherResponse.hasBody);

		if(useForSend) {
			//判断是否启用压缩
			if (HttpStatic.GZIP_STRING.equals(otherResponse.header().get(HttpStatic.CONTENT_ENCODING_STRING))) {
				this.setCompress(true);
			}

			this.header().remove("Date");
			this.header().remove("Server");
		} else {
			this.async 		= otherResponse.async;
			this.basicSend 	= otherResponse.basicSend;
		}

		return this;
	}

	/**
	 * 清理
	 */
	public void clear(){
		this.header.clear();
		this.cookies.clear();
		this.protocol.clear();
		this.body.clear();
		this.isCompress = false;
		this.basicSend = false;
		this.async = false;
		this.cookieParsed = false;
		this.mark = null;
	}

	@Override
	public String toString() {
		return new String(readHead());
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy