diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index 554eb2e29..a70aad950 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -861,6 +861,25 @@ public class Editor extends JFrame implements RunnerListener { JMenu menu = new JMenu("Help"); JMenuItem item; + item = new JMenuItem("Web Server Test"); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + //WebServer ws = new WebServer(); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + try { + int port = WebServer.launch("/Users/fry/coconut/processing/build/shared/reference.zip"); + Base.openURL("http://127.0.0.1:" + port + "/reference/setup_.html"); + + } catch (IOException e1) { + e1.printStackTrace(); + } + } + }); + } + }); + menu.add(item); + /* item = new JMenuItem("Browser Test"); item.addActionListener(new ActionListener() { diff --git a/app/src/processing/app/WebServer.java b/app/src/processing/app/WebServer.java index 71f3dbbb0..01f6cc39e 100644 --- a/app/src/processing/app/WebServer.java +++ b/app/src/processing/app/WebServer.java @@ -1,51 +1,34 @@ +package processing.app; + import java.io.*; import java.net.*; import java.util.*; +import java.util.zip.*; + +//import javax.swing.SwingUtilities; /** * An example of a very simple, multi-threaded HTTP server. - * Implementation notes are in WebServer.html, and also - * as comments in the source code. - *

* Taken from this article on java.sun.com. */ -class WebServer implements HttpConstants { - - /* static class data/methods */ - - /* print to stdout */ - protected static void p(String s) { - System.out.println(s); - } - - /* print to the log file */ - protected static void log(String s) { - synchronized (log) { - log.println(s); - log.flush(); - } - } - - static PrintStream log = null; - /* our server's configuration information is stored - * in these properties - */ - protected static Properties props = new Properties(); +public class WebServer implements HttpConstants { /* Where worker threads stand idle */ static Vector threads = new Vector(); /* the web server's virtual root */ - static File root; + //static File root; /* timeout on client connections */ - static int timeout = 0; + static int timeout = 10000; /* max # worker threads */ static int workers = 5; +// static PrintStream log = System.out; - /* load www-server.properties from java.home */ + + /* static void loadProps() throws IOException { File f = new File (System.getProperty("java.home")+File.separator+ @@ -78,7 +61,7 @@ class WebServer implements HttpConstants { } } - /* if no properties were specified, choose defaults */ + // if no properties were specified, choose defaults if (root == null) { root = new File(System.getProperty("user.dir")); } @@ -99,58 +82,109 @@ class WebServer implements HttpConstants { p("timeout="+timeout); p("workers="+workers); } + */ - public static void main(String[] a) throws Exception { - int port = 8080; - if (a.length > 0) { - port = Integer.parseInt(a[0]); - } - loadProps(); - printProps(); - /* start worker threads */ + + /* print to stdout */ +// protected static void p(String s) { +// System.out.println(s); +// } + + /* print to the log file */ + protected static void log(String s) { + if (false) { + System.out.println(s); + } +// synchronized (log) { +// log.println(s); +// log.flush(); +// } + } + + + //public static void main(String[] a) throws Exception { + static public int launch(String zipPath) throws IOException { + final ZipFile zip = new ZipFile(zipPath); + final HashMap entries = new HashMap(); + Enumeration en = zip.entries(); + while (en.hasMoreElements()) { + ZipEntry entry = (ZipEntry) en.nextElement(); + entries.put(entry.getName(), entry); + } + +// if (a.length > 0) { +// port = Integer.parseInt(a[0]); +// } +// loadProps(); +// printProps(); + // start worker threads for (int i = 0; i < workers; ++i) { - Worker w = new Worker(); - (new Thread(w, "worker #"+i)).start(); + WebServerWorker w = new WebServerWorker(zip, entries); + Thread t = new Thread(w, "Web Server Worker #" + i); + t.start(); threads.addElement(w); } - ServerSocket ss = new ServerSocket(port); - while (true) { + final int port = 8080; - Socket s = ss.accept(); - - Worker w = null; - synchronized (threads) { - if (threads.isEmpty()) { - Worker ws = new Worker(); + //SwingUtilities.invokeLater(new Runnable() { + Runnable r = new Runnable() { + public void run() { + try { + ServerSocket ss = new ServerSocket(port); + while (true) { + Socket s = ss.accept(); + WebServerWorker w = null; + synchronized (threads) { + if (threads.isEmpty()) { + WebServerWorker ws = new WebServerWorker(zip, entries); ws.setSocket(s); (new Thread(ws, "additional worker")).start(); - } else { - w = (Worker) threads.elementAt(0); + } else { + w = (WebServerWorker) threads.elementAt(0); threads.removeElementAt(0); w.setSocket(s); + } } + } + } catch (IOException e) { + e.printStackTrace(); } - } + } + }; + new Thread(r).start(); +// }); + return port; } } -class Worker extends WebServer implements HttpConstants, Runnable { +class WebServerWorker /*extends WebServer*/ implements HttpConstants, Runnable { + ZipFile zip; + HashMap entries; + final static int BUF_SIZE = 2048; - static final byte[] EOL = {(byte)'\r', (byte)'\n' }; + 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; - } + WebServerWorker(ZipFile zip, HashMap entries) { + this.entries = entries; + this.zip = zip; + + buf = new byte[BUF_SIZE]; + s = null; + } +// Worker() { +// buf = new byte[BUF_SIZE]; +// s = null; +// } +// synchronized void setSocket(Socket s) { this.s = s; notify(); @@ -188,40 +222,35 @@ class Worker extends WebServer implements HttpConstants, Runnable { } } + 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.InterruptedIOException, - * at which point we will abandon the connection. - */ + // we will only block in read for this many milliseconds + // before we fail with java.io.InterruptedIOException, + // at which point we will abandon the connection. s.setSoTimeout(WebServer.timeout); s.setTcpNoDelay(true); - /* zero out the buffer from last time */ + // zero out the buffer from last time for (int i = 0; i < BUF_SIZE; i++) { buf[i] = 0; } try { - /* We only support HTTP GET/HEAD, and don't - * support any fancy HTTP options, - * so we're only interested really in - * the first line. - */ + // We only support HTTP GET/HEAD, and don't support any fancy HTTP + // options, so we're only interested really in the first line. int nread = 0, r = 0; outerloop: while (nread < BUF_SIZE) { r = is.read(buf, nread, BUF_SIZE - nread); if (r == -1) { - /* EOF */ - return; + return; // EOF } int i = nread; nread += r; for (; i < nread; i++) { if (buf[i] == (byte)'\n' || buf[i] == (byte)'\r') { - /* read one line */ - break outerloop; + break outerloop; // read one line } } } @@ -264,8 +293,23 @@ outerloop: break; } } - String fname = (new String(buf, 0, index, - i-index)).replace('/', File.separatorChar); + + String fname = new String(buf, index, i-index); + // get the zip entry, remove the front slash + ZipEntry entry = entries.get(fname.substring(1)); + //System.out.println(fname + " " + entry); + boolean ok = printHeaders(entry, ps); + if (entry != null) { + InputStream stream = zip.getInputStream(entry); + if (doingGet && ok) { + sendFile(stream, ps); + } + } else { + send404(ps); + } + /* + String fname = + (new String(buf, 0, index, i-index)).replace('/', File.separatorChar); if (fname.startsWith(File.separator)) { fname = fname.substring(1); } @@ -284,17 +328,68 @@ outerloop: send404(targ, ps); } } + */ } finally { s.close(); } } + + boolean printHeaders(ZipEntry targ, PrintStream ps) throws IOException { + boolean ret = false; + int rCode = 0; + if (targ == null) { + 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; + } + if (targ != null) { + WebServer.log("From " +s.getInetAddress().getHostAddress()+": GET " + targ.getName()+" --> "+rCode); + } + ps.print("Server: Processing Documentation Server"); + ps.write(EOL); + ps.print("Date: " + (new Date())); + ps.write(EOL); + if (ret) { + if (!targ.isDirectory()) { + ps.print("Content-length: " + targ.getSize()); + ps.write(EOL); + ps.print("Last Modified: " + new Date(targ.getTime())); + 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) { + //System.err.println("unknown content type " + name.substring(ind)); + ct = "application/x-unknown-content-type"; + } + ps.print("Content-type: " + ct); + ps.write(EOL); + } else { + ps.print("Content-type: text/html"); + ps.write(EOL); + } + } + ps.write(EOL); // adding another newline here [fry] + return ret; + } + + 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.print("HTTP/1.0 " + HTTP_NOT_FOUND + " Not Found"); ps.write(EOL); ret = false; } else { @@ -303,18 +398,16 @@ outerloop: ps.write(EOL); ret = true; } - log("From " +s.getInetAddress().getHostAddress()+": GET " + - targ.getAbsolutePath()+"-->"+rCode); + WebServer.log("From " +s.getInetAddress().getHostAddress()+": GET " + targ.getAbsolutePath()+"-->"+rCode); 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.print("Content-length: " + targ.length()); ps.write(EOL); - ps.print("Last Modified: " + (new - Date(targ.lastModified()))); + ps.print("Last Modified: " + new Date(targ.lastModified())); ps.write(EOL); String name = targ.getName(); int ind = name.lastIndexOf('.'); @@ -335,13 +428,17 @@ outerloop: return ret; } - void send404(File targ, PrintStream ps) throws IOException { + + void send404(PrintStream ps) throws IOException { + ps.write(EOL); + ps.write(EOL); + ps.print("

404 Not Found

"+ + "The requested resource was not found."); 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); @@ -351,7 +448,11 @@ outerloop: } else { is = new FileInputStream(targ.getAbsolutePath()); } - + sendFile(is, ps); + } + + + void sendFile(InputStream is, PrintStream ps) throws IOException { try { int n; while ((n = is.read(buf)) > 0) { @@ -374,6 +475,7 @@ outerloop: static void fillMap() { setSuffix("", "content/unknown"); + setSuffix(".uu", "application/octet-stream"); setSuffix(".exe", "application/octet-stream"); setSuffix(".ps", "application/postscript"); @@ -383,19 +485,24 @@ outerloop: 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(".css", "text/css"); + setSuffix(".java", "text/javascript"); + + setSuffix(".txt", "text/plain"); + setSuffix(".java", "text/plain"); + setSuffix(".c", "text/plain"); setSuffix(".cc", "text/plain"); setSuffix(".c++", "text/plain"); setSuffix(".h", "text/plain"); setSuffix(".pl", "text/plain"); - setSuffix(".txt", "text/plain"); - setSuffix(".java", "text/plain"); } void listDirectory(File dir, PrintStream ps) throws IOException { @@ -415,6 +522,7 @@ outerloop: } + interface HttpConstants { /** 2XX: generally "OK" */ public static final int HTTP_OK = 200; @@ -459,6 +567,3 @@ interface HttpConstants { public static final int HTTP_GATEWAY_TIMEOUT = 504; public static final int HTTP_VERSION = 505; } - - -