Sunday, December 23, 2012

Tiny Embeddable Java Webserver

This is a tiny embedded Java webserver that'll serve pages from a local drive or a .jar (if it is within the jar file).
While it may not be completely useful to do this a very easy extension would include a Map from Strings to Objects, and call the .toString() method on the object and use that as output.
By creating something like this, you would get a simple CGI like server that you could use to write difficult GUI code, using styling from CSS and Javascript.

Source Code

/**
* A local embedded webserver, will run from a .jar file, and serve files
* from the root.
*
* @author Joseph Lewis <joehms22@gmail.com>
*/

import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;


public class Webserver extends Thread
{
private static final FileNotFoundException FILE_NOT_FOUND = new FileNotFoundException();
private static final byte[] ERROR_404 = "HTTP/1.0 404 Not Found\r\nContent-type: text/plain\r\n\r\n404 Error, page not found.".getBytes();
private static final int PORT_NUM = 8_000;
public static Webserver SERVER = null;
private ServerSocket serverSocket;

public static Webserver getInstance()
{
// Make sure we only ever have one server running.
if(SERVER == null)
SERVER = new Webserver();

return SERVER;
}

private Webserver()
{
try
{
serverSocket = new ServerSocket(PORT_NUM);

System.err.println("Starting server on port: " + PORT_NUM);

// If you're running this embedded, you will probably want to
// make the thread a daemon so it quits when your main program
// quits rather than serves forever.
//setDaemon(true);

start();

} catch (IOException e) {
e.printStackTrace();
}
}

@Override
public void run()
{
// Starts a new thread for every request we get.
while (true)
try {
new RequestHandler(serverSocket.accept());
} catch (Exception e) {
e.printStackTrace();
}
}

public static void main(String[] args)
{
Webserver.getInstance();
}


private class RequestHandler extends Thread
{
private final Socket socket;
private final ClassLoader m_loader;
private static final String DEFAULT_INDEX_PAGE = "index.html";

// Start the thread in the constructor
public RequestHandler(Socket s) {
socket = s;
m_loader = Webserver.getInstance().getClass().getClassLoader();
start();
}

@Override
public void run()
{
try
{
DataInputStream in = new DataInputStream(socket.getInputStream());
PrintStream out = new PrintStream(new BufferedOutputStream(socket.getOutputStream()));


// Parse the HTML request
StringTokenizer st = new StringTokenizer(in.readLine());

try
{
String requestType = st.nextToken();
String filename = normalizeFilePath(st.nextToken());

System.err.println("Request: " + requestType + " " + filename);


InputStream f = m_loader.getResourceAsStream(filename);

if(f == null)
throw FILE_NOT_FOUND;

out.print("HTTP/1.0 200 OK\r\nContent-type: " + getMimeType(filename) + "\r\n\r\n");

// Send file contents to client, then close the connection
final byte[] a = new byte[2048];
int n;
while ((n = f.read(a)) > 0)
out.write(a, 0, n);


} catch (NoSuchElementException | FileNotFoundException e) {
out.print(ERROR_404);
}

out.close();
in.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}

public String getMimeType(String filename)
{
String ext = filename.substring(filename.indexOf(".") + 1);

switch(ext)
{
case "html": case "htm":
return "text/html";
case "css":
return "text/css";
case "java":
return "text/x-java";
case "class":
return "application/x-java";
case "png":
return "image/png";
case "jpg": case "jpeg":
return "image/jpeg";
case "js":
return "application/javascript";
default:
return "application/octet-stream";
}
}

public String normalizeFilePath(String filename) throws FileNotFoundException
{
// Remove GET params.
if(filename.contains("?"))
filename = filename.split("?", 2)[0];

// Make requests to a folder show the index.html file.
if(filename.endsWith("/"))
filename += DEFAULT_INDEX_PAGE;

// Remove leading /
while (filename.indexOf("/") == 0)
filename = filename.substring(1);

// Check for illegal characters to prevent access to superdirectories
if (filename.contains("..") || filename.contains(":") || filename.contains("|"))
throw FILE_NOT_FOUND;

return filename;
}
}
}

No comments:

Post a Comment