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

io.cettia.asity.bridge.netty4.AsityServerCodec Maven / Gradle / Ivy

There is a newer version: 3.0.0
Show newest version
/*
 * Copyright 2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.cettia.asity.bridge.netty4;

import io.cettia.asity.action.Action;
import io.cettia.asity.action.Actions;
import io.cettia.asity.action.ConcurrentActions;
import io.cettia.asity.http.ServerHttpExchange;
import io.cettia.asity.websocket.ServerWebSocket;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.timeout.ReadTimeoutHandler;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * ChannelHandler to process {@link HttpRequest} and {@link HttpResponse} into
 * {@link NettyServerHttpExchange} and {@link NettyServerWebSocket}. When you
 * configure handler, you must add {@link HttpServerCodec} in
 * front of this handler.
 * 

*

*

 *
 * ChannelPipeline pipeline = ch.pipeline();
 * pipeline.addLast(new HttpServerCodec())
 * .addLast(new AsityServerCodec() {
 *     {@literal @}Override
 *     protected boolean accept(HttpRequest req) {
 *         return URI.create(req.getUri()).getPath().equals("/cettia");
 *     }
 * }
 * .onhttp(http -> {}).onwebsocket(ws -> {}));
 * 
* * @author Donghwan Kim */ public class AsityServerCodec extends ChannelInboundHandlerAdapter { private Actions httpActions = new ConcurrentActions<>(); private Actions wsActions = new ConcurrentActions<>(); private Map httpMap = new ConcurrentHashMap<>(); private Map wsMap = new ConcurrentHashMap<>(); private Map wsReqMap = new ConcurrentHashMap<>(); @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { if (msg instanceof HttpRequest) { HttpRequest req = (HttpRequest) msg; if (!accept(req)) { ctx.fireChannelRead(msg); return; } if (req.getMethod() == HttpMethod.GET && req.headers().contains(HttpHeaders.Names.UPGRADE, HttpHeaders.Values.WEBSOCKET, true)) { // Because WebSocketServerHandshaker requires FullHttpRequest FullHttpRequest wsRequest = new DefaultFullHttpRequest(req.getProtocolVersion(), req .getMethod(), req.getUri()); wsRequest.headers().set(req.headers()); wsReqMap.put(ctx.channel(), wsRequest); // Set timeout to avoid memory leak ctx.pipeline().addFirst(new ReadTimeoutHandler(5)); } else { NettyServerHttpExchange http = new NettyServerHttpExchange(ctx, req); httpMap.put(ctx.channel(), http); httpActions.fire(http); } } else if (msg instanceof HttpContent) { FullHttpRequest wsReq = wsReqMap.get(ctx.channel()); if (wsReq != null) { wsReq.content().writeBytes(((HttpContent) msg).content()); if (msg instanceof LastHttpContent) { wsReqMap.remove(ctx.channel()); // Cancel timeout ctx.pipeline().remove(ReadTimeoutHandler.class); WebSocketServerHandshakerFactory factory = new WebSocketServerHandshakerFactory (getWebSocketLocation(ctx.pipeline(), wsReq), null, true); WebSocketServerHandshaker handshaker = factory.newHandshaker(wsReq); if (handshaker == null) { WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); } else { handshaker.handshake(ctx.channel(), wsReq); NettyServerWebSocket ws = new NettyServerWebSocket(ctx, wsReq, handshaker); wsMap.put(ctx.channel(), ws); wsActions.fire(ws); } } } else { NettyServerHttpExchange http = httpMap.get(ctx.channel()); if (http != null) { http.handleChunk((HttpContent) msg); } } } else if (msg instanceof WebSocketFrame) { NettyServerWebSocket ws = wsMap.get(ctx.channel()); if (ws != null) { ws.handleFrame((WebSocketFrame) msg); } } } /** * Whether to process this request or not. By default, it accepts every * request. */ protected boolean accept(HttpRequest req) { return true; } private String getWebSocketLocation(ChannelPipeline pipeline, HttpRequest req) { return (pipeline.get(SslHandler.class) == null ? "ws://" : "wss://") + HttpHeaders.getHost (req) + req.getUri(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { NettyServerHttpExchange http = httpMap.get(ctx.channel()); if (http != null) { http.handleError(cause); } NettyServerWebSocket ws = wsMap.get(ctx.channel()); if (ws != null) { ws.handleError(cause); } ctx.close(); } @Override public void channelInactive(ChannelHandlerContext ctx) { NettyServerHttpExchange http = httpMap.remove(ctx.channel()); if (http != null) { http.handleClose(); } NettyServerWebSocket ws = wsMap.remove(ctx.channel()); if (ws != null) { ws.handleClose(); } wsReqMap.remove(ctx.channel()); } /** * Registers an action to be called when {@link ServerHttpExchange} is * available. */ public AsityServerCodec onhttp(Action action) { httpActions.add(action); return this; } /** * Registers an action to be called when {@link ServerWebSocket} is * available. */ public AsityServerCodec onwebsocket(Action action) { wsActions.add(action); return this; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy