package net.zephyrizing.http_server;

import java.io.IOException;
import java.net.ServerSocket;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;

import net.zephyrizing.http_server.exceptions.HttpServerException;
import net.zephyrizing.http_server.exceptions.BadRequestException;
import net.zephyrizing.http_server.handlers.Handler;
import net.zephyrizing.http_server.handlers.CobSpecHandler;

import static java.util.Arrays.asList;

public class HttpServer {

    private static int DEFAULT_THREADS = 24;

    public static void main(String[] args) throws InterruptedException {
        String defaultPublicRoot = String.format("%s/src/cob_spec/public/",
                                                 System.getenv("HOME"));

        OptionParser parser = new OptionParser();
        OptionSpec<Integer> portOpt = parser.acceptsAll(asList("p", "port"))
            .withRequiredArg().ofType(Integer.class).defaultsTo(5000);
        OptionSpec<String>  rootOpt = parser.acceptsAll(asList("d", "directory"))
            .withRequiredArg().ofType(String.class).defaultsTo(defaultPublicRoot);
        OptionSpec<Integer> threadsOpt = parser.acceptsAll(asList("t", "threads"))
            .withRequiredArg().ofType(Integer.class).defaultsTo(DEFAULT_THREADS);

        OptionSet options = parser.parse(args);

        int portNumber = options.valueOf(portOpt);
        String public_root_path = options.valueOf(rootOpt);
        int threadPoolSize = options.valueOf(threadsOpt);

        Path public_root = Paths.get(public_root_path);

        if (!Files.isDirectory(public_root)) {
            System.err.println("The directory given must exist!");
            return;
        }

        System.err.format("Starting server on port %d...", portNumber);
        try (ServerSocket serverSocket = new ServerSocket();
             HttpServerSocket httpSocket = new HttpServerSocketImpl(serverSocket);) {
            Executor executor = Executors.newFixedThreadPool(threadPoolSize);
            Handler handler = new CobSpecHandler(public_root);
            HttpServer server = new HttpServer(executor, httpSocket, portNumber, handler);
            server.listen();
            server.serve();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static HttpServer createServer(int port, Handler handler) {
        try (ServerSocket serverSocket = new ServerSocket();
             HttpServerSocket httpSocket = new HttpServerSocketImpl(serverSocket);) {
            return new HttpServer(httpSocket, port, handler);
        } catch (IOException e) {
            return null;
        }
    }

    // Actual class begins

    private Executor executor;
    private HttpServerSocket serverSocket;
    private int port;
    private Handler handler;

    public HttpServer(HttpServerSocket serverSocket, int port, Handler handler) {
        this(Executors.newFixedThreadPool(DEFAULT_THREADS), serverSocket, port, handler);
    }

    public HttpServer(Executor executor, HttpServerSocket serverSocket, int port, Handler handler) {
        this.executor = executor;
        this.serverSocket = serverSocket;
        this.port = port;
        this.handler = handler;
    }

    public void listen() throws IOException {
        serverSocket.bind(port);
    }

    public HttpConnection acceptConnection() {
        return serverSocket.acceptConnection();
    }

    public boolean acceptingConnections() {
        return true;
    }

    private class Worker implements Runnable {
        private HttpConnection connection;

        public Worker(HttpConnection connection) {
            this.connection = connection;
        }

        @Override
        public void run() {
            try (HttpConnection connection = this.connection) {
                HttpRequest request = connection.getRequest();
                if (request == null) {
                    return;
                }
                connection.send(handler.handle(request));

            } catch (BadRequestException e) {
                connection.send(new HttpResponse(400).setContent(e));
            } catch (HttpServerException e) {

            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public void serve() throws InterruptedException {
        while (acceptingConnections()) {
            // This is here to support testing the server via same
            // process threads. If there is no check of
            // Thread.interrupted(), then the server thread will never
            // quit, and thus never clean up it's bound sockets
            if (Thread.interrupted()) {
                throw new InterruptedException();
            }

            Worker worker = new Worker(acceptConnection());
            executor.execute(worker);
        }
    }
}

@@ -27,24 +27,50 @@
 tp_server;%0A%0A
+import java.io.Closeable;%0A
 import java.
@@ -739,16 +739,37 @@
 pServer 
+implements Closeable 
 %7B%0A%0A    p
@@ -2058,237 +2058,9 @@
 r);%0A
-        try (ServerSocket serverSocket = new ServerSocket();%0A             HttpServerSocket httpSocket = new HttpServerSocketImpl(serverSocket);) %7B%0A            Executor executor = Executors.newFixedThreadPool(threadPoolSize);%0A    
+%0A
     
@@ -2114,36 +2114,37 @@
 _root);%0A        
-    
+try (
 HttpServer serve
@@ -2146,20 +2146,16 @@
 server =
- new
  HttpSer
@@ -2161,31 +2161,22 @@
 rver
-(executor, httpSocket, 
+.createServer(
 port
@@ -2183,33 +2183,36 @@
 Number, handler)
-;
+) %7B%0A
 %0A            ser
@@ -2407,32 +2407,51 @@
 Handler handler)
+ throws IOException
  %7B%0A        try (
@@ -2445,21 +2445,16 @@
         
-try (
 ServerSo
@@ -2478,35 +2478,67 @@
  = n
-ew ServerSocket()
+ull;%0A        HttpServerSocket httpSocket = null;
 ;%0A
+%0A
         
     
@@ -2533,37 +2533,80 @@
         
-     HttpServerSocket
+try %7B%0A            serverSocket = new ServerSocket();%0A           
  httpSoc
@@ -2650,19 +2650,16 @@
 Socket);
-) %7B
 %0A       
@@ -2739,32 +2739,213 @@
 OException e) %7B%0A
+            if (httpSocket != null) %7B%0A                httpSocket.close();%0A%0A            %7D%0A            if (serverSocket != null) %7B%0A                serverSocket.close();%0A            %7D%0A
             retu
@@ -3537,32 +3537,133 @@
 handler;%0A    %7D%0A%0A
+    @Override%0A    public void close() throws IOException %7B%0A        this.serverSocket.close();%0A    %7D%0A%0A
     public void 

Make the server Closeable