org.voovan.http.server.WebServerHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of voovan-web Show documentation
Show all versions of voovan-web Show documentation
Voovan is a java framwork and it not depends on any third-party framework.
package org.voovan.http.server;
import org.voovan.Global;
import org.voovan.http.HttpSessionParam;
import org.voovan.http.HttpRequestType;
import org.voovan.http.message.HttpParser;
import org.voovan.http.message.HttpStatic;
import org.voovan.http.message.Request;
import org.voovan.http.server.context.WebContext;
import org.voovan.http.server.context.WebServerConfig;
import org.voovan.http.server.exception.RouterNotFound;
import org.voovan.http.websocket.WebSocketFrame;
import org.voovan.http.websocket.WebSocketTools;
import org.voovan.network.IoHandler;
import org.voovan.network.IoSession;
import org.voovan.network.exception.SendMessageException;
import org.voovan.tools.ByteBufferChannel;
import org.voovan.tools.exception.MemoryReleasedException;
import org.voovan.tools.hashwheeltimer.HashWheelTask;
import org.voovan.tools.log.Logger;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Vector;
/**
* WebServer Socket 事件处理类
*
* @author helyho
*
* Voovan Framework.
* WebSite: https://github.com/helyho/Voovan
* Licence: Apache v2 License
*/
public class WebServerHandler implements IoHandler {
ThreadLocal THREAD_HTTP_REQUEST = new ThreadLocal();
ThreadLocal THREAD_HTTP_RESPONSE = new ThreadLocal();
private HttpDispatcher httpDispatcher;
private WebSocketDispatcher webSocketDispatcher;
private WebServerConfig webConfig;
private List keepAliveSessionList;
public WebServerHandler(WebServerConfig webConfig, HttpDispatcher httpDispatcher, WebSocketDispatcher webSocketDispatcher) {
this.httpDispatcher = httpDispatcher;
this.webSocketDispatcher = webSocketDispatcher;
this.webConfig = webConfig;
keepAliveSessionList = new Vector();
initKeepAliveTimer();
}
/**
* 获取属性
* @param session 会话对象
* @param sessionParam 会话参数枚举
* @param 范型
* @return 参数
*/
public static T getAttribute(IoSession session, HttpSessionParam sessionParam){
return (T)session.getAttribute(sessionParam);
}
/**
*
* @param session 会话对象
* @param sessionParam 会话参数枚举
* @param value 参数
* @param 范型
*/
public static void setAttribute(IoSession session, HttpSessionParam sessionParam, T value){
session.setAttribute(sessionParam, value);
}
/**
* 初始化连接保持 Timer
*/
public void initKeepAliveTimer(){
Global.getHashWheelTimer().addTask(new HashWheelTask() {
@Override
public void run() {
long currentTimeValue = System.currentTimeMillis();
//遍历所有的 session
for(int i=0; i webConfig.getGzipMinSize()){
//检查 MimeType 是否启用 gzip
for(String gzipMimeType : webConfig.getGzipMimeType()){
if(httpResponse.header().get(HttpStatic.CONTENT_TYPE_STRING).contains(gzipMimeType)){
httpResponse.setCompress(true);
}
}
}
}
// 处理响应请求
httpDispatcher.process(httpRequest, httpResponse);
if (WebContext.isCache()) {
Long mark = httpRequest.getMark();
if (mark != null) {
Integer bodyMark = httpResponse.body().getMark();
if (bodyMark != null) {
httpResponse.setMark(mark << 32 >> 32 | bodyMark);
}
}
}
return httpResponse;
}
/**
* Http协议升级处理
*
* @param session HTTP-Session 对象
* @param httpRequest HTTP 请求对象
* @param httpResponse HTTP 响应对象
* @return HTTP 响应对象
*/
private static String upgradeStatusCode = "Switching Protocols";
public HttpResponse disposeUpgrade(IoSession session, HttpRequest httpRequest, HttpResponse httpResponse) {
//如果不是匹配的路由则关闭连接
if(webSocketDispatcher.findRouter(httpRequest)!=null){
setAttribute(session, HttpSessionParam.TYPE, HttpRequestType.UPGRADE);
//初始化响应消息
httpResponse.protocol().setStatus(101);
httpResponse.protocol().setStatusCode(upgradeStatusCode);
httpResponse.header().put(HttpStatic.CONNECTION_STRING, HttpStatic.UPGRADE_STRING);
if(httpRequest.header()!=null && HttpStatic.WEB_SOCKET_STRING.equals(httpRequest.header().get(HttpStatic.UPGRADE_STRING))){
httpResponse.header().put(HttpStatic.UPGRADE_STRING, HttpStatic.WEB_SOCKET_STRING);
String webSocketKey = WebSocketTools.generateSecKey(httpRequest.header().get(HttpStatic.SEC_WEB_SOCKET_KEY_STRING));
httpResponse.header().put(HttpStatic.SEC_WEB_SOCKET_ACCEPT_STRING, webSocketKey);
}
else if(httpRequest.header()!=null && "h2c".equals(httpRequest.header().get(HttpStatic.UPGRADE_STRING))){
httpResponse.header().put(HttpStatic.UPGRADE_STRING, "h2c");
//这里写 HTTP2的实现,暂时留空
}
} else {
httpDispatcher.exceptionMessage(httpRequest, httpResponse, new RouterNotFound("Not avaliable router!"));
}
resetThreadLocal();
return httpResponse;
}
/**
* WebSocket 帧处理
*
* @param session HTTP-Session 对象
* @param webSocketFrame WebSocket 帧对象
* @return WebSocket 帧对象
*/
public synchronized WebSocketFrame disposeWebSocket(IoSession session, WebSocketFrame webSocketFrame) {
ByteBufferChannel byteBufferChannel = null;
if(!session.containAttribute("WebSocketByteBufferChannel")){
byteBufferChannel = new ByteBufferChannel(session.socketContext().getReadBufferSize());
session.setAttribute("WebSocketByteBufferChannel",byteBufferChannel);
}else{
byteBufferChannel = (ByteBufferChannel)session.getAttribute("WebSocketByteBufferChannel");
}
HttpRequest reqWebSocket = getAttribute(session, HttpSessionParam.HTTP_REQUEST);
// WS_CLOSE 如果收到关闭帧则关闭连接
if(webSocketFrame.getOpcode() == WebSocketFrame.Opcode.CLOSING) {
return WebSocketFrame.newInstance(true, WebSocketFrame.Opcode.CLOSING, false, webSocketFrame.getFrameData());
}
// WS_PING 收到 ping 帧则返回 pong 帧
else if(webSocketFrame.getOpcode() == WebSocketFrame.Opcode.PING) {
return webSocketDispatcher.firePingEvent(session, reqWebSocket, webSocketFrame.getFrameData());
}
// WS_PING 收到 pong 帧则返回 ping 帧
else if(webSocketFrame.getOpcode() == WebSocketFrame.Opcode.PONG) {
refreshTimeout(session);
webSocketDispatcher.firePoneEvent(session, reqWebSocket, webSocketFrame.getFrameData());
return null;
}else if(webSocketFrame.getOpcode() == WebSocketFrame.Opcode.CONTINUOUS){
byteBufferChannel.writeEnd(webSocketFrame.getFrameData());
}
// WS_RECIVE 文本和二进制消息出发 Recived 事件
else if (webSocketFrame.getOpcode() == WebSocketFrame.Opcode.TEXT || webSocketFrame.getOpcode() == WebSocketFrame.Opcode.BINARY) {
byteBufferChannel.writeEnd(webSocketFrame.getFrameData());
WebSocketFrame respWebSocketFrame = null;
//判断解包是否有错
if(webSocketFrame.getErrorCode()==0){
try {
respWebSocketFrame = webSocketDispatcher.fireReceivedEvent(session, reqWebSocket, byteBufferChannel.getByteBuffer());
} finally {
byteBufferChannel.compact();
byteBufferChannel.clear();
}
}else{
//解析时出现异常,返回关闭消息
respWebSocketFrame = WebSocketFrame.newInstance(true, WebSocketFrame.Opcode.CLOSING, false, ByteBuffer.wrap(WebSocketTools.intToByteArray(webSocketFrame.getErrorCode(), 2)));
}
return respWebSocketFrame;
}
return null;
}
private void refreshTimeout(IoSession session){
int keepAliveTimeout = webConfig.getKeepAliveTimeout();
long timeoutValue = System.currentTimeMillis()+keepAliveTimeout*1000;
setAttribute(session, HttpSessionParam.KEEP_ALIVE_TIMEOUT, timeoutValue);
}
@Override
public void onSent(IoSession session, Object obj) {
HttpRequest request = getAttribute(session,HttpSessionParam.HTTP_REQUEST);
//WebSocket 协议处理
if(obj instanceof WebSocketFrame){
WebSocketFrame webSocketFrame = (WebSocketFrame)obj;
if(webSocketFrame.getOpcode() == WebSocketFrame.Opcode.CLOSING){
session.close();
} else if (webSocketFrame.getOpcode() != WebSocketFrame.Opcode.PING &&
webSocketFrame.getOpcode() != WebSocketFrame.Opcode.PONG) {
webSocketDispatcher.fireSentEvent(session, request, webSocketFrame.getFrameData());
}
}
//针对 WebSocket 的处理协议升级
if(HttpRequestType.UPGRADE.equals(getAttribute(session, HttpSessionParam.TYPE))){
setAttribute(session, HttpSessionParam.TYPE, HttpRequestType.WEBSOCKET);
setAttribute(session, HttpSessionParam.KEEP_ALIVE, true);
//触发 onOpen 事件
WebSocketFrame webSocketFrame = webSocketDispatcher.fireOpenEvent(session, request);
if(webSocketFrame!=null) {
try {
session.syncSend(webSocketFrame);
} catch (SendMessageException e) {
session.close();
Logger.error("WebSocket Open event send frame error", e);
}
}
//发送第一次心跳消息
Global.getHashWheelTimer().addTask(new HashWheelTask() {
@Override
public void run() {
//发送 ping 消息
try {
WebSocketFrame ping = WebSocketFrame.newInstance(true, WebSocketFrame.Opcode.PING, false, null);
session.send(ping.toByteBuffer());
} catch (Exception e) {
session.close();
Logger.error("WebSocket send Ping frame error", e);
}finally {
this.cancel();
}
}
}, session.socketContext().getReadTimeout()/3/1000);
}
}
@Override
public void onFlush(IoSession session, List
© 2015 - 2024 Weber Informatics LLC | Privacy Policy