最小的JAVA WEB SERVER源程序(可正常運行)

来源:互联网 发布:2016网络小说完本推荐 编辑:程序博客网 时间:2024/04/28 17:25

文件結構如下:

代碼:

//一個非常小、可正常運行的WebServer。可以用于學習
/*
Copyright Paul James Mutton, 2001-2004, http://www.jibble.org/
 
This file is part of Jibble Web Server / WebServerLite.
 
This software is dual-licensed, allowing you to choose between the GNU
General Public License (GPL) and the www.jibble.org Commercial License.
Since the GPL may be too restrictive for use in a proprietary application,
a commercial license is also provided. Full license information can be
found at http://www.jibble.org/licenses/
 
$Author: pjm2 $
$Id: Logger.java,v 1.2 2004/02/01 13:37:35 pjm2 Exp $
 
*/
Logger.java:
/**
 * A logging class which prefixes messages to the standard output with
 * human readable timestamps.
 *
 * @author Copyright Paul Mutton, http://www.jibble.org/
 */
public class Logger {
   
    private Logger() {
        // Prevent this class from being constructed.
    }
   
    public static void log(String ip, String request, int code) {
        System.out.println("[" + new java.util.Date().toString() + "] " + ip + " /"" + request + "/" " + code);
    }
   
}
RequestThread.java:
import java.io.*;
import java.net.*;
import java.util.*;
 
 
/**
 * A thread which deals with an individual request to the web server.
 * This is passed a socket from the WebServer when a connection is
 * accepted.
 *
 * @author Copyright Paul Mutton, http://www.jibble.org/
 */
public class RequestThread implements Runnable {
 
    public RequestThread(Socket socket, File rootDir) {
        _socket = socket;
        _rootDir = rootDir;
    }
   
    // handles a connction from a client.
    public void run() {
        String ip = "unknown";
        String request = "unknown";
        int bytesSent = 0;
        BufferedInputStream reader = null;
        try {
            ip = _socket.getInetAddress().getHostAddress();
            BufferedReader in = new BufferedReader(new InputStreamReader(_socket.getInputStream()));
            BufferedOutputStream out = new BufferedOutputStream(_socket.getOutputStream());
           
            String path = "";
            // Read the first line from the client.
            request = in.readLine();
            if (request != null && request.startsWith("GET ") && (request.endsWith(" HTTP/1.0") || request.endsWith("HTTP/1.1"))) {
                path = request.substring(4, request.length() - 9);
            }
            else {
                // Invalid request type (no "GET")
                Logger.log(ip, request, 405);
                _socket.close();
                return;
            }
           
            //Read in and store all the headers.
            HashMap headers = new HashMap();
            String line = null;
            while ((line = in.readLine()) != null) {
                line = line.trim();
                if (line.equals("")) {
                    break;
                }
                int colonPos = line.indexOf(":");
                if (colonPos > 0) {
                    String key = line.substring(0, colonPos);
                    String value = line.substring(colonPos + 1);
                    headers.put(key, value.trim());
                }
            }
           
            File file = new File(_rootDir, URLDecoder.decode(path));
            file = file.getCanonicalFile();
           
            if (!file.toString().startsWith(_rootDir.toString())) {
                // Uh-oh, it looks like some lamer is trying to take a peek
                // outside of our web root directory.
                Logger.log(ip, request, 404);
                out.write(("HTTP/1.0 403 Forbidden/r/n" +
                           "Content-Type: text/html/r/n" +
                           "Expires: Thu, 01 Dec 1994 16:00:00 GMT/r/n" +
                           "/r/n" +
                           "<h1>403 Forbidden</h1><code>" + path + "</code><p><hr>" +
                           "<i>" + WebServerConfig.VERSION + "</i>").getBytes());
                out.flush();
                _socket.close();
                return;
            }
           
            if (file.isDirectory()) {
                // Check to see if there are any index files in the directory.
                for (int i = 0; i < WebServerConfig.DEFAULT_FILES.length; i++) {
                    File indexFile = new File(file, WebServerConfig.DEFAULT_FILES[i]);
                    if (indexFile.exists() && !indexFile.isDirectory()) {
                        file = indexFile;
                        break;
                    }
                }
                if (file.isDirectory()) {
                    // print directory listing
                    Logger.log(ip, request, 200);
                    if (!path.endsWith("/")) {
                        path = path + "/";
                    }
                    File[] files = file.listFiles();
                    out.write(("HTTP/1.0 200 OK/r/n" +
                               "Content-Type: text/html/r/n" +
                               "Expires: Thu, 01 Dec 1994 16:00:00 GMT/r/n" +
                               "/r/n" +
                               "<h1>Directory Listing</h1>" +
                               "<h3>" + path + "</h3>" +
                               "<table border=/"0/" cellspacing=/"8/">" +
                               "<tr><td><b>Filename</b><br></td><td align=/"right/"><b>Size</b></td><td><b>Last Modified</b></td></tr>" +
                               "<tr><td><b><a href=/"..//">../</b><br></td><td></td><td></td></tr>").getBytes());
                    for (int i = 0; i < files.length; i++) {
                        file = files[i];
                        if (file.isDirectory()) {
                            out.write(("<tr><td><b><a href=/"" + path + file.getName() + "//">" + file.getName() + "/</a></b></td><td></td><td></td></tr>").getBytes());
                        }
                        else {
                            out.write(("<tr><td><a href=/"" + path + file.getName() + "/">" + file.getName() + "</a></td><td align=/"right/">" + file.length() + "</td><td>" + new Date(file.lastModified()).toString() + "</td></tr>").getBytes());
                        }
                    }
                    out.write(("</table><hr>" +
                               "<i>" + WebServerConfig.VERSION + "</i>").getBytes());
                    out.flush();
                    _socket.close();
                    return;
                }
            }
           
            if (!file.exists()) {
                // The file was not found.
                Logger.log(ip, request, 404);
                out.write(("HTTP/1.0 404 File Not Found/r/n" +
                           "Content-Type: text/html/r/n" +
                           "Expires: Thu, 01 Dec 1994 16:00:00 GMT/r/n" +
                           "/r/n" +
                           "<h1>404 File Not Found</h1><code>" + path + "</code><p><hr>" +
                           "<i>" + WebServerConfig.VERSION + "</i>").getBytes());
                out.flush();
                _socket.close();
                return;
            }
 
            String extension = WebServerConfig.getExtension(file);
           
            // Execute any files in any cgi-bin directories under the web root.
            if (file.getParent().indexOf("cgi-bin") >= 0) {
                try {
                    out.write("HTTP/1.0 200 OK/r/n".getBytes());
                    ServerSideScriptEngine.execute(out, headers, file, path);
                    out.flush();
                    Logger.log(ip, path, 200);
                }
                catch (Throwable t) {
                    // Internal server error!
                    Logger.log(ip, request, 500);
                    out.write(("Content-Type: text/html/r/n/r/n" +
                               "<h1>Internal Server Error</h1><code>" + path + "</code><hr>Your script produced the following error: -<p><pre>" +
                               t.toString() +
                               "</pre><hr><i>" + WebServerConfig.VERSION + "</i>").getBytes());
                    out.flush();
                    _socket.close();
                    return;
                }
                out.flush();
                _socket.close();
                return;
            }
 
            reader = new BufferedInputStream(new FileInputStream(file));
           
            Logger.log(ip, request, 200);
            String contentType = (String)WebServerConfig.MIME_TYPES.get(extension);
            if (contentType == null) {
                contentType = "application/octet-stream";
            }
            out.write(("HTTP/1.0 200 OK/r/n" +
                       "Date: " + new Date().toString() + "/r/n" +
                       "Server: JibbleWebServer/1.0/r/n" +
                       "Content-Type: " + contentType + "/r/n" +
                       "Expires: Thu, 01 Dec 1994 16:00:00 GMT/r/n" +
                       "Content-Length: " + file.length() + "/r/n" +
                       "Last-modified: " + new Date(file.lastModified()).toString() + "/r/n" +
                       "/r/n").getBytes());
 
            if (WebServerConfig.SSI_EXTENSIONS.contains(extension)) {
                reader.close();
               ServerSideIncludeEngine.deliverDocument(out, file);
                _socket.close();
                return;
            }
 
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = reader.read(buffer, 0, 4096)) != -1) {
                out.write(buffer, 0, bytesRead);
                bytesSent += bytesRead;
            }
            out.flush();
            reader.close();
            _socket.close();
           
        }
        catch (IOException e) {
            Logger.log(ip, "ERROR " + e.toString() + " " + request, 0);
            if (reader != null) {
                try {
                    reader.close();
                }
                catch (Exception anye) {
                    // Do nothing.
                }
            }
        }
    }
   
    private Socket _socket;
    private File _rootDir;
 
}
ServerSideIncludeEngine.java:
import java.io.*;
import java.util.HashSet;
 
 
/**
 * Provides static methods to offer limited support for simple SSI
 * command directives.
 *
 * @author Copyright Paul Mutton, http://www.jibble.org/
 */
public class ServerSideIncludeEngine {
   
    private ServerSideIncludeEngine() {
        // Prevent this class from being constructed.
    }
   
    // Deliver the fully processed SSI page to the client
    public static void deliverDocument(BufferedOutputStream out, File file) throws IOException {
        HashSet visited = new HashSet();
        parse(out, visited, file);
        out.flush();       
    }
   
    // Oooh scary recursion
    private static void parse(BufferedOutputStream out, HashSet visited, File file) throws IOException {
       
        if (!file.exists() || file.isDirectory()) {
            out.write(("[SSI include not found: " + file.getCanonicalPath() + "]").getBytes());
            return;
        }
       
        if (visited.contains(file)) {
            out.write(("[SSI circular inclusion rejected: " + file.getCanonicalPath() + "]").getBytes());
            return;
        }
 
        visited.add(file);
       
        // Work out the filename extension. If there isn't one, we keep
        // it as the empty string ("").
        String extension = WebServerConfig.getExtension(file);
       
        if (WebServerConfig.SSI_EXTENSIONS.contains(extension)) {
            // process this ssi page line by line
            BufferedReader reader = new BufferedReader(new FileReader(file));
            String line = null;
            while ((line = reader.readLine()) != null) {
                int startIndex;
                int endIndex;
                while ((startIndex = line.indexOf("<!--#include file=/"")) >= 0) {
                    if ((endIndex = line.indexOf("/" -->", startIndex)) > startIndex) {
                        out.write(line.substring(0, startIndex).getBytes());
                        String filename = line.substring(startIndex + 19, endIndex);
                        parse(out, visited, new File(file.getParentFile(), filename));
                        line = line.substring(endIndex + 5, line.length());
                    }
                    else {
                        out.write(line.substring(0, 19).getBytes());
                        line = line.substring(19, line.length());
                    }
                }
                out.write(line.getBytes());
                out.write(WebServerConfig.LINE_SEPARATOR);
            }
        }
        else {
            // just dish out the bytes
            BufferedInputStream reader = new BufferedInputStream(new FileInputStream(file));
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = reader.read(buffer, 0, 4096)) != -1) {
                out.write(buffer, 0, bytesRead);
            }
        }
       
        visited.remove(file);
       
    }
   
}
ServerSideScriptEngine.java:
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
 
/**
 * Provides limited support for running server side scripts.
 * The HashMap of server variables are sent to the process
 * when it is executed. While the process is outputting
 * data to standard output, this will be issued to the connecting
 * client.
 *
 * @author Copyright Paul Mutton, http://www.jibble.org/
 */
public class ServerSideScriptEngine {
 
    // This could be a lot better. Consider server side scripting a beta feature
    // for now.
   
    public static void execute(BufferedOutputStream out, HashMap serverVars, File file, String path) throws Throwable {
       
        // Place server variables into a String array.
        String[] envp = new String[serverVars.size()];
        Iterator varIt = serverVars.keySet().iterator();
        for (int i = 0; i < serverVars.size(); i++) {
            String key = (String)varIt.next();
            String value = (String)serverVars.get(key);
            envp[i] = key + "=" + value;
        }
       
        // Execute the external command
        String filename = file.toString();
        String[] cmdarray = null;
       
        if (filename.toLowerCase().endsWith(".pl")) {
            cmdarray = new String[]{"perl", filename};
        }
        else if (filename.toLowerCase().endsWith(".php")) {
            cmdarray = new String[]{"php", filename};
        }
        else {
            cmdarray = new String[]{filename};
        }
        Process process = Runtime.getRuntime().exec(cmdarray, envp, file.getParentFile());
       
        // Send the process output to the connecting client.
        InputStream in = process.getInputStream();
        byte[] buffer = new byte[4096];
        int bytesRead;
        while ((bytesRead = in.read(buffer, 0, 4096)) != -1) {
            out.write(buffer, 0, bytesRead);
        }
        in.close();
       
    }
   
}
WebServer.java:
import java.io.*;
import java.net.*;
import java.util.*;
 
/**
 * The central class to the Jibble Web Server. This is instantiated
 * by the WebServerMain class and listens for connections on the
 * specified port number before starting a new RequestThread to
 * allow connections to be dealt with concurrently.
 *
 * @author Copyright Paul Mutton, http://www.jibble.org/
 */
public class WebServer {
   
    public WebServer(String rootDir, int port) throws WebServerException {
        try {
            _rootDir = new File(rootDir).getCanonicalFile();
        }
        catch (IOException e) {
            throw new WebServerException("Unable to determine the canonical path of the web root directory.");
        }
        if (!_rootDir.isDirectory()) {
            throw new WebServerException("The specified root directory does not exist or is not a directory.");
        }
        _port = port;
    }
   
    public void activate() throws WebServerException {
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(_port);
        }
        catch (Exception e) {
            throw new WebServerException("Cannot start the web server on port " + _port + ".");
        }
       
        // Keep all RequestThreads within their own thread group for tidyness.
        ThreadGroup threadGroup = new ThreadGroup("HTTP Request Thread Group");
        while (_active) {
            try {
                // Pass the socket to a new thread so that it can be dealt with
                // while we can go and get ready to accept another connection.
                Socket socket = serverSocket.accept();
                RequestThread requestThread = new RequestThread(socket, _rootDir);
                Thread thread = new Thread(threadGroup, requestThread);
                thread.start();
            }
            catch (Exception e) {
                throw new WebServerException("Error processing new connection: " + e);
            }
        }
    }
   
    private File _rootDir;
    private int _port;
    private boolean _active = true;
 
}
WebServerConfig.java:
import java.util.*;
 
/**
 * Provides configuration to the web server. This leads to a standalone
 * jar file which requires no external configuration, but perhaps it
 * may be nice one day to allow these settings to be specified
 * externally so that a bit of flexibility may be given to the user
 * (this would also reduce the class size a bit :)
 *
 * @author Copyright Paul Mutton, http://www.jibble.org/
 */
public class WebServerConfig {
   
    private WebServerConfig() {
        // Prevent the default constructor from being called.
    }
   
    public static final String VERSION = "<a href=/"http://www.jibble.org/">Jibble Web Server 1.0</a> - An extremely small Java web server";
   
    public static final String DEFAULT_ROOT_DIRECTORY = ".";
    public static final int DEFAULT_PORT = 80;
   
    public static final String[] DEFAULT_FILES = new String[] {"index.html", "index.htm", "index.shtml", "index.shtm", "index.stm", "index.sht"};
   
    public static final byte[] LINE_SEPARATOR = "/r/n".getBytes();
   
    public static final HashSet SSI_EXTENSIONS = new HashSet();
    public static final HashMap MIME_TYPES = new HashMap();
   
    // Work out the filename extension. If there isn't one, we keep
    // it as the empty string ("").
    public static String getExtension(java.io.File file) {
        String extension = "";
        String filename = file.getName();
        int dotPos = filename.lastIndexOf(".");
        if (dotPos >= 0) {
            extension = filename.substring(dotPos);
        }
        return extension.toLowerCase();
    }
   
    static {
       
        // Set up the SSI filename extensions.
        SSI_EXTENSIONS.add(".shtml");
        SSI_EXTENSIONS.add(".shtm");
        SSI_EXTENSIONS.add(".stm");
        SSI_EXTENSIONS.add(".sht");
       
        // Set up the filename extension to mime type associations.
       
        String ps = "application/postscript";
        MIME_TYPES.put(".ai", ps);
        MIME_TYPES.put(".ps", ps);
        MIME_TYPES.put(".eps", ps);
       
        String rtf = "application/rtf";
        MIME_TYPES.put(".rtf", rtf);
       
        String au = "audio/basic";
        MIME_TYPES.put(".au", au);
        MIME_TYPES.put(".snd", au);
       
        String exe = "application/octet-stream";
        MIME_TYPES.put(".bin", exe);
        MIME_TYPES.put(".dms", exe);
        MIME_TYPES.put(".lha", exe);
        MIME_TYPES.put(".lzh", exe);
        MIME_TYPES.put(".exe", exe);
        MIME_TYPES.put(".class", exe);
       
        String doc = "application/msword";
        MIME_TYPES.put(".doc", doc);
       
        String pdf = "application/pdf";
        MIME_TYPES.put(".pdf", pdf);
       
        String ppt = "application/powerpoint";
        MIME_TYPES.put(".ppt", ppt);
       
        String smi = "application/smil";
        MIME_TYPES.put(".smi", smi);
        MIME_TYPES.put(".smil", smi);
        MIME_TYPES.put(".sml", smi);
       
        String js = "application/x-javascript";
        MIME_TYPES.put(".js", js);
       
        String zip = "application/zip";
        MIME_TYPES.put(".zip", zip);
       
        String midi = "audio/midi";
        MIME_TYPES.put(".midi", midi);
        MIME_TYPES.put(".kar", midi);
       
        String mp3 = "audio/mpeg";
        MIME_TYPES.put(".mpga", mp3);
        MIME_TYPES.put(".mp2", mp3);
        MIME_TYPES.put(".mp3", mp3);
       
        String wav = "audio/x-wav";
        MIME_TYPES.put(".wav", wav);
       
        String gif = "image/gif";
        MIME_TYPES.put(".gif", gif);
       
        String ief = "image/ief";
        MIME_TYPES.put(".ief", ief);
       
        String jpeg = "image/jpeg";
        MIME_TYPES.put(".jpeg", jpeg);
        MIME_TYPES.put(".jpg", jpeg);
        MIME_TYPES.put(".jpe", jpeg);
       
        String png = "image/png";
        MIME_TYPES.put(".png", png);
       
        String tiff = "image/tiff";
        MIME_TYPES.put(".tiff", tiff);
        MIME_TYPES.put(".tif", tiff);
       
        String vrml = "model/vrml";
        MIME_TYPES.put(".wrl", vrml);
        MIME_TYPES.put(".vrml", vrml);
       
        String css = "text/css";
        MIME_TYPES.put(".css", css);
       
        String html = "text/html";
        MIME_TYPES.put(".html", html);
        MIME_TYPES.put(".htm", html);
        MIME_TYPES.put(".shtml", html);
        MIME_TYPES.put(".shtm", html);
        MIME_TYPES.put(".stm", html);
        MIME_TYPES.put(".sht", html);
       
        String txt = "text/plain";
        MIME_TYPES.put(".txt", txt);
       MIME_TYPES.put(".inf", txt);
        MIME_TYPES.put(".nfo", txt);
       
        String xml = "text/xml";
        MIME_TYPES.put(".xml", xml);
        MIME_TYPES.put(".dtd", xml);
       
        String mpeg = "video/mpeg";
        MIME_TYPES.put(".mpeg", mpeg);
        MIME_TYPES.put(".mpg", mpeg);
        MIME_TYPES.put(".mpe", mpeg);
       
        String avi = "video/x-msvideo";
        MIME_TYPES.put(".avi", avi);       
    }
   
}
WebServerException.java:
/**
 * A custom exception class for the web server.
 *
 * @author Copyright Paul Mutton, http://www.jibble.org/
 */
public class WebServerException extends Exception {
   
    public WebServerException(String e) {
        super(e);
    }
   
}
WebServerMain.java:
/**
 * This class contains the main method for the Jibble Web Server.
 *
 * @author Copyright Paul Mutton, http://www.jibble.org/
 */
public class WebServerMain {
 
    public static void main(String[] args) {
       
        String rootDir = WebServerConfig.DEFAULT_ROOT_DIRECTORY;
        int port = WebServerConfig.DEFAULT_PORT;
       
        if (args.length > 0) {
            rootDir = args[0];
        }
       
        if (args.length > 1) {
            try {
                port = Integer.parseInt(args[1]);
            }
            catch (NumberFormatException e) {
                // Stick with the default value.
            }
        }
       
        try {
            WebServer server = new WebServer(rootDir, port);
            server.activate();
        }
        catch (WebServerException e) {
            System.out.println(e.toString());
        }
    }
 
}
原创粉丝点击