diff options
Diffstat (limited to 'src/main/javassist/web/Webserver.java')
-rw-r--r-- | src/main/javassist/web/Webserver.java | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/src/main/javassist/web/Webserver.java b/src/main/javassist/web/Webserver.java new file mode 100644 index 00000000..95ad05a2 --- /dev/null +++ b/src/main/javassist/web/Webserver.java @@ -0,0 +1,400 @@ +/* + * This file is part of the Javassist toolkit. + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * either http://www.mozilla.org/MPL/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Javassist. + * + * The Initial Developer of the Original Code is Shigeru Chiba. Portions + * created by Shigeru Chiba are Copyright (C) 1999-2003 Shigeru Chiba. + * All Rights Reserved. + * + * Contributor(s): + * + * The development of this software is supported in part by the PRESTO + * program (Sakigake Kenkyu 21) of Japan Science and Technology Corporation. + */ + +package javassist.web; + +import java.net.*; +import java.io.*; +import java.util.Hashtable; +import java.util.Date; +import javassist.ClassPool; +import javassist.Translator; + +/** + * A web server for Javassist. + * + * <p>This enables a Java program to instrument class files loaded by + * web browsers for applets. Since the (standard) security manager + * does not allow an applet to create and use a class loader, + * instrumenting class files must be done by this web server. + * + * <p>Programmers can register a <code>ClassPool</code> object for + * instrumenting class files when they are sent to web browsers. + * + * <p><b>Note:</b> although this class is included in the Javassist API, + * it is provided as a sample implementation of the web server using + * Javassist. Especially, there might be security flaws in this server. + * Please use this with YOUR OWN RISK. + */ +public class Webserver { + private ServerSocket socket; + private ClassPool classPool; + + private final static byte[] endofline = { 0x0d, 0x0a }; + private byte[] filebuffer = new byte[4096]; + + private final static int typeHtml = 1; + private final static int typeClass = 2; + private final static int typeGif = 3; + private final static int typeJpeg = 4; + private final static int typeText = 5; + private final static int typeUnknown = 6; + + /** + * If this field is not null, the class files taken from + * <code>ClassPool</code> are written out under the directory + * specified by this field. The directory name must not end + * with a directory separator. + */ + public String debugDir = null; + + /** + * The top directory of html (and .gif, .class, ...) files. + * It must end with the directory separator such as "/". + * (For portability, "/" should be used as the directory separator. + * Javassist automatically translates "/" into a platform-dependent + * character.) + * If this field is null, the top directory is the current one where + * the JVM is running. + * + * <p>If the given URL indicates a class file and the class file + * is not found under the directory specified by this variable, + * then <code>Class.getResourceAsStream()</code> is called + * for searching the Java class paths. + */ + public String htmlfileBase = null; + + /** + * Starts a web server. + * The port number is specified by the first argument. + */ + public static void main(String[] args) throws IOException { + if (args.length == 1) { + Webserver web = new Webserver(args[0]); + web.run(); + } + else + System.err.println( + "Usage: java javassist.web.Webserver <port number>"); + } + + /** + * Constructs a web server. + * + * @param port port number + */ + public Webserver(String port) throws IOException { + this(Integer.parseInt(port)); + } + + /** + * Constructs a web server. + * + * @param port port number + */ + public Webserver(int port) throws IOException { + socket = new ServerSocket(port); + classPool = null; + } + + /** + * Requests the web server to use the specified + * <code>ClassPool</code> object for obtaining a class file. + */ + public void setClassPool(ClassPool loader) { + classPool = loader; + } + + /** + * Closes the socket. + */ + public void end() throws IOException { + socket.close(); + } + + /** + * Prints a log message. + */ + public void logging(String msg) { + System.out.println(msg); + } + + /** + * Prints a log message. + */ + public void logging(String msg1, String msg2) { + System.out.print(msg1); + System.out.print(" "); + System.out.println(msg2); + } + + /** + * Prints a log message. + */ + public void logging(String msg1, String msg2, String msg3) { + System.out.print(msg1); + System.out.print(" "); + System.out.print(msg2); + System.out.print(" "); + System.out.println(msg3); + } + + /** + * Prints a log message with indentation. + */ + public void logging2(String msg) { + System.out.print(" "); + System.out.println(msg); + } + + /** + * Begins the HTTP service. + */ + public void run() { + System.err.println("ready to service..."); + for (;;) + try { + ServiceThread th = new ServiceThread(this, socket.accept()); + th.start(); + } + catch (IOException e) { + logging(e.toString()); + } + } + + final void process(Socket clnt) throws IOException { + InputStream in = new BufferedInputStream(clnt.getInputStream()); + String cmd = readLine(in); + logging(clnt.getInetAddress().getHostName(), + new Date().toString(), cmd); + while (skipLine(in) > 0){ + } + + OutputStream out = new BufferedOutputStream(clnt.getOutputStream()); + try { + doReply(in, out, cmd); + } + catch (BadHttpRequest e) { + replyError(out, e); + } + + out.flush(); + in.close(); + out.close(); + clnt.close(); + } + + private String readLine(InputStream in) throws IOException { + StringBuffer buf = new StringBuffer(); + int c; + while ((c = in.read()) >= 0 && c != 0x0d) + buf.append((char)c); + + in.read(); /* skip 0x0a (LF) */ + return buf.toString(); + } + + private int skipLine(InputStream in) throws IOException { + int c; + int len = 0; + while ((c = in.read()) >= 0 && c != 0x0d) + ++len; + + in.read(); /* skip 0x0a (LF) */ + return len; + } + + /** + * Proceses a HTTP request from a client. + * + * @param out the output stream to a client + * @param cmd the command received from a client + */ + public void doReply(InputStream in, OutputStream out, String cmd) + throws IOException, BadHttpRequest + { + int len; + int fileType; + String filename, urlName; + + if (cmd.startsWith("GET /")) + filename = urlName = cmd.substring(5, cmd.indexOf(' ', 5)); + else + throw new BadHttpRequest(); + + if (filename.endsWith(".class")) + fileType = typeClass; + else if (filename.endsWith(".html") || filename.endsWith(".htm")) + fileType = typeHtml; + else if (filename.endsWith(".gif")) + fileType = typeGif; + else if (filename.endsWith(".jpg")) + fileType = typeJpeg; + else + fileType = typeText; // or textUnknown + + len = filename.length(); + if (fileType == typeClass + && letUsersSendClassfile(out, filename, len)) + return; + + checkFilename(filename, len); + if (htmlfileBase != null) + filename = htmlfileBase + filename; + + if (File.separatorChar != '/') + filename = filename.replace('/', File.separatorChar); + + File file = new File(filename); + if (file.canRead()) { + sendHeader(out, file.length(), fileType); + FileInputStream fin = new FileInputStream(file); + for (;;) { + len = fin.read(filebuffer); + if (len <= 0) + break; + else + out.write(filebuffer, 0, len); + } + + fin.close(); + return; + } + + // If the file is not found under the html-file directory, + // then Class.getResourceAsStream() is tried. + + if (fileType == typeClass) { + InputStream fin + = getClass().getResourceAsStream("/" + urlName); + if (fin != null) { + ByteArrayOutputStream barray = new ByteArrayOutputStream(); + for (;;) { + len = fin.read(filebuffer); + if (len <= 0) + break; + else + barray.write(filebuffer, 0, len); + } + + byte[] classfile = barray.toByteArray(); + sendHeader(out, classfile.length, typeClass); + out.write(classfile); + fin.close(); + return; + } + } + + throw new BadHttpRequest(); + } + + private void checkFilename(String filename, int len) + throws BadHttpRequest + { + for (int i = 0; i < len; ++i) { + char c = filename.charAt(i); + if (!Character.isJavaIdentifierPart(c) && c != '.' && c != '/') + throw new BadHttpRequest(); + } + + if (filename.indexOf("..") >= 0) + throw new BadHttpRequest(); + } + + private boolean letUsersSendClassfile(OutputStream out, + String filename, int length) + throws IOException, BadHttpRequest + { + if (classPool == null) + return false; + + byte[] classfile; + String classname + = filename.substring(0, length - 6).replace('/', '.'); + try { + classfile = classPool.write(classname); + if (debugDir != null) + classPool.writeFile(classname, debugDir); + } + catch (Exception e) { + throw new BadHttpRequest(e); + } + + sendHeader(out, classfile.length, typeClass); + out.write(classfile); + return true; + } + + private void sendHeader(OutputStream out, long dataLength, int filetype) + throws IOException + { + out.write("HTTP/1.0 200 OK".getBytes()); + out.write(endofline); + out.write("Content-Length: ".getBytes()); + out.write(Long.toString(dataLength).getBytes()); + out.write(endofline); + if (filetype == typeClass) + out.write("Content-Type: application/octet-stream".getBytes()); + else if (filetype == typeHtml) + out.write("Content-Type: text/html".getBytes()); + else if (filetype == typeGif) + out.write("Content-Type: image/gif".getBytes()); + else if (filetype == typeJpeg) + out.write("Content-Type: image/jpg".getBytes()); + else if (filetype == typeText) + out.write("Content-Type: text/plain".getBytes()); + + out.write(endofline); + out.write(endofline); + } + + private void replyError(OutputStream out, BadHttpRequest e) + throws IOException + { + logging2("bad request: " + e.toString()); + out.write("HTTP/1.0 400 Bad Request".getBytes()); + out.write(endofline); + out.write(endofline); + out.write("<H1>Bad Request</H1>".getBytes()); + } +} + +class ServiceThread extends Thread { + Webserver web; + Socket sock; + + public ServiceThread(Webserver w, Socket s) { + web = w; + sock = s; + } + + public void run() { + try { + web.process(sock); + } + catch (IOException e) { + } + } +} |