• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License.  Alternatively, the contents of this file may be used under
8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  */
15 
16 package javassist.tools.web;
17 
18 import java.net.*;
19 import java.io.*;
20 import java.util.Date;
21 import javassist.*;
22 
23 /**
24  * A web server for running sample programs.
25  *
26  * <p>This enables a Java program to instrument class files loaded by
27  * web browsers for applets.  Since the (standard) security manager
28  * does not allow an applet to create and use a class loader,
29  * instrumenting class files must be done by this web server.
30  *
31  * <p><b>Note:</b> although this class is included in the Javassist API,
32  * it is provided as a sample implementation of the web server using
33  * Javassist.  Especially, there might be security flaws in this server.
34  * Please use this with YOUR OWN RISK.
35  */
36 public class Webserver {
37     private ServerSocket socket;
38     private ClassPool classPool;
39     protected Translator translator;
40 
41     private final static byte[] endofline = { 0x0d, 0x0a };
42 
43     private final static int typeHtml = 1;
44     private final static int typeClass = 2;
45     private final static int typeGif = 3;
46     private final static int typeJpeg = 4;
47     private final static int typeText = 5;
48 
49     /**
50      * If this field is not null, the class files taken from
51      * <code>ClassPool</code> are written out under the directory
52      * specified by this field.  The directory name must not end
53      * with a directory separator.
54      */
55     public String debugDir = null;
56 
57     /**
58      * The top directory of html (and .gif, .class, ...) files.
59      * It must end with the directory separator such as "/".
60      * (For portability, "/" should be used as the directory separator.
61      * Javassist automatically translates "/" into a platform-dependent
62      * character.)
63      * If this field is null, the top directory is the current one where
64      * the JVM is running.
65      *
66      * <p>If the given URL indicates a class file and the class file
67      * is not found under the directory specified by this variable,
68      * then <code>Class.getResourceAsStream()</code> is called
69      * for searching the Java class paths.
70      */
71     public String htmlfileBase = null;
72 
73     /**
74      * Starts a web server.
75      * The port number is specified by the first argument.
76      */
main(String[] args)77     public static void main(String[] args) throws IOException {
78         if (args.length == 1) {
79             Webserver web = new Webserver(args[0]);
80             web.run();
81         }
82         else
83             System.err.println(
84                         "Usage: java javassist.tools.web.Webserver <port number>");
85     }
86 
87     /**
88      * Constructs a web server.
89      *
90      * @param port      port number
91      */
Webserver(String port)92     public Webserver(String port) throws IOException {
93         this(Integer.parseInt(port));
94     }
95 
96     /**
97      * Constructs a web server.
98      *
99      * @param port      port number
100      */
Webserver(int port)101     public Webserver(int port) throws IOException {
102         socket = new ServerSocket(port);
103         classPool = null;
104         translator = null;
105     }
106 
107     /**
108      * Requests the web server to use the specified
109      * <code>ClassPool</code> object for obtaining a class file.
110      */
setClassPool(ClassPool loader)111     public void setClassPool(ClassPool loader) {
112         classPool = loader;
113     }
114 
115     /**
116      * Adds a translator, which is called whenever a client requests
117      * a class file.
118      *
119      * @param cp        the <code>ClassPool</code> object for obtaining
120      *                  a class file.
121      * @param t         a translator.
122      */
addTranslator(ClassPool cp, Translator t)123     public void addTranslator(ClassPool cp, Translator t)
124         throws NotFoundException, CannotCompileException
125     {
126         classPool = cp;
127         translator = t;
128         t.start(classPool);
129     }
130 
131     /**
132      * Closes the socket.
133      */
end()134     public void end() throws IOException {
135         socket.close();
136     }
137 
138     /**
139      * Prints a log message.
140      */
logging(String msg)141     public void logging(String msg) {
142         System.out.println(msg);
143     }
144 
145     /**
146      * Prints a log message.
147      */
logging(String msg1, String msg2)148     public void logging(String msg1, String msg2) {
149         System.out.print(msg1);
150         System.out.print(" ");
151         System.out.println(msg2);
152     }
153 
154     /**
155      * Prints a log message.
156      */
logging(String msg1, String msg2, String msg3)157     public void logging(String msg1, String msg2, String msg3) {
158         System.out.print(msg1);
159         System.out.print(" ");
160         System.out.print(msg2);
161         System.out.print(" ");
162         System.out.println(msg3);
163     }
164 
165     /**
166      * Prints a log message with indentation.
167      */
logging2(String msg)168     public void logging2(String msg) {
169         System.out.print("    ");
170         System.out.println(msg);
171     }
172 
173     /**
174      * Begins the HTTP service.
175      */
run()176     public void run() {
177         System.err.println("ready to service...");
178         for (;;)
179             try {
180                 ServiceThread th = new ServiceThread(this, socket.accept());
181                 th.start();
182             }
183             catch (IOException e) {
184                 logging(e.toString());
185             }
186     }
187 
process(Socket clnt)188     final void process(Socket clnt) throws IOException {
189         InputStream in = new BufferedInputStream(clnt.getInputStream());
190         String cmd = readLine(in);
191         logging(clnt.getInetAddress().getHostName(),
192                 new Date().toString(), cmd);
193         while (skipLine(in) > 0){
194         }
195 
196         OutputStream out = new BufferedOutputStream(clnt.getOutputStream());
197         try {
198             doReply(in, out, cmd);
199         }
200         catch (BadHttpRequest e) {
201             replyError(out, e);
202         }
203 
204         out.flush();
205         in.close();
206         out.close();
207         clnt.close();
208     }
209 
readLine(InputStream in)210     private String readLine(InputStream in) throws IOException {
211         StringBuffer buf = new StringBuffer();
212         int c;
213         while ((c = in.read()) >= 0 && c != 0x0d)
214             buf.append((char)c);
215 
216         in.read();      /* skip 0x0a (LF) */
217         return buf.toString();
218     }
219 
skipLine(InputStream in)220     private int skipLine(InputStream in) throws IOException {
221         int c;
222         int len = 0;
223         while ((c = in.read()) >= 0 && c != 0x0d)
224             ++len;
225 
226         in.read();      /* skip 0x0a (LF) */
227         return len;
228     }
229 
230     /**
231      * Proceses a HTTP request from a client.
232      *
233      * @param out       the output stream to a client
234      * @param cmd       the command received from a client
235      */
doReply(InputStream in, OutputStream out, String cmd)236     public void doReply(InputStream in, OutputStream out, String cmd)
237         throws IOException, BadHttpRequest
238     {
239         int len;
240         int fileType;
241         String filename, urlName;
242 
243         if (cmd.startsWith("GET /"))
244             filename = urlName = cmd.substring(5, cmd.indexOf(' ', 5));
245         else
246             throw new BadHttpRequest();
247 
248         if (filename.endsWith(".class"))
249             fileType = typeClass;
250         else if (filename.endsWith(".html") || filename.endsWith(".htm"))
251             fileType = typeHtml;
252         else if (filename.endsWith(".gif"))
253             fileType = typeGif;
254         else if (filename.endsWith(".jpg"))
255             fileType = typeJpeg;
256         else
257             fileType = typeText;        // or textUnknown
258 
259         len = filename.length();
260         if (fileType == typeClass
261             && letUsersSendClassfile(out, filename, len))
262             return;
263 
264         checkFilename(filename, len);
265         if (htmlfileBase != null)
266             filename = htmlfileBase + filename;
267 
268         if (File.separatorChar != '/')
269             filename = filename.replace('/', File.separatorChar);
270 
271         File file = new File(filename);
272         if (file.canRead()) {
273             sendHeader(out, file.length(), fileType);
274             FileInputStream fin = new FileInputStream(file);
275             byte[] filebuffer = new byte[4096];
276             for (;;) {
277                 len = fin.read(filebuffer);
278                 if (len <= 0)
279                     break;
280                 else
281                     out.write(filebuffer, 0, len);
282             }
283 
284             fin.close();
285             return;
286         }
287 
288         // If the file is not found under the html-file directory,
289         // then Class.getResourceAsStream() is tried.
290 
291         if (fileType == typeClass) {
292             InputStream fin
293                 = getClass().getResourceAsStream("/" + urlName);
294             if (fin != null) {
295                 ByteArrayOutputStream barray = new ByteArrayOutputStream();
296                 byte[] filebuffer = new byte[4096];
297                 for (;;) {
298                     len = fin.read(filebuffer);
299                     if (len <= 0)
300                         break;
301                     else
302                         barray.write(filebuffer, 0, len);
303                 }
304 
305                 byte[] classfile = barray.toByteArray();
306                 sendHeader(out, classfile.length, typeClass);
307                 out.write(classfile);
308                 fin.close();
309                 return;
310             }
311         }
312 
313         throw new BadHttpRequest();
314     }
315 
checkFilename(String filename, int len)316     private void checkFilename(String filename, int len)
317         throws BadHttpRequest
318     {
319         for (int i = 0; i < len; ++i) {
320             char c = filename.charAt(i);
321             if (!Character.isJavaIdentifierPart(c) && c != '.' && c != '/')
322                 throw new BadHttpRequest();
323         }
324 
325         if (filename.indexOf("..") >= 0)
326             throw new BadHttpRequest();
327     }
328 
letUsersSendClassfile(OutputStream out, String filename, int length)329     private boolean letUsersSendClassfile(OutputStream out,
330                                           String filename, int length)
331         throws IOException, BadHttpRequest
332     {
333         if (classPool == null)
334             return false;
335 
336         byte[] classfile;
337         String classname
338             = filename.substring(0, length - 6).replace('/', '.');
339         try {
340             if (translator != null)
341                 translator.onLoad(classPool, classname);
342 
343             CtClass c = classPool.get(classname);
344             classfile = c.toBytecode();
345             if (debugDir != null)
346                 c.writeFile(debugDir);
347         }
348         catch (Exception e) {
349             throw new BadHttpRequest(e);
350         }
351 
352         sendHeader(out, classfile.length, typeClass);
353         out.write(classfile);
354         return true;
355     }
356 
sendHeader(OutputStream out, long dataLength, int filetype)357     private void sendHeader(OutputStream out, long dataLength, int filetype)
358         throws IOException
359     {
360         out.write("HTTP/1.0 200 OK".getBytes());
361         out.write(endofline);
362         out.write("Content-Length: ".getBytes());
363         out.write(Long.toString(dataLength).getBytes());
364         out.write(endofline);
365         if (filetype == typeClass)
366             out.write("Content-Type: application/octet-stream".getBytes());
367         else if (filetype == typeHtml)
368             out.write("Content-Type: text/html".getBytes());
369         else if (filetype == typeGif)
370             out.write("Content-Type: image/gif".getBytes());
371         else if (filetype == typeJpeg)
372             out.write("Content-Type: image/jpg".getBytes());
373         else if (filetype == typeText)
374             out.write("Content-Type: text/plain".getBytes());
375 
376         out.write(endofline);
377         out.write(endofline);
378     }
379 
replyError(OutputStream out, BadHttpRequest e)380     private void replyError(OutputStream out, BadHttpRequest e)
381         throws IOException
382     {
383         logging2("bad request: " + e.toString());
384         out.write("HTTP/1.0 400 Bad Request".getBytes());
385         out.write(endofline);
386         out.write(endofline);
387         out.write("<H1>Bad Request</H1>".getBytes());
388     }
389 }
390 
391 class ServiceThread extends Thread {
392     Webserver web;
393     Socket sock;
394 
ServiceThread(Webserver w, Socket s)395     public ServiceThread(Webserver w, Socket s) {
396         web = w;
397         sock = s;
398     }
399 
run()400     public void run() {
401         try {
402             web.process(sock);
403         }
404         catch (IOException e) {
405         }
406     }
407 }
408