/*
* JLibs: Common Utilities for Java
* Copyright (C) 2009 Santhosh Kumar T <santhosh.tekuri@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package jlibs.nio;
import jlibs.core.net.Protocol;
import jlibs.core.net.SSLUtil;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.util.function.Consumer;
import static java.util.Objects.requireNonNull;
import static jlibs.nio.Debugger.DEBUG;
import static jlibs.nio.Debugger.println;
/**
* @author Santhosh Kumar Tekuri
*/
public class TCPEndpoint{
public final String host;
public final int port;
private final String toString;
public TCPEndpoint(String host, int port){
this.host = requireNonNull(host, "host==null");
this.port = port;
toString = host+':'+port;
}
public TCPEndpoint(int port){
this("0.0.0.0", port);
}
public TCPEndpoint(InetSocketAddress address){
this(address.getHostString(), address.getPort());
}
public TCPEndpoint(String url) throws GeneralSecurityException, SSLException{
Protocol protocol = Protocol.TCP;
int port = -1;
int colon = url.indexOf("://");
if(colon!=-1){
protocol = Protocol.valueOf(url.substring(0, colon).toUpperCase());
url = url.substring(colon+"://".length());
}
colon = url.indexOf(':');
if(colon!=-1){
port = Integer.parseInt(url.substring(colon+1));
url = url.substring(0, colon);
}
if(port==-1)
port = protocol.port();
this.host = url;
this.port = port;
toString = host+':'+port;
if(protocol.secured())
sslContext = SSLUtil.defaultContext();
}
public InetSocketAddress socketAddress(){
return new InetSocketAddress(host, port);
}
public SSLContext sslContext;
@Override
public final String toString(){
return toString;
}
public TCPServer startServer(TCPServer.Listener listener) throws IOException{
TCPServer server = new TCPServer(new TCPServer.Listener(){
@Override
public void accept(TCPConnection con){
try{
if(sslContext!=null){
SSLEngine engine = sslContext.createSSLEngine();
engine.setUseClientMode(false);
new SSLSocket(con.in(), con.out(), engine);
}
}catch(Throwable thr){
Reactor.current().handleException(thr);
con.close();
return;
}
listener.accept(con);
}
@Override
public String toString(){
return listener.getClass().getSimpleName();
}
});
try{
server.bind(socketAddress());
}catch(Throwable thr){
server.close();
throw thr;
}
return server;
}
public void getConnection(Consumer<Result<Connection>> listener, Proxy proxy){
Reactor reactor = Reactor.current();
while(true){
Connection con = reactor.connectionPool.remove(toString());
if(con==null)
break;
ByteBuffer buffer = reactor.allocator.allocate(1);
int read = -1;
try{
read = con.in().read(buffer);
}catch(Throwable ignore){
// ignore.printStackTrace();
}
reactor.allocator.free(buffer);
assert read<=0;
if(read==-1){
if(DEBUG)
println(con+".isBroken=true");
con.close();
}else{
listener.accept(new Result<>(con));
return;
}
}
newConnection(listener, proxy);
}
public void newConnection(Consumer<Result<Connection>> listener, Proxy proxy){
if(proxy==null)
new NewConnection(listener).start();
else
proxy.getConnection(this, listener);
}
private class NewConnection implements TCPConnector.Listener{
private Consumer<Result<Connection>> listener;
private NewConnection(Consumer<Result<Connection>> listener){
this.listener = listener;
}
public void start(){
TCPConnector connector = null;
try{
connector = new TCPConnector();
connector.connect(socketAddress(), this);
}catch(Throwable thr){
if(connector!=null)
connector.close();
listener.accept(new Result<>(thr));
}
}
@Override
public void process(TCPConnector connector){
TCPConnection con;
try{
con = connector.getTCPConnection();
}catch(Throwable thr){
connector.close();
listener.accept(new Result<>(thr));
return;
}
try{
if(sslContext!=null){
SSLEngine engine = sslContext.createSSLEngine();
engine.setUseClientMode(true);
new SSLSocket(con.in(), con.out(), engine);
}
}catch(Throwable thr){
con.shutdown();
listener.accept(new Result<>(thr));
return;
}
listener.accept(new Result<>(con));
}
}
}