import java.io.*;
import java.net.*;
import java.util.*;
class WebServer implements HttpConstants {
protected static void p(String s) {
System.out.println(s);
}
protected static Properties props = new Properties();
static Vector threads = new Vector();
static File root;
static int port = 80;
static int timeout = 0;
static int workers = 10;
public static ServerSocket ss;
static void loadProps() throws IOException {
File f = new File(System.getProperty("user.dir")+File.separator+
"WebServer.ini");
if(f.exists()) {
InputStream is=new BufferedInputStream(new FileInputStream(f));
props.load(is);
is.close();
String r = props.getProperty("root");
if(r != null) {
root = new File(r);
if(!root.exists()) {
throw new Error(root + " doesn't exist as server root");
}
}
r = props.getProperty("port");
if(r != null) {
port = Integer.parseInt(r);
}
r = props.getProperty("timeout");
if(r != null) {
timeout = Integer.parseInt(r);
}
r = props.getProperty("workers");
if(r != null) {
workers = Integer.parseInt(r);
}
}
}
static void printProps() {
p("root="+root);
p("port="+port);
p("timeout="+timeout);
p("workers="+workers);
}
public static void main(String[] a) throws Exception {
loadProps();
printProps();
for(int i=0;i<workers;i++) {
Worker w=new Worker();
(new Thread(w, "worker #"+i)).start();
threads.addElement(w);
}
ss = new ServerSocket(port);
while (true) {
Socket s = ss.accept();
Worker w=null;
synchronized (threads) {
if(threads.isEmpty()) {
Worker ws = new Worker();
ws.setSocket(s);
(new Thread(ws, "additional worker")).start();
} else {
w = (Worker) threads.elementAt(0);
threads.removeElementAt(0);
w.setSocket(s);
}
}
}
}
}
class Worker extends WebServer implements HttpConstants, Runnable {
final static int BUF_SIZE=2048;
static final byte[] EOL={(byte)'\r', (byte)'\n' };
/* buffer to use for requests */
byte[] buf;
/* Socket to client we're handling */
private Socket s; Worker() {
buf=new byte[BUF_SIZE];
s=null;
}
synchronized void setSocket(Socket s) {
this.s=s;
notify();
}
public synchronized void run() {
while(true) {
if (s== null) {
/* nothing to do */
try{
wait();
} catch (InterruptedException e) {
/* should not happen */
continue;
}
} try {
handleClient();
} catch (Exception e) {
e.printStackTrace();
}
/* go back in wait queue if there's fewer
* than numHandler connections. */
s=null;
Vector pool = WebServer.threads;
synchronized (pool) {
if (pool.size()>= WebServer.workers) {
/* too many threads, exit this one */
return;
} else {
pool.addElement(this);
}
}
}
}
void handleClient() throws IOException {
InputStream is = new BufferedInputStream(s.getInputStream());
PrintStream ps = new PrintStream(s.getOutputStream());
/* we will only block in read for this many milliseconds
* before we fail with java.io.lnterruptedlOException,
* alt which point we will abandon the connection.
*/
s.setSoTimeout(WebServer.timeout);
s.setTcpNoDelay(true);
/* zero out the buffer from last time */
for(int i = 0; i <BUF_SIZE; i++) {
buf[i]= 0;
} try{
int nread = 0, r = 0;
outerloop:
while (nread < BUF_SIZE) {
r=is.read(buf, nread, BUF_SIZE nread);
if (r== -1) {
/* EOF */
return;
}
int i=nread;
nread +=r;
for (; i < nread; i++) {
if (buf[i]== (byte)'\n' || buf[i]== (byte)'\r') {
/* read one line */
break outerloop;
}
}
}
/* are we doing a GET or just a HEAD */
boolean doingGet;
/* beginning of file name */
int index;
if (buf[0]== (byte)'G' &&
buf[1]== (byte)'E' &&
buf[2]== (byte)'T' &&
buf[3]== (byte)' ') {
doingGet=true;
index=4
} else if (buf[0]== (byte)'H' &&
buf[1]== (byte)'E' &&
buf[2]== (byte)'A' &&
buf[3]== (byte)'D' &&
buf[4]== (byte)' ') {
doingGet=false;
index=5;
} else {
/* we don't support this method */
ps.print("HTTP/1.0 " + HTTP_BAD_METHOD +
" unsupported method type:");
ps.write(buf, 0, 5);
ps.write(EOL);
ps.flush();
s.close();
return;
}
int i = 0;
/* find the file name, from:
* GET /foo/bar.html HTTP/1.0
* extract "/foo/bar.html"
*/
for (i = index; i < nread; i++) {
if (buf[i]== (byte)' ') {
break;
}
}
String fname = (new String(buf, 0, index,
i-index)).replace('/', File.separatorChar);
if (fname.startsWith(File.separator)) {
fname = fname.substring(1);
}
File targ= new File(WebServer.root, fname);
if (targ.isDirectory()) {
File ind = new File(targ, "index.html");
if (ind.exists()) {
targ = ind;
}
}
boolean OK=printHeaders(targ, ps);
if (doingGet) {
if (OK) {
sendFile(targ, ps);
} else {
send404(targ, ps);
}
}
} finally {
s.close();
}
}
boolean printHeaders(File targ, PrintStream ps) throws IOException {
boolean ret = false;
int rCode=0;
if (!targ.exists()) {
rCode = HTTP_NOT_FOUND;
ps.print("HTTP/1.0 " + HTTP_NOT_FOUND + " not found");
ps.write(EOL);
ret = false;
} else {
rCode = HTTP_OK;
ps.print("HTTP/1.0 " + HTTP_OK + " OK");
ps.write(EOL);
ret = true;
}
ps.print("Server: Simple Java");
ps.write(EOL);
ps.print("Date: " + (new Date()));
ps.write(EOL);
if (ret) {
if (!targ.isDirectory()) {
ps.print("Content-length: "+targ.length());
ps.write(EOL);
ps.print("Last Modified: " + (new Date(targ.lastModified())));
ps.write(EOL);
String name = targ.getName();
int ind = name.lastIndexOf('.');
String ct = null;
if (ind > 0) {
ct = (String) map.get(name.substring(ind));
}
if (ct == null) {
ct = "unknown/unknown";
}
ps.print("Content-type: " + ct);
ps.write(EOL);
} else {
ps.print("Content-type: text/html");
ps.write(EOL);
}
}
return ret;
}
void send404(File targ, PrintStream ps) throws IOException {
ps.write(EOL);
ps.write(EOL);
ps.println("Not Found\n\n"+
"The requested resource was not found.\n");
}
void sendFile(File targ, PrintStream ps) throws IOException {
InputStream is = null;
ps.write(EOL);
if (targ.isDirectory()) {
return;
} else {
is = new FileInputStream(targ.getAbsolutePath());
}
try{
int n;
while ((n = is.read(buf)) > 0) {
ps.write(buf, 0, n);
}
} finally {
is.close();
}
}
/* mapping of file extensions to content-types */
static java.util.Hashtable map = new java.util.Hashtable(); static {
fillMap();
}
static void setSuffix(String k, String v) {
map.put(k, v);
}
static void fillMap() {
setSuffix("", "content/unknown");
setSuffix(".uu", "application/octet-stream");
setSuffix(".exe", "application/octet-stream");
setSuffix(".ps", "application/postscript");
setSuffix(".zip", "application/zip");
setSuffix(".sh", "application/x-shar");
setSuffix(".tar", "application/x-tar");
setSuffix(".snd", "audio/basic");
setSuffix(".au", "audio/basic");
setSuffix(".wav", "audio/x-wav");
setSuffix(".gif", "image/gif");
setSuffix(".jpg", "image/jpeg");
setSuffix(".jpeg", "image/jpeg");
setSuffix(".htm", "text/html");
setSuffix(".html", "text/html");
setSuffix(".text", "text/plain");
setSuffix(".c", "text/plain");
setSuffix(".cc", "text/plain");
setSuffix(".c++", "text/plain");
setSuffix(".h", "text/plain");
setSuffix(".pi", "text/plain");
setSuffix(".txt", "text/plain");
setSuffix(".Java", "text/plain");
}
}
interface HttpConstants {
/** 2XX: generally "OK" */
public static final int HTTP_OK = 200;
public static final int HTTP_CREATED = 201;
public static final int HTTP_ACCEPTED = 202;
public static final int HTTP_NOT_AUTHORITATIVE = 203;
public static final int HTTP_NO_CONTENT = 204;
public static final int HTTP_RESET = 205;
public static final int HTTP_PARTIAL = 206;
/** 3XX: relocation/redirect */
public static final int HTTP_MULT_CHOICE = 300;
public static final int HTTP_MOVED_PERM = 301;
public static final int HTTP_MOVED_TEMP = 302;
public static final int HTTP_SEE_OTHER = 303;
public static final int HTTP_NOT_MODIFIED = 304;
public static final int HTTP_USE_PROXY = 305;
/** 4ХХ: client error */
public static final int HTTP_BAD_REQUEST = 400;
public static final int HTTP_UNAUTHORIZED = 401;
public static final int HTTP_PAYMENT_REQUIRED = 402;
public static final int HTTP_FORBIDDEN = 403;
public static final int HTTP_NOT_FOUND = 404;
public static final int HTTP_BAD_METHOD = 405;
public static final int HTTP_NOT_ACCEPTABLE = 406;
public static final int HTTP_PROXY_AUTH = 407;
public static final int HTTP_CLIENT_TIMEOUT = 408;
public static final int HTTP_CONFLICT = 409;
public static final int HTTP_GONE = 410;
public static final int HTTP_LENGTH_REQUIRED = 411;
public static final int HTTP_PRECON_FAILED = 412;
public static final int HTTP_ENTITY_TOO_LARGE = 413;
public static final int HTTP_REQ_TOO_LONG = 414;
public static final int HTTP_UNSUPPORTED_TYPE = 415;
/**5XX: server error*/
public static final int HTTP_SERVER_ERROR = 500;
public static final int HTTP_INTERNAL_ERROR = 501;
public static final int HTTP_BAD_GATEWAY = 502;
public static final int HTTP_UNAVAILABLE = 503;
public static final int HTTP_GATEWAY_TIMEOUT = 504;
public static final int HTTP_VERSION = 505;
}