package com.atlassian.localtunnel;
import com.atlassian.localtunnel.data.Backend;
import com.atlassian.localtunnel.data.ControlTunnelResponse;
import com.google.gson.Gson;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import java.io.IOException;
import java.net.*;
/**
* @since version
*/
public class DefaultLocalTunnel implements LocalTunnel
{
private final int port;
private final String tunnelName;
private final String clientName;
private final String host;
private String remoteHost;
private ControlPingPongService pingPongService;
private ProxyService proxyService;
private boolean started = false;
public DefaultLocalTunnel(int port, String tunnelName, String clientName, String host)
{
this.port = port;
this.tunnelName = tunnelName;
this.clientName = clientName;
this.host = host;
}
@Override
public void start() throws IOException
{
System.out.println(String.format("Creating tunnel: '%s' to forward local port '%s' to remote server '%s', with credentials: '%s' ...", tunnelName, port, host, clientName));
LocalTunnelProtocol protocol = new LocalTunnelProtocol();
URL hostURL = new URL(host);
Backend backend = getBackend(hostURL);
Socket control = createControlSocket(backend);
protocol.sendVersion(control);
protocol.sendMessage(control,protocol.controlRequest(getTunnelName(), getClientName()));
String response = protocol.receiveMessage(control);
pingPongService = new ControlPingPongService(control);
pingPongService.start();
Gson gson = new Gson();
ControlTunnelResponse ctr = gson.fromJson(response,ControlTunnelResponse.class);
proxyService = new ProxyService(backend,port,tunnelName,clientName,ctr.getConcurrency().intValue());
proxyService.start();
if (hostURL.getPort() == 80)
{
remoteHost = "http://" + ctr.getHost().split("\\.")[0] + "." + hostURL.getHost();
} else
{
remoteHost = "http://" + ctr.getHost().split("\\.")[0] + "." + hostURL.getHost() + ":" + hostURL.getPort();
}
System.out.println("started the local tunnel");
System.out.println("you can now access: " + remoteHost);
started = true;
}
private Backend getBackend(URL url)
{
int backendPort = discoverBackendPort(url);
return new Backend(url.getHost(), backendPort);
}
@Override
public void stop()
{
proxyService.stop();
pingPongService.stop();
started = false;
}
@Override
public boolean isStarted()
{
return started;
}
@Override
public String getRemoteHost()
{
return remoteHost;
}
@Override
public Socket createControlSocket(final Backend backend) throws IOException
{
Backend be = backend;
if(null == backend)
{
be = getBackend(new URL(host));
}
return new Socket(be.getHost(),be.getPort());
}
public Socket createControlSocket(final Backend backend, Proxy proxy) throws IOException
{
Backend be = backend;
if(null == backend)
{
be = getBackend(new URL(host));
}
Socket socket = new Socket(proxy);
InetSocketAddress addr = new InetSocketAddress(be.getHost(),be.getPort());
socket.connect(addr);
return socket;
}
public int getPort()
{
return port;
}
public String getTunnelName()
{
return tunnelName;
}
public String getClientName()
{
return clientName;
}
public String getHost()
{
return host;
}
private int discoverBackendPort(URL url)
{
int port = 0;
HttpClient httpclient = new DefaultHttpClient();
try
{
String urlString = url.toExternalForm().replaceAll(":80$","") + "/meta/backend"; // there is a huge backend BUG when using :80
HttpGet httpGet = new HttpGet(urlString);
// httpGet.addHeader("Accept-Encoding", "identity");
// httpGet.addHeader("Host","_backend." + bHost);
ResponseHandler<String> responseHandler = new BasicResponseHandler();
String response = httpclient.execute(httpGet,responseHandler);
port = Integer.parseInt(response);
}
catch (IOException ioe)
{
ioe.printStackTrace();
port = 0;
}
catch (NumberFormatException nfe)
{
port = 0;
}
finally
{
httpclient.getConnectionManager().shutdown();
}
return port;
}
}