package org.coinjoin.server;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import javax.net.ServerSocketFactory;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.TransactionInput;
import org.bitcoinj.core.TransactionOutput;
/**
* Class HttpsListener
* @author egordon
* Listens on provided port for HTTPS requests,
* and directs them to the corresponding api function.
*/
public class SSLListener extends Thread {
private int port;
public static enum SSLStatus {
OK, ERR_CLIENT, ERR_SERVER
}
public static enum APICall {
GET_RSA, REG_IN, REG_OUT, REG_SIGN, STATUS
}
/**
* @param newPort on which to listen for connections
*/
public SSLListener(int newPort) {
this.port = newPort;
}
/**
* Creates new SSL server running in given port.
* For each new connection, send it to its own dedicated SSLRunner Thread.
*/
@Override
public void run() {
System.out.println("Starting SSL Server on: " + port);
ServerSocket serversocket = null;
ServerSocketFactory serversocketfactory = null;
try {
serversocketfactory =
(ServerSocketFactory) ServerSocketFactory.getDefault();
serversocket =
(ServerSocket) serversocketfactory.createServerSocket(port);
} catch (Exception e) {
// Fatal Error
e.printStackTrace();
System.exit(1);
}
// Listen Forever
Socket socket;
ArrayList<SSLRunner> runners = new ArrayList<SSLRunner>();
while(true) {
try {
socket = (Socket) serversocket.accept();
} catch (Exception e) {
e.printStackTrace();
continue;
}
// Add New Runner
runners.add(new SSLRunner(socket));
runners.get(runners.size()-1).start();
// Clean Up Dead Threads
for (Object obj : runners.toArray()) {
SSLRunner runner = (SSLRunner) obj;
if (!runner.isAlive()) runners.remove(obj);
}
Thread.yield();
}
}
protected class SSLRunner extends Thread {
private Socket socket;
public SSLRunner(Socket socket) {
this.socket = socket;
}
/**
* Checks that the request is valid, then redirects it to the proper
* API call.
*/
@Override
public void run() {
System.out.println("Established connection...");
ObjectInputStream ois = null;
ObjectOutputStream oos = null;
SSLResponse response;
try {
System.out.println("Reading API Call...");
// Get Streams
ois = new ObjectInputStream(socket.getInputStream());
oos = new ObjectOutputStream(socket.getOutputStream());
APICall call = (APICall) ois.readObject();
System.out.println("Received " + call.toString() + " API Call...");
// Call API Function
switch(call) {
case GET_RSA:
response = SSLAPI.getPublicRSA();
break;
case REG_IN:
response = SSLAPI.registerInput(ois.readInt(), (TransactionOutput)ois.readObject(),
(TransactionOutput)ois.readObject(), (byte[])ois.readObject());
break;
case REG_OUT:
response = SSLAPI.registerOutput(ois.readInt(), (Address)ois.readObject(),
(byte[])ois.readObject());
break;
case REG_SIGN:
response = SSLAPI.registerSignature(ois.readInt(), ois.readInt(), (TransactionInput)ois.readObject());
break;
case STATUS:
response = SSLAPI.txidStatus(ois.readInt());
break;
default:
response = new SSLResponse();
response.retStatus = SSLStatus.ERR_CLIENT;
response.retObjects.add("Error: Invalid API Call");
break;
}
} catch (IOException e) {
// Nothing to do! Wait for thread to die.
e.printStackTrace();
return;
} catch (ClassNotFoundException e) {
// Return error
e.printStackTrace();
response = new SSLResponse();
response.retStatus = SSLStatus.ERR_CLIENT;
response.retObjects.add("Error: Could not parse request");
}
try {
System.out.println("Response Status: " + response.retStatus.toString());
// Return Response
oos.writeObject(response.retStatus);
for (Object obj : response.retObjects) {
oos.writeObject(obj);
}
// Flush and Disconnect
oos.flush();
oos.close();
ois.close();
socket.close();
} catch (Exception e) {
// Nothing to do! Wait for thread to die...
e.printStackTrace();
}
}
}
}