to the main page about the tutorial  INTERACTIVE WEB SERVICES WITH JAVA back up next

A Web Server in 150 Lines

A simple but functioning HTTP file server in Java.

Read command-line arguments and open server socket:
import java.net.*;
import java.io.*;
import java.util.*;

public class FileServer
{
    public static void main(String[] args) 
    {
        // read arguments
        if (args.length!=2) {
            System.out.println("Usage: java FileServer <port> <wwwhome>");
            System.exit(-1);
        }
        int port = Integer.parseInt(args[0]);
        String wwwhome = args[1];
        
        // open server socket
        ServerSocket socket = null; 
        try {
            socket = new ServerSocket(port); 
        } catch (IOException e) {
            System.err.println("Could not start server: " + e);
            System.exit(-1);
        }
        System.out.println("FileServer accepting connections on port " + port);

Begin request-response loop:
        // request handler loop
        while (true) {
            Socket connection = null;
            try {
                // wait for request
                connection = socket.accept();
                BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                OutputStream out = new BufferedOutputStream(connection.getOutputStream());
                PrintStream pout = new PrintStream(out);

Read first line of request to get file name:
              
                // read first line of request (ignore the rest)
                String request = in.readLine();
                if (request==null)
                    continue;
                log(connection, request);
                while (true) {
                    String misc = in.readLine();
                    if (misc==null || misc.length()==0)
                        break;
                }

Process request by checking that the request is well-formed and permitted. For directory requests that do not end in '/', redirect browser. For files, send back the contents:
                // parse the line
                if (!request.startsWith("GET") || request.length()<14 ||
                    !(request.endsWith("HTTP/1.0") || request.endsWith("HTTP/1.1"))) {
                    // bad request
                    errorReport(pout, connection, "400", "Bad Request", 
                                "Your browser sent a request that " + 
                                "this server could not understand.");
                } else {
                    String req = request.substring(4, request.length()-9).trim();
                    if (req.indexOf("..")!=-1 || 
                        req.indexOf("/.ht")!=-1 || req.endsWith("~")) {
                        // evil hacker trying to read non-wwwhome or secret file
                        errorReport(pout, connection, "403", "Forbidden",
                                    "You don't have permission to access the requested URL.");
                    } else {
                        String path = wwwhome + "/" + req;
                        File f = new File(path);
                        if (f.isDirectory() && !path.endsWith("/")) {
                            // redirect browser if referring to directory without final '/'
                            pout.print("HTTP/1.0 301 Moved Permanently\r\n" +
                                       "Location: http://" + 
                                       connection.getLocalAddress().getHostAddress() + ":" +
                                       connection.getLocalPort() + "/" + req + "/\r\n\r\n");
                            log(connection, "301 Moved Permanently");
                        } else {
                            if (f.isDirectory()) { 
                                // if directory, implicitly add 'index.html'
                                path = path + "index.html";
                                f = new File(path);
                            }
                            try { 
                                // send file
                                InputStream file = new FileInputStream(f);
                                pout.print("HTTP/1.0 200 OK\r\n" +
                                           "Content-Type: " + guessContentType(path) + "\r\n" +
                                           "Date: " + new Date() + "\r\n" +
                                           "Server: FileServer 1.0\r\n\r\n");
                                sendFile(file, out); // send raw file 
                                log(connection, "200 OK");
                            } catch (FileNotFoundException e) { 
                                // file not found
                                errorReport(pout, connection, "404", "Not Found",
                                            "The requested URL was not found on this server.");
                            }
                        }
                    }
                }
                out.flush();

Catch exceptions and close connection:
            } catch (IOException e) { System.err.println(e); }
            try {
                if (connection != null) connection.close(); 
            } catch (IOException e) { System.err.println(e); }
        }
    }

Auxiliary methods for logging and error reporting:
    
    private static void log(Socket connection, String msg)
    {
        System.err.println(new Date() + " [" + connection.getInetAddress().getHostAddress() + 
                           ":" + connection.getPort() + "] " + msg);
    }

    private static void errorReport(PrintStream pout, Socket connection,
                                    String code, String title, String msg)
    {
        pout.print("HTTP/1.0 " + code + " " + title + "\r\n" +
                   "\r\n" +
                   "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n" +
                   "<TITLE>" + code + " " + title + "</TITLE>\r\n" +
                   "</HEAD><BODY>\r\n" +
                   "<H1>" + title + "</H1>\r\n" + msg + "<P>\r\n" +
                   "<HR><ADDRESS>FileServer 1.0 at " + 
                   connection.getLocalAddress().getHostName() + 
                   " Port " + connection.getLocalPort() + "</ADDRESS>\r\n" +
                   "</BODY></HTML>\r\n");
        log(connection, code + " " + title);
    }

Auxiliary methods for guessing MIME type and sending file contents:
    private static String guessContentType(String path)
    {
        if (path.endsWith(".html") || path.endsWith(".htm")) 
            return "text/html";
        else if (path.endsWith(".txt") || path.endsWith(".java")) 
            return "text/plain";
        else if (path.endsWith(".gif")) 
            return "image/gif";
        else if (path.endsWith(".class"))
            return "application/octet-stream";
        else if (path.endsWith(".jpg") || path.endsWith(".jpeg"))
            return "image/jpeg";
        else    
            return "text/plain";
    }

    private static void sendFile(InputStream file, OutputStream out)
    {
        try {
            byte[] buffer = new byte[1000];
            while (file.available()>0) 
                out.write(buffer, 0, file.read(buffer));
        } catch (IOException e) { System.err.println(e); }
    }
}

Obvious extensions:

back COPYRIGHT © 2002-2003 ANDERS MØLLER & MICHAEL I. SCHWARTZBACH next