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