/** Server that accept the path of a file an echo back its content. */ public final class FileServer { static final boolean SSL = System.getProperty("ssl") != null; // Use the same default port with the telnet example so that we can use the telnet client example // to access it. static final int PORT = Integer.parseInt(System.getProperty("port", SSL ? "8992" : "8023")); public static void main(String[] args) throws Exception { // Configure SSL. final SslContext sslCtx; if (SSL) { SelfSignedCertificate ssc = new SelfSignedCertificate(); sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build(); } else { sslCtx = null; } // Configure the server. EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 100) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler( new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); if (sslCtx != null) { p.addLast(sslCtx.newHandler(ch.alloc())); } p.addLast( new StringEncoder(CharsetUtil.UTF_8), new LineBasedFrameDecoder(8192), new StringDecoder(CharsetUtil.UTF_8), new ChunkedWriteHandler(), new FileServerHandler()); } }); // Start the server. ChannelFuture f = b.bind(PORT).sync(); // Wait until the server socket is closed. f.channel().closeFuture().sync(); } finally { // Shut down all event loops to terminate all threads. bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
public static void main(final String[] args) { final int port = Integer.valueOf(System.getProperty("port", "7657")); final int bossThreads = Integer.valueOf(System.getProperty("bossThreads", "1")); final int workerThreads = Integer.valueOf(System.getProperty("workerThreads", "1")); final HelloServer server = new HelloServer(); server.start(port, bossThreads, workerThreads); Runtime.getRuntime().addShutdownHook(new Thread(server::stop)); }
public static void main(String[] args) throws Exception { try { long t0 = System.nanoTime(); for (int i = 0; i < 100000; i++) { TcpClient.sendMsg(i + "你好1"); } long t1 = System.nanoTime(); System.out.println((t1 - t0) / 1000000.0); } catch (Exception e) { e.printStackTrace(); } }
public FileChunk get() throws IOException { if (useLocalFile) { startTime = System.currentTimeMillis(); finishTime = System.currentTimeMillis(); state = TajoProtos.FetcherState.FETCH_FINISHED; return fileChunk; } this.startTime = System.currentTimeMillis(); this.state = TajoProtos.FetcherState.FETCH_FETCHING; ChannelFuture future = null; try { future = bootstrap .clone() .connect(new InetSocketAddress(host, port)) .addListener(ChannelFutureListener.CLOSE_ON_FAILURE); // Wait until the connection attempt succeeds or fails. Channel channel = future.awaitUninterruptibly().channel(); if (!future.isSuccess()) { state = TajoProtos.FetcherState.FETCH_FAILED; throw new IOException(future.cause()); } String query = uri.getPath() + (uri.getRawQuery() != null ? "?" + uri.getRawQuery() : ""); // Prepare the HTTP request. HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, query); request.headers().set(HttpHeaders.Names.HOST, host); request.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE); request.headers().set(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP); LOG.info("Status: " + getState() + ", URI:" + uri); // Send the HTTP request. channel.writeAndFlush(request); // Wait for the server to close the connection. throw exception if failed channel.closeFuture().syncUninterruptibly(); fileChunk.setLength(fileChunk.getFile().length()); return fileChunk; } finally { if (future != null && future.channel().isOpen()) { // Close the channel to exit. future.channel().close().awaitUninterruptibly(); } this.finishTime = System.currentTimeMillis(); LOG.info( "Fetcher finished:" + (finishTime - startTime) + " ms, " + getState() + ", URI:" + uri); } }
@Override public void channelActive(@NotNull ChannelHandlerContext ctx) { startTime = System.nanoTime(); ctx.writeAndFlush(firstMessage); System.out.print("Running throughput test ( for 10 seconds ) "); }
public void onMessage(Message message) { if (message.getType() == MessageType.MSG && message.get(MessageProperty.ROOM).equals("#main")) { long userId = message.get(MessageProperty.USER_ID); String name = message.get(MessageProperty.NAME); UserCredentials userCredentials = null; if (needsFetchingConnectionData(userId)) { userCredentials = fetchConnectionDataForUser(name, userId); } if (userCredentials != null) { String id = userCredentials.getId(); Channel channel = connections.get(id.toLowerCase()); if (channel == null || !channel.isActive()) { try { channel = createConnection(id, userCredentials.getToken()); } catch (InterruptedException e) { logger.warn("", e); } } if (channel != null) { channel.attr(lastMessageAttrKey).set(System.currentTimeMillis()); channel.writeAndFlush( "PRIVMSG #" + this.channel + " :" + message.get(MessageProperty.TEXT) + "\r\n"); } } } }
public class TcpListenApp { static final int PORT = Integer.parseInt(System.getProperty("port", "8007")); static final String RABBIT_HOST = System.getProperty("rabbit", "localhost"); public static void main(String[] args) throws Exception { UserBroadcastService userBroadcastService = new UserBroadcastService(); RabbitService publisher = new RabbitService(userBroadcastService, RABBIT_HOST); publisher.listen(); EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); DefaultEventExecutorGroup eventExecutors = new DefaultEventExecutorGroup(20); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 100) .handler(new LoggingHandler(LogLevel.DEBUG)) .childHandler( new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); p.addLast(new GreetingHandler()); p.addLast(new LineBasedFrameDecoder(1000)); p.addLast(new UserMessageDecoder(userBroadcastService)); p.addLast(eventExecutors, new MessagePublishingHandler(publisher)); } }); // Start the server. ChannelFuture f = b.bind(PORT).sync(); // Wait until the server socket is closed. f.channel().closeFuture().sync(); } finally { // Shut down all event loops to terminate all threads. bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); publisher.close(); } } }
@Override public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { if (getState() != TajoProtos.FetcherState.FETCH_FINISHED) { // channel is closed, but cannot complete fetcher finishTime = System.currentTimeMillis(); state = TajoProtos.FetcherState.FETCH_FAILED; } IOUtils.cleanup(LOG, fc, raf); super.channelUnregistered(ctx); }
@Override public void channelRead(@NotNull ChannelHandlerContext ctx, @NotNull Object msg) { try { bytesReceived += ((ByteBuf) msg).readableBytes(); if (i++ % 10000 == 0) System.out.print("."); if (TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime) >= 10) { long time = System.nanoTime() - startTime; System.out.printf("\nThroughput was %.1f MB/s%n", 1e3 * bytesReceived / time); return; } } finally { ReferenceCountUtil.release(msg); // (2) } final ByteBuf outMsg = ctx.alloc().buffer(bufferSize); // (2) outMsg.writeBytes(payload); ctx.writeAndFlush(outMsg); // (3) }
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { if (cause instanceof ReadTimeoutException) { LOG.warn(cause.getMessage(), cause); } else { LOG.error("Fetch failed :", cause); } // this fetching will be retry IOUtils.cleanup(LOG, fc, raf); finishTime = System.currentTimeMillis(); state = TajoProtos.FetcherState.FETCH_FAILED; ctx.close(); }
public abstract static class DefaultContextImpl implements Context { private static final String durationProp = System.getProperty(DefaultContextImpl.class.getName() + ".durationMillis"); private static final long DURATION = durationProp != null ? Long.parseLong(durationProp) : 60_000L; private final ReentrantLock lock = new ReentrantLock(); private final long created; private final Map<String, Object> attachments = new HashMap<>(); private boolean valid = true; public DefaultContextImpl() { created = new Date().getTime(); } @Override public void invalidate() { attachments.clear(); valid = false; } @Override public final boolean isValid() { final boolean ret = valid && (new Date().getTime() - created) <= DURATION; if (!ret) invalidate(); return ret; } @Override public final Map<String, Object> getAttachments() { return attachments; } @Override public final ReentrantLock getLock() { return lock; } @Override public boolean watch() { return true; } }
@Override public void run() { try { logger.debug("starting cleanup"); for (Map.Entry<String, Channel> entry : connections.entrySet()) { Channel channel = entry.getValue(); Long lastMessage = channel.attr(lastMessageAttrKey).get(); if (System.currentTimeMillis() - lastMessage > FIVE_MINUTES) { channel.disconnect(); connections.remove(entry.getKey()); logger.debug("connection released for {}", entry.getKey()); } else if (!channel.isActive()) { connections.remove(entry.getKey()); logger.debug("connection released for {}", entry.getKey()); } } logger.debug("cleanup complete"); } catch (Exception e) { logger.error("exception while clean up", e); } }
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { messageReceiveCount++; if (msg instanceof HttpResponse) { try { HttpResponse response = (HttpResponse) msg; StringBuilder sb = new StringBuilder(); if (LOG.isDebugEnabled()) { sb.append("STATUS: ") .append(response.getStatus()) .append(", VERSION: ") .append(response.getProtocolVersion()) .append(", HEADER: "); } if (!response.headers().names().isEmpty()) { for (String name : response.headers().names()) { for (String value : response.headers().getAll(name)) { if (LOG.isDebugEnabled()) { sb.append(name).append(" = ").append(value); } if (this.length == -1 && name.equals("Content-Length")) { this.length = Long.parseLong(value); } } } } if (LOG.isDebugEnabled()) { LOG.debug(sb.toString()); } if (response.getStatus().code() == HttpResponseStatus.NO_CONTENT.code()) { LOG.warn("There are no data corresponding to the request"); length = 0; return; } else if (response.getStatus().code() != HttpResponseStatus.OK.code()) { LOG.error(response.getStatus().reasonPhrase()); state = TajoProtos.FetcherState.FETCH_FAILED; return; } } catch (Exception e) { LOG.error(e.getMessage(), e); } finally { ReferenceCountUtil.release(msg); } } if (msg instanceof HttpContent) { try { HttpContent httpContent = (HttpContent) msg; ByteBuf content = httpContent.content(); if (content.isReadable()) { content.readBytes(fc, content.readableBytes()); } if (msg instanceof LastHttpContent) { if (raf != null) { fileLen = file.length(); } finishTime = System.currentTimeMillis(); if (state != TajoProtos.FetcherState.FETCH_FAILED) { state = TajoProtos.FetcherState.FETCH_FINISHED; } IOUtils.cleanup(LOG, fc, raf); } } catch (Exception e) { LOG.error(e.getMessage(), e); } finally { ReferenceCountUtil.release(msg); } } }
/** @author circlespainter */ public class WebActorHandler extends SimpleChannelInboundHandler<Object> { // @FunctionalInterface public interface WebActorContextProvider { Context get(ChannelHandlerContext ctx, FullHttpRequest req); } public interface Context { boolean isValid(); void invalidate(); ActorRef<? extends WebMessage> getRef(); ReentrantLock getLock(); Map<String, Object> getAttachments(); boolean handlesWithHttp(String uri); boolean handlesWithWebSocket(String uri); boolean watch(); } public abstract static class DefaultContextImpl implements Context { private static final String durationProp = System.getProperty(DefaultContextImpl.class.getName() + ".durationMillis"); private static final long DURATION = durationProp != null ? Long.parseLong(durationProp) : 60_000L; private final ReentrantLock lock = new ReentrantLock(); private final long created; private final Map<String, Object> attachments = new HashMap<>(); private boolean valid = true; public DefaultContextImpl() { created = new Date().getTime(); } @Override public void invalidate() { attachments.clear(); valid = false; } @Override public final boolean isValid() { final boolean ret = valid && (new Date().getTime() - created) <= DURATION; if (!ret) invalidate(); return ret; } @Override public final Map<String, Object> getAttachments() { return attachments; } @Override public final ReentrantLock getLock() { return lock; } @Override public boolean watch() { return true; } } public WebActorHandler(WebActorContextProvider selector) { this(selector, null); } public WebActorHandler(WebActorContextProvider selector, String httpResponseEncoderName) { this.selector = selector; this.httpResponseEncoderName = httpResponseEncoderName; } @Override public final void channelReadComplete(ChannelHandlerContext ctx) { ctx.flush(); } @Override public final void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { if (ctx.channel().isOpen()) ctx.close(); log.error("Exception caught", cause); } @Override protected final void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof FullHttpRequest) { handleHttpRequest(ctx, (FullHttpRequest) msg); } else if (msg instanceof WebSocketFrame) { handleWebSocketFrame(ctx, (WebSocketFrame) msg); } else { throw new AssertionError("Unexpected message " + msg); } } protected static boolean sessionsEnabled() { return "always".equals(trackSession) || "sse".equals(trackSession); } protected static final String SESSION_COOKIE_KEY = "JSESSIONID"; protected static final Map<String, Context> sessions = Collections.synchronizedMap(new WeakHashMap<String, Context>()); protected static final String TRACK_SESSION_PROP = HttpChannelAdapter.class.getName() + ".trackSession"; protected static final String trackSession = System.getProperty(TRACK_SESSION_PROP, "sse"); protected static final String OMIT_DATE_HEADER_PROP = HttpChannelAdapter.class.getName() + ".omitDateHeader"; protected static final Boolean omitDateHeader = SystemProperties.isEmptyOrTrue(OMIT_DATE_HEADER_PROP); private static final String ACTOR_KEY = "co.paralleluniverse.comsat.webactors.sessionActor"; private static final WeakHashMap<Class<?>, List<Pair<String, String>>> classToUrlPatterns = new WeakHashMap<>(); private static final InternalLogger log = InternalLoggerFactory.getInstance(AutoWebActorHandler.class); private final WebActorContextProvider selector; private final String httpResponseEncoderName; private WebSocketServerHandshaker handshaker; private WebSocketActorAdapter webSocketActor; private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { // Check for closing frame if (frame instanceof CloseWebSocketFrame) { handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain()); return; } if (frame instanceof PingWebSocketFrame) { ctx.channel().writeAndFlush(new PongWebSocketFrame(frame.content().retain())); return; } if (frame instanceof ContinuationWebSocketFrame) return; if (frame instanceof TextWebSocketFrame) webSocketActor.onMessage(((TextWebSocketFrame) frame).text()); else webSocketActor.onMessage(frame.content().nioBuffer()); } private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws SuspendExecution { // Handle a bad request. if (!req.getDecoderResult().isSuccess()) { sendHttpResponse( ctx, req, new DefaultFullHttpResponse(req.getProtocolVersion(), BAD_REQUEST), false); return; } final String uri = req.getUri(); final Context actorCtx = selector.get(ctx, req); assert actorCtx != null; final ReentrantLock lock = actorCtx.getLock(); assert lock != null; lock.lock(); try { final ActorRef<? extends WebMessage> userActorRef = actorCtx.getRef(); ActorImpl internalActor = (ActorImpl) actorCtx.getAttachments().get(ACTOR_KEY); if (userActorRef != null) { if (actorCtx.handlesWithWebSocket(uri)) { if (internalActor == null || !(internalActor instanceof WebSocketActorAdapter)) { //noinspection unchecked webSocketActor = new WebSocketActorAdapter(ctx, (ActorRef<? super WebMessage>) userActorRef); addActorToContextAndUnlock(actorCtx, webSocketActor, lock); } // Handshake final WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(uri, null, true); handshaker = wsFactory.newHandshaker(req); if (handshaker == null) { WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); } else { @SuppressWarnings("unchecked") final ActorRef<WebMessage> userActorRef0 = (ActorRef<WebMessage>) webSocketActor.userActor; handshaker .handshake(ctx.channel(), req) .addListener( new GenericFutureListener<ChannelFuture>() { @Override public void operationComplete(ChannelFuture future) throws Exception { FiberUtil.runInFiber( new SuspendableRunnable() { @Override public void run() throws SuspendExecution, InterruptedException { userActorRef0.send( new WebSocketOpened(WebActorHandler.this.webSocketActor.ref())); } }); } }); } return; } else if (actorCtx.handlesWithHttp(uri)) { if (internalActor == null || !(internalActor instanceof HttpActorAdapter)) { //noinspection unchecked internalActor = new HttpActorAdapter( (ActorRef<HttpRequest>) userActorRef, actorCtx, httpResponseEncoderName); addActorToContextAndUnlock(actorCtx, internalActor, lock); } //noinspection unchecked ((HttpActorAdapter) internalActor).service(ctx, req); return; } } } finally { if (lock.isHeldByCurrentStrand() && lock.isLocked()) lock.unlock(); } sendHttpResponse( ctx, req, new DefaultFullHttpResponse(req.getProtocolVersion(), NOT_FOUND), false); } private void addActorToContextAndUnlock( Context actorContext, ActorImpl actor, ReentrantLock lock) { actorContext.getAttachments().put(ACTOR_KEY, actor); lock.unlock(); } private static final class WebSocketActorAdapter extends FakeActor<WebDataMessage> { ActorRef<? super WebMessage> userActor; private ChannelHandlerContext ctx; public WebSocketActorAdapter( ChannelHandlerContext ctx, ActorRef<? super WebMessage> userActor) { super(userActor.getName(), new WebSocketChannelAdapter(ctx)); ((WebSocketChannelAdapter) (SendPort) getMailbox()).actor = this; this.ctx = ctx; this.userActor = userActor; watch(userActor); } @Override public final void interrupt() { die(new InterruptedException()); } @Override public final String toString() { return "WebSocketActorAdapter{" + "userActor=" + userActor + '}'; } private void onMessage(final ByteBuffer message) { try { userActor.send(new WebDataMessage(ref(), message)); } catch (SuspendExecution ex) { throw new AssertionError(ex); } } private void onMessage(final String message) { try { userActor.send(new WebDataMessage(ref(), message)); } catch (SuspendExecution ex) { throw new AssertionError(ex); } } @Override protected final WebDataMessage handleLifecycleMessage(LifecycleMessage m) { if (m instanceof ExitMessage) { ExitMessage em = (ExitMessage) m; if (em.getActor() != null && em.getActor().equals(userActor)) die(em.getCause()); } return null; } @Override protected final void throwIn(RuntimeException e) { die(e); } @Override protected final void die(Throwable cause) { super.die(cause); if (ctx.channel().isOpen()) ctx.close(); // Ensure to release server references userActor = null; ctx = null; } } private static final class WebSocketChannelAdapter implements SendPort<WebDataMessage> { private final ChannelHandlerContext ctx; WebSocketActorAdapter actor; public WebSocketChannelAdapter(ChannelHandlerContext ctx) { this.ctx = ctx; } @Override public final void send(WebDataMessage message) throws SuspendExecution, InterruptedException { trySend(message); } @Override public final boolean send(WebDataMessage message, long timeout, TimeUnit unit) throws SuspendExecution, InterruptedException { return trySend(message); } @Override public final boolean send(WebDataMessage message, Timeout timeout) throws SuspendExecution, InterruptedException { return send(message, timeout.nanosLeft(), TimeUnit.NANOSECONDS); } @Override public final boolean trySend(WebDataMessage message) { if (!message.isBinary()) ctx.writeAndFlush(new TextWebSocketFrame(message.getStringBody())); else ctx.writeAndFlush( new BinaryWebSocketFrame(Unpooled.wrappedBuffer(message.getByteBufferBody()))); return true; } @Override public final void close() { if (ctx.channel().isOpen()) ctx.close(); if (actor != null) actor.die(null); } @Override public final void close(Throwable t) { if (actor != null) actor.die(t); close(); } } private static final class HttpActorAdapter extends FakeActor<HttpResponse> { private ActorRef<? super HttpRequest> userActor; private Context context; private volatile boolean dead; private volatile ChannelHandlerContext ctx; private volatile FullHttpRequest req; private volatile Object watchToken; HttpActorAdapter( ActorRef<? super HttpRequest> userActor, Context actorContext, String httpResponseEncoderName) { super("HttpActorAdapter", new HttpChannelAdapter(actorContext, httpResponseEncoderName)); if (actorContext.watch()) ((HttpChannelAdapter) (SendPort) getMailbox()).actor = this; this.userActor = userActor; this.context = actorContext; } final void service(ChannelHandlerContext ctx, FullHttpRequest req) throws SuspendExecution { if (context.watch()) watchToken = watch(userActor); this.ctx = ctx; this.req = req; if (isDone()) { handleDeath(getDeathCause()); return; } userActor.send(new HttpRequestWrapper(ref(), ctx, req)); } final void unwatch() { if (watchToken != null && userActor != null) { unwatch(userActor, watchToken); watchToken = null; } } private void handleDeath(Throwable cause) { if (cause != null) sendHttpResponse( ctx, req, new DefaultFullHttpResponse( req.getProtocolVersion(), INTERNAL_SERVER_ERROR, Unpooled.wrappedBuffer( ("Actor is dead because of " + cause.getMessage()).getBytes())), false); else sendHttpResponse( ctx, req, new DefaultFullHttpResponse( req.getProtocolVersion(), INTERNAL_SERVER_ERROR, Unpooled.wrappedBuffer(("Actor has terminated.").getBytes())), false); } @Override protected final HttpResponse handleLifecycleMessage(LifecycleMessage m) { if (m instanceof ExitMessage) { final ExitMessage em = (ExitMessage) m; if (em.getActor() != null && em.getActor().equals(userActor)) { handleDeath(em.getCause()); die(em.getCause()); } } return null; } @Override protected final void die(Throwable cause) { if (dead) return; dead = true; super.die(cause); try { context.invalidate(); } catch (final Exception ignored) { } // Ensure to release references to server objects unwatch(); userActor = null; watchToken = null; context = null; ctx = null; req = null; } @Override protected final void throwIn(RuntimeException e) { die(e); } @Override protected final void interrupt() { die(new InterruptedException()); } @Override public final String toString() { return "HttpActorAdapter{" + userActor + "}"; } } private static final class HttpChannelAdapter implements SendPort<HttpResponse> { HttpActorAdapter actor; private final String httpResponseEncoderName; private Context actorContext; public HttpChannelAdapter(Context actorContext, String httpResponseEncoderName) { this.actorContext = actorContext; this.httpResponseEncoderName = httpResponseEncoderName; } @Override public final void send(HttpResponse message) throws SuspendExecution, InterruptedException { trySend(message); } @Override public final boolean send(HttpResponse message, long timeout, TimeUnit unit) throws SuspendExecution, InterruptedException { send(message); return true; } @Override public final boolean send(HttpResponse message, Timeout timeout) throws SuspendExecution, InterruptedException { return send(message, timeout.nanosLeft(), TimeUnit.NANOSECONDS); } @Override public final boolean trySend(HttpResponse message) { try { final HttpRequestWrapper nettyRequest = (HttpRequestWrapper) message.getRequest(); final FullHttpRequest req = nettyRequest.req; final ChannelHandlerContext ctx = nettyRequest.ctx; final HttpResponseStatus status = HttpResponseStatus.valueOf(message.getStatus()); if (message.getStatus() >= 400 && message.getStatus() < 600) { sendHttpResponse( ctx, req, new DefaultFullHttpResponse(req.getProtocolVersion(), status), false); close(); return true; } if (message.getRedirectPath() != null) { sendHttpRedirect(ctx, req, message.getRedirectPath()); close(); return true; } FullHttpResponse res; if (message.getStringBody() != null) res = new DefaultFullHttpResponse( req.getProtocolVersion(), status, Unpooled.wrappedBuffer(message.getStringBody().getBytes())); else if (message.getByteBufferBody() != null) res = new DefaultFullHttpResponse( req.getProtocolVersion(), status, Unpooled.wrappedBuffer(message.getByteBufferBody())); else res = new DefaultFullHttpResponse(req.getProtocolVersion(), status); if (message.getCookies() != null) { final ServerCookieEncoder enc = ServerCookieEncoder.STRICT; for (final Cookie c : message.getCookies()) HttpHeaders.setHeader(res, COOKIE, enc.encode(getNettyCookie(c))); } if (message.getHeaders() != null) { for (final Map.Entry<String, String> h : message.getHeaders().entries()) HttpHeaders.setHeader(res, h.getKey(), h.getValue()); } if (message.getContentType() != null) { String ct = message.getContentType(); if (message.getCharacterEncoding() != null) ct = ct + "; charset=" + message.getCharacterEncoding().name(); HttpHeaders.setHeader(res, CONTENT_TYPE, ct); } final boolean sseStarted = message.shouldStartActor(); if (trackSession(sseStarted)) { final String sessionId = UUID.randomUUID().toString(); res.headers() .add(SET_COOKIE, ServerCookieEncoder.STRICT.encode(SESSION_COOKIE_KEY, sessionId)); startSession(sessionId, actorContext); } if (!sseStarted) { final String stringBody = message.getStringBody(); long contentLength = 0L; if (stringBody != null) contentLength = stringBody.getBytes().length; else { final ByteBuffer byteBufferBody = message.getByteBufferBody(); if (byteBufferBody != null) contentLength = byteBufferBody.remaining(); } res.headers().add(CONTENT_LENGTH, contentLength); } final HttpStreamActorAdapter httpStreamActorAdapter; if (sseStarted) // This will copy the request content, which must still be referenceable, doing before the // request handler // unallocates it (unfortunately it is explicitly reference-counted in Netty) httpStreamActorAdapter = new HttpStreamActorAdapter(ctx, req); else httpStreamActorAdapter = null; sendHttpResponse(ctx, req, res, false); if (sseStarted) { if (httpResponseEncoderName != null) { ctx.pipeline().remove(httpResponseEncoderName); } else { final ChannelPipeline pl = ctx.pipeline(); final List<String> handlerKeysToBeRemoved = new ArrayList<>(); for (final Map.Entry<String, ChannelHandler> e : pl) { if (e.getValue() instanceof HttpResponseEncoder) handlerKeysToBeRemoved.add(e.getKey()); } for (final String k : handlerKeysToBeRemoved) pl.remove(k); } try { message.getFrom().send(new HttpStreamOpened(httpStreamActorAdapter.ref(), message)); } catch (SuspendExecution e) { throw new AssertionError(e); } } return true; } finally { if (actor != null) actor.unwatch(); } } private io.netty.handler.codec.http.cookie.Cookie getNettyCookie(Cookie c) { io.netty.handler.codec.http.cookie.Cookie ret = new io.netty.handler.codec.http.cookie.DefaultCookie(c.getName(), c.getValue()); ret.setDomain(c.getDomain()); ret.setHttpOnly(c.isHttpOnly()); ret.setMaxAge(c.getMaxAge()); ret.setPath(c.getPath()); ret.setSecure(c.isSecure()); return ret; } @Override public final void close() { if (actor != null) actor.die(null); actorContext = null; } @Override public final void close(Throwable t) { log.error("Exception while closing HTTP adapter", t); if (actor != null) actor.die(t); } } protected static boolean trackSession(boolean sseStarted) { return trackSession != null && ("always".equals(trackSession) || sseStarted && "sse".equals(trackSession)); } protected static boolean handlesWithHttp(String uri, Class<?> actorClass) { return match(uri, actorClass).equals("websocket"); } protected static boolean handlesWithWebSocket(String uri, Class<?> actorClass) { return match(uri, actorClass).equals("ws"); } private static final class HttpStreamActorAdapter extends FakeActor<WebDataMessage> { private volatile boolean dead; public HttpStreamActorAdapter(final ChannelHandlerContext ctx, final FullHttpRequest req) { super(req.toString(), new HttpStreamChannelAdapter(ctx, req)); ((HttpStreamChannelAdapter) (SendPort) getMailbox()).actor = this; } @Override protected WebDataMessage handleLifecycleMessage(LifecycleMessage m) { if (m instanceof ShutdownMessage) { die(null); } return null; } @Override protected void throwIn(RuntimeException e) { die(e); } @Override public void interrupt() { die(new InterruptedException()); } @Override protected void die(Throwable cause) { if (dead) return; this.dead = true; mailbox().close(); super.die(cause); } @Override public String toString() { return "HttpStreamActorAdapter{request + " + getName() + "}"; } } private static final class HttpStreamChannelAdapter implements SendPort<WebDataMessage> { private final Charset encoding; private final ChannelHandlerContext ctx; HttpStreamActorAdapter actor; public HttpStreamChannelAdapter(ChannelHandlerContext ctx, FullHttpRequest req) { this.ctx = ctx; this.encoding = HttpRequestWrapper.extractCharacterEncodingOrDefault(req.headers()); } @Override public final void send(WebDataMessage message) throws SuspendExecution, InterruptedException { trySend(message); } @Override public final boolean send(WebDataMessage message, long timeout, TimeUnit unit) throws SuspendExecution, InterruptedException { send(message); return true; } @Override public final boolean send(WebDataMessage message, Timeout timeout) throws SuspendExecution, InterruptedException { return send(message, timeout.nanosLeft(), TimeUnit.NANOSECONDS); } @Override public final boolean trySend(WebDataMessage res) { final ByteBuf buf; final String stringBody = res.getStringBody(); if (stringBody != null) { byte[] bs = stringBody.getBytes(encoding); buf = Unpooled.wrappedBuffer(bs); } else { buf = Unpooled.wrappedBuffer(res.getByteBufferBody()); } ctx.writeAndFlush(buf); return true; } @Override public final void close() { if (ctx.channel().isOpen()) ctx.close(); if (actor != null) actor.die(null); } @Override public final void close(Throwable t) { if (actor != null) actor.die(t); close(); } } private static void sendHttpResponse( ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res, Boolean close) { writeHttpResponse(ctx, req, res, close); } private static void sendHttpRedirect( ChannelHandlerContext ctx, FullHttpRequest req, String newUri) { final FullHttpResponse res = new DefaultFullHttpResponse(req.getProtocolVersion(), FOUND); HttpHeaders.setHeader(res, LOCATION, newUri); writeHttpResponse(ctx, req, res, true); } private static void writeHttpResponse( ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res, Boolean close) { if (!omitDateHeader && !res.headers().contains(DefaultHttpHeaders.Names.DATE)) DefaultHttpHeaders.addDateHeader(res, DefaultHttpHeaders.Names.DATE, new Date()); // Send the response and close the connection if necessary. if (!HttpHeaders.isKeepAlive(req) || res.getStatus().code() != 200 || close == null || close) { res.headers().set(CONNECTION, HttpHeaders.Values.CLOSE); ctx.writeAndFlush(res).addListener(ChannelFutureListener.CLOSE); } else { res.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE); write(ctx, res); } } private static ChannelFuture write(ChannelHandlerContext ctx, Object res) { return ctx.writeAndFlush(res); // : ctx.write(res); } private static String match(String uri, Class<?> actorClass) { if (uri != null && actorClass != null) { for (final Pair<String, String> e : lookupOrInsert(actorClass)) { if (servletMatch(e.getFirst(), uri)) return e.getSecond(); } } return ""; } private static List<Pair<String, String>> lookupOrInsert(Class<?> actorClass) { if (actorClass != null) { final List<Pair<String, String>> lookup = classToUrlPatterns.get(actorClass); if (lookup != null) return lookup; return insert(actorClass); } return null; } private static List<Pair<String, String>> insert(Class<?> actorClass) { if (actorClass != null) { final WebActor wa = actorClass.getAnnotation(WebActor.class); final List<Pair<String, String>> ret = new ArrayList<>(4); for (String httpP : wa.httpUrlPatterns()) addPattern(ret, httpP, "websocket"); for (String wsP : wa.webSocketUrlPatterns()) addPattern(ret, wsP, "ws"); classToUrlPatterns.put(actorClass, ret); return ret; } return null; } private static void addPattern(List<Pair<String, String>> ret, String p, String type) { if (p != null) { @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") final Pair<String, String> entry = new Pair<>(p, type); if (p.endsWith("*") || p.startsWith("*.") || p.equals("/")) // Wildcard -> end ret.add(entry); else // Exact -> beginning ret.add(0, entry); } } private static boolean servletMatch(String pattern, String uri) { // As per servlet spec if (pattern != null && uri != null) { if (pattern.startsWith("/") && pattern.endsWith("*")) return uri.startsWith(pattern.substring(0, pattern.length() - 1)); if (pattern.startsWith("*.")) return uri.endsWith(pattern.substring(2)); if (pattern.isEmpty()) return uri.equals("/"); return pattern.equals("/") || pattern.equals(uri); } return false; } private static void startSession(String sessionId, Context actorContext) { sessions.put(sessionId, actorContext); } }
public void channelRead0(final ChannelHandlerContext ctx, final FullHttpRequest nettyRequest) throws Exception { if (!nettyRequest.getDecoderResult().isSuccess()) { sendError(ctx, HttpResponseStatus.BAD_REQUEST); return; } final long startTime = System.nanoTime(); final Request request = new DefaultRequest( new NettyHeadersBackedHeaders(nettyRequest.headers()), nettyRequest.getMethod().name(), nettyRequest.getUri(), nettyRequest.content()); final Channel channel = ctx.channel(); final DefaultMutableStatus responseStatus = new DefaultMutableStatus(); final HttpHeaders httpHeaders = new DefaultHttpHeaders(false); final MutableHeaders responseHeaders = new NettyHeadersBackedMutableHeaders(httpHeaders); FileHttpTransmitter fileHttpTransmitter = new DefaultFileHttpTransmitter( nettyRequest, httpHeaders, channel, compressResponses, addResponseTimeHeader ? startTime : -1); final DefaultEventController<RequestOutcome> requestOutcomeEventController = new DefaultEventController<>(); // We own the lifecycle nettyRequest.content().retain(); final Response response = new DefaultResponse( responseStatus, responseHeaders, fileHttpTransmitter, ctx.alloc(), new Action<ByteBuf>() { @Override public void execute(final ByteBuf byteBuf) throws Exception { final HttpResponse nettyResponse = new CustomHttpResponse(responseStatus.getResponseStatus(), httpHeaders); nettyRequest.content().release(); responseHeaders.set(HttpHeaderConstants.CONTENT_LENGTH, byteBuf.writerIndex()); boolean shouldClose = true; if (channel.isOpen()) { if (isKeepAlive(nettyRequest)) { responseHeaders.set( HttpHeaderConstants.CONNECTION, HttpHeaderConstants.KEEP_ALIVE); shouldClose = false; } long stopTime = System.nanoTime(); if (addResponseTimeHeader) { responseHeaders.set( "X-Response-Time", NumberUtil.toMillisDiffString(startTime, stopTime)); } execController.getExecution().complete(); channel.writeAndFlush(nettyResponse); channel.write(new DefaultHttpContent(byteBuf)); ChannelFuture future = channel.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); if (requestOutcomeEventController.isHasListeners()) { Headers headers = new DelegatingHeaders(responseHeaders); Status status = new DefaultStatus(responseStatus.getCode(), responseStatus.getMessage()); SentResponse sentResponse = new DefaultSentResponse(headers, status); RequestOutcome requestOutcome = new DefaultRequestOutcome( request, sentResponse, System.currentTimeMillis()); requestOutcomeEventController.fire(requestOutcome); } if (shouldClose) { future.addListener(ChannelFutureListener.CLOSE); } } } }); InetSocketAddress socketAddress = (InetSocketAddress) channel.localAddress(); final BindAddress bindAddress = new InetSocketAddressBackedBindAddress(socketAddress); Action<Action<Object>> subscribeHandler = new Action<Action<Object>>() { @Override public void execute(Action<Object> thing) throws Exception { channelSubscriptions.put(channel, thing); channel .closeFuture() .addListener( new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { channelSubscriptions.remove(channel); } }); } }; final DirectChannelAccess directChannelAccess = new DefaultDirectChannelAccess(channel, subscribeHandler); execController.start( new Action<Execution>() { @Override public void execute(Execution execution) throws Exception { DefaultContext.RequestConstants requestConstants = new DefaultContext.RequestConstants( applicationConstants, bindAddress, request, response, directChannelAccess, requestOutcomeEventController.getRegistry(), execution); new DefaultContext(requestConstants, registry, handlers, 0, return404).next(); } }); }
/** * Sends one message when a connection is open and echoes back any received data to the server. * Simply put, the echo client initiates the ping-pong traffic between the echo client and server by * sending the first message to the server. */ public final class NettyClientThroughPutTest { static final boolean SSL = System.getProperty("ssl") != null; static final String HOST = System.getProperty("host", "127.0.0.1"); static final int PORT = Integer.parseInt(System.getProperty("port", Integer.toString(EchoClientMain.PORT))); static class MyChannelInboundHandler extends ChannelInboundHandlerAdapter { private final ByteBuf firstMessage; final int bufferSize = 32 * 1024; @NotNull byte[] payload = new byte[bufferSize]; long bytesReceived = 0; long startTime; int i = 0; { Arrays.fill(payload, (byte) 'X'); firstMessage = Unpooled.buffer(bufferSize); firstMessage.writeBytes(payload); } @Override public void channelActive(@NotNull ChannelHandlerContext ctx) { startTime = System.nanoTime(); ctx.writeAndFlush(firstMessage); System.out.print("Running throughput test ( for 10 seconds ) "); } @Override public void channelRead(@NotNull ChannelHandlerContext ctx, @NotNull Object msg) { try { bytesReceived += ((ByteBuf) msg).readableBytes(); if (i++ % 10000 == 0) System.out.print("."); if (TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - startTime) >= 10) { long time = System.nanoTime() - startTime; System.out.printf("\nThroughput was %.1f MB/s%n", 1e3 * bytesReceived / time); return; } } finally { ReferenceCountUtil.release(msg); // (2) } final ByteBuf outMsg = ctx.alloc().buffer(bufferSize); // (2) outMsg.writeBytes(payload); ctx.writeAndFlush(outMsg); // (3) } @Override public void channelReadComplete(@NotNull ChannelHandlerContext ctx) { ctx.flush(); } @Override public void exceptionCaught(@NotNull ChannelHandlerContext ctx, @NotNull Throwable cause) { // Close the connection when an exception is raised. cause.printStackTrace(); ctx.close(); } } public static void main(String[] args) throws SSLException, InterruptedException { // Configure SSL.git final SslContext sslCtx; if (SSL) { sslCtx = SslContext.newClientContext(InsecureTrustManagerFactory.INSTANCE); } else { sslCtx = null; } // Configure the client. EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group) .channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY, true) .handler( new ChannelInitializer<SocketChannel>() { @Override public void initChannel(@NotNull SocketChannel ch) { ChannelPipeline p = ch.pipeline(); if (sslCtx != null) { p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT)); } // p.addLast(new LoggingHandler(LogLevel.INFO)); p.addLast(new MyChannelInboundHandler()); } }); // Start the client. ChannelFuture f = b.connect(HOST, PORT).sync(); // Wait until the connection is closed. f.channel().closeFuture().sync(); } finally { // Shut down the event loop to terminate all threads. group.shutdownGracefully(); } } }
private void close(boolean shutdownEventLoopGroup) { ServerChannel serverChannel = this.serverChannel.get(); if (serverChannel == null) { LOG.assertTrue(clientChannels.isEmpty()); return; } else if (!this.serverChannel.compareAndSet(serverChannel, null)) { return; } EventLoopGroup eventLoopGroup = shutdownEventLoopGroup ? serverChannel.eventLoop().parent() : null; try { long start = System.currentTimeMillis(); Channel[] clientChannels = this.clientChannels.toArray(new Channel[] {}); this.clientChannels.clear(); final CountDownLatch countDown = new CountDownLatch(clientChannels.length + 1); GenericFutureListener<ChannelFuture> listener = new GenericFutureListener<ChannelFuture>() { @Override public void operationComplete(@NotNull ChannelFuture future) throws Exception { try { Throwable cause = future.cause(); if (cause != null) { LOG.warn(cause); } } finally { countDown.countDown(); } } }; serverChannel.close().addListener(listener); for (Channel channel : clientChannels) { channel.close().addListener(listener); } try { countDown.await(5, TimeUnit.SECONDS); } catch (InterruptedException e) { LOG.warn( "Cannot close all channels for 10 seconds, channels: " + Arrays.toString(clientChannels)); } long duration = System.currentTimeMillis() - start; if (duration > 1000) { LOG.info( "Close all channels took " + duration + " ms: " + (duration / 60000) + " min " + ((duration % 60000) / 1000) + "sec"); } } finally { if (eventLoopGroup != null) { eventLoopGroup.shutdownGracefully(1, 2, TimeUnit.NANOSECONDS); } } }
public void channelRead0(final ChannelHandlerContext ctx, final FullHttpRequest nettyRequest) throws Exception { if (!nettyRequest.getDecoderResult().isSuccess()) { sendError(ctx, HttpResponseStatus.BAD_REQUEST); return; } final long startTime = addResponseTimeHeader ? System.nanoTime() : 0; final Request request = new DefaultRequest( new NettyHeadersBackedHeaders(nettyRequest.headers()), nettyRequest.getMethod().name(), nettyRequest.getUri(), nettyRequest.content()); final Channel channel = ctx.channel(); final DefaultMutableStatus responseStatus = new DefaultMutableStatus(); final HttpHeaders nettyHeaders = new DefaultHttpHeaders(false); final MutableHeaders responseHeaders = new NettyHeadersBackedMutableHeaders(nettyHeaders); final MimeTypes mimeTypes = registry.get(MimeTypes.class); final DefaultEventController<RequestOutcome> requestOutcomeEventController = new DefaultEventController<>(); final AtomicBoolean transmitted = new AtomicBoolean(false); final ResponseTransmitter responseTransmitter = new DefaultResponseTransmitter( transmitted, channel, nettyRequest, request, nettyHeaders, responseStatus, requestOutcomeEventController, startTime); final Action<Action<? super ResponseTransmitter>> responseTransmitterWrapper = Actions.wrap(responseTransmitter); final FileHttpTransmitter fileHttpTransmitter = new DefaultFileHttpTransmitter( nettyHeaders, mimeTypes, compressResponses, compressionMinSize, compressionMimeTypeWhiteList, compressionMimeTypeBlackList, responseTransmitterWrapper); StreamTransmitter streamTransmitter = new DefaultStreamTransmitter(nettyRequest, nettyHeaders, channel); final Response response = new DefaultResponse( responseStatus, responseHeaders, fileHttpTransmitter, streamTransmitter, ctx.alloc(), new Action<ByteBuf>() { @Override public void execute(final ByteBuf byteBuf) throws Exception { responseTransmitterWrapper.execute( new Action<ResponseTransmitter>() { @Override public void execute(ResponseTransmitter responseTransmitter) throws Exception { nettyHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, byteBuf.writerIndex()); responseTransmitter.transmit(new DefaultHttpContent(byteBuf)); } }); } }); InetSocketAddress socketAddress = (InetSocketAddress) channel.localAddress(); final BindAddress bindAddress = new InetSocketAddressBackedBindAddress(socketAddress); Action<Action<Object>> subscribeHandler = new Action<Action<Object>>() { @Override public void execute(Action<Object> thing) throws Exception { transmitted.set(true); channelSubscriptions.put(channel, thing); channel .closeFuture() .addListener( new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { channelSubscriptions.remove(channel); } }); } }; final DirectChannelAccess directChannelAccess = new DefaultDirectChannelAccess(channel, subscribeHandler); final DefaultContext.RequestConstants requestConstants = new DefaultContext.RequestConstants( applicationConstants, bindAddress, request, response, directChannelAccess, requestOutcomeEventController.getRegistry()); DefaultContext.start( execController.getControl(), requestConstants, registry, handlers, return404, new Action<Execution>() { @Override public void execute(Execution execution) throws Exception { if (!transmitted.get()) { Handler lastHandler = requestConstants.handler; StringBuilder description = new StringBuilder(); description .append("No response sent for ") .append(request.getMethod().getName()) .append(" request to ") .append(request.getUri()) .append(" (last handler: "); if (lastHandler instanceof DescribingHandler) { ((DescribingHandler) lastHandler).describeTo(description); } else { DescribingHandlers.describeTo(lastHandler, description); } description.append(")"); String message = description.toString(); LOGGER.warn(message); response.status(500); if (launchConfig.isDevelopment()) { response.send(message); } else { response.send(); } } } }); }