Launcher/LaunchServer/src/main/java/ru/gravit/launchserver/socket/NettyServerSocketHandler.java

234 lines
9.4 KiB
Java

package ru.gravit.launchserver.socket;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.TrustManager;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import ru.gravit.launcher.ssl.LauncherKeyStore;
import ru.gravit.launcher.ssl.LauncherTrustManager;
import ru.gravit.launchserver.LaunchServer;
import ru.gravit.launchserver.fileserver.FileServerHandler;
import ru.gravit.launchserver.response.Response;
import ru.gravit.launchserver.socket.websocket.WebSocketFrameHandler;
import ru.gravit.utils.helper.CommonHelper;
import ru.gravit.utils.helper.LogHelper;
import ru.gravit.utils.helper.VerifyHelper;
@SuppressWarnings({"unused", "rawtypes"})
public final class NettyServerSocketHandler implements Runnable, AutoCloseable {
private static final String WEBSOCKET_PATH = "/api";
private static SSLServerSocketFactory ssf;
private static final ThreadFactory THREAD_FACTORY = r -> CommonHelper.newThread("Network Thread", true, r);
public volatile boolean logConnections = Boolean.getBoolean("launcher.logConnections");
private final AtomicReference<ServerSocket> serverSocket = new AtomicReference<>();
private final ExecutorService threadPool = Executors.newCachedThreadPool(THREAD_FACTORY);
// API
private final Map<String, Response.Factory> customResponses = new ConcurrentHashMap<>(2);
private final AtomicLong idCounter = new AtomicLong(0L);
private Set<Socket> sockets;
private volatile Listener listener;
public NettyServerSocketHandler(LaunchServer server) {
// Instance
LaunchServer server1 = server;
}
@Override
public void close() {
ServerSocket socket = serverSocket.getAndSet(null);
if (socket != null) {
LogHelper.info("Closing server socket listener");
try {
socket.close();
} catch (IOException e) {
LogHelper.error(e);
}
}
}
public SSLContext SSLContextInit() throws NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException, KeyManagementException, IOException, CertificateException {
TrustManager[] trustAllCerts = new TrustManager[]{
new LauncherTrustManager()
};
KeyStore ks = LauncherKeyStore.getKeyStore("keystore", "PSP1000");
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory
.getDefaultAlgorithm());
kmf.init(ks, "PSP1000".toCharArray());
SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc.init(kmf.getKeyManagers(), trustAllCerts, new SecureRandom());
return sc;
}
@Override
public void run() {
/*SSLContext sc = null;
try {
sc = SSLContextInit();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
}*/
//System.setProperty( "javax.net.ssl.keyStore","keystore");
//System.setProperty( "javax.net.ssl.keyStorePassword","PSP1000");
try {
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
} catch (IOException e) {
e.printStackTrace();
}
LogHelper.info("Starting server socket thread");
//SSLEngine engine = sc.createSSLEngine();
//engine.setUseClientMode(false);
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
WebSocketFrameHandler.server = LaunchServer.server;
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.DEBUG))
.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
public void initChannel(NioSocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
//p.addLast(new LoggingHandler(LogLevel.INFO));
System.out.println("P!");
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new HttpObjectAggregator(65536));
pipeline.addLast(new WebSocketServerCompressionHandler());
pipeline.addLast(new WebSocketServerProtocolHandler(WEBSOCKET_PATH, null, true));
pipeline.addLast(new FileServerHandler(LaunchServer.server.updatesDir, true));
pipeline.addLast(new WebSocketFrameHandler());
}
});
ChannelFuture f = b.bind(new InetSocketAddress(9876)).sync(); //TEST ONLY!
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
/*
try (SSLServerSocket serverSocket = (SSLServerSocket) ssf.createServerSocket()) {
serverSocket.setEnabledProtocols(new String[] {"TLSv1.2"});
if (!this.serverSocket.compareAndSet(null, serverSocket)) {
throw new IllegalStateException("Previous socket wasn't closed");
}
// Set socket params
serverSocket.setReuseAddress(true);
serverSocket.setPerformancePreferences(1, 0, 2);
//serverSocket.setReceiveBufferSize(0x10000);
serverSocket.bind(server.config.getSocketAddress());
LogHelper.info("Server socket thread successfully started");
// Listen for incoming connections
while (serverSocket.isBound()) {
SSLSocket socket = (SSLSocket) serverSocket.accept();
sockets.add(socket);
socket.startHandshake();
// Invoke pre-connect listener
long id = idCounter.incrementAndGet();
if (listener != null && !listener.onConnect(id, socket.getInetAddress())) {
continue; // Listener didn't accepted this connection
}
// Reply in separate thread
threadPool.execute(new ResponseThread(server, id, socket));
}
} catch (IOException e) {
// Ignore error after close/rebind
if (serverSocket.get() != null) {
LogHelper.error(e);
}
}
*/
}
public void registerCustomResponse(String name, Response.Factory factory) {
VerifyHelper.verifyIDName(name);
VerifyHelper.putIfAbsent(customResponses, name, Objects.requireNonNull(factory, "factory"),
String.format("Custom response has been already registered: '%s'", name));
}
public void setListener(Listener listener) {
this.listener = listener;
}
/*package*/ void onDisconnect(long id, Exception e) {
if (listener != null) {
listener.onDisconnect(id, e);
}
}
/*package*/ boolean onHandshake(long id, Integer type) {
return listener == null || listener.onHandshake(id, type);
}
public interface Listener {
boolean onConnect(long id, InetAddress address);
void onDisconnect(long id, Exception e);
boolean onHandshake(long id, Integer type);
}
}