You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Webserver.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. /*
  2. * Javassist, a Java-bytecode translator toolkit.
  3. * Copyright (C) 1999-2003 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. package javassist.web;
  16. import java.net.*;
  17. import java.io.*;
  18. import java.util.Date;
  19. import javassist.ClassPool;
  20. /**
  21. * A web server for Javassist.
  22. *
  23. * <p>This enables a Java program to instrument class files loaded by
  24. * web browsers for applets. Since the (standard) security manager
  25. * does not allow an applet to create and use a class loader,
  26. * instrumenting class files must be done by this web server.
  27. *
  28. * <p>Programmers can register a <code>ClassPool</code> object for
  29. * instrumenting class files when they are sent to web browsers.
  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. private final static byte[] endofline = { 0x0d, 0x0a };
  40. private byte[] filebuffer = new byte[4096];
  41. private final static int typeHtml = 1;
  42. private final static int typeClass = 2;
  43. private final static int typeGif = 3;
  44. private final static int typeJpeg = 4;
  45. private final static int typeText = 5;
  46. private final static int typeUnknown = 6;
  47. /**
  48. * If this field is not null, the class files taken from
  49. * <code>ClassPool</code> are written out under the directory
  50. * specified by this field. The directory name must not end
  51. * with a directory separator.
  52. */
  53. public String debugDir = null;
  54. /**
  55. * The top directory of html (and .gif, .class, ...) files.
  56. * It must end with the directory separator such as "/".
  57. * (For portability, "/" should be used as the directory separator.
  58. * Javassist automatically translates "/" into a platform-dependent
  59. * character.)
  60. * If this field is null, the top directory is the current one where
  61. * the JVM is running.
  62. *
  63. * <p>If the given URL indicates a class file and the class file
  64. * is not found under the directory specified by this variable,
  65. * then <code>Class.getResourceAsStream()</code> is called
  66. * for searching the Java class paths.
  67. */
  68. public String htmlfileBase = null;
  69. /**
  70. * Starts a web server.
  71. * The port number is specified by the first argument.
  72. */
  73. public static void main(String[] args) throws IOException {
  74. if (args.length == 1) {
  75. Webserver web = new Webserver(args[0]);
  76. web.run();
  77. }
  78. else
  79. System.err.println(
  80. "Usage: java javassist.web.Webserver <port number>");
  81. }
  82. /**
  83. * Constructs a web server.
  84. *
  85. * @param port port number
  86. */
  87. public Webserver(String port) throws IOException {
  88. this(Integer.parseInt(port));
  89. }
  90. /**
  91. * Constructs a web server.
  92. *
  93. * @param port port number
  94. */
  95. public Webserver(int port) throws IOException {
  96. socket = new ServerSocket(port);
  97. classPool = null;
  98. }
  99. /**
  100. * Requests the web server to use the specified
  101. * <code>ClassPool</code> object for obtaining a class file.
  102. */
  103. public void setClassPool(ClassPool loader) {
  104. classPool = loader;
  105. }
  106. /**
  107. * Closes the socket.
  108. */
  109. public void end() throws IOException {
  110. socket.close();
  111. }
  112. /**
  113. * Prints a log message.
  114. */
  115. public void logging(String msg) {
  116. System.out.println(msg);
  117. }
  118. /**
  119. * Prints a log message.
  120. */
  121. public void logging(String msg1, String msg2) {
  122. System.out.print(msg1);
  123. System.out.print(" ");
  124. System.out.println(msg2);
  125. }
  126. /**
  127. * Prints a log message.
  128. */
  129. public void logging(String msg1, String msg2, String msg3) {
  130. System.out.print(msg1);
  131. System.out.print(" ");
  132. System.out.print(msg2);
  133. System.out.print(" ");
  134. System.out.println(msg3);
  135. }
  136. /**
  137. * Prints a log message with indentation.
  138. */
  139. public void logging2(String msg) {
  140. System.out.print(" ");
  141. System.out.println(msg);
  142. }
  143. /**
  144. * Begins the HTTP service.
  145. */
  146. public void run() {
  147. System.err.println("ready to service...");
  148. for (;;)
  149. try {
  150. ServiceThread th = new ServiceThread(this, socket.accept());
  151. th.start();
  152. }
  153. catch (IOException e) {
  154. logging(e.toString());
  155. }
  156. }
  157. final void process(Socket clnt) throws IOException {
  158. InputStream in = new BufferedInputStream(clnt.getInputStream());
  159. String cmd = readLine(in);
  160. logging(clnt.getInetAddress().getHostName(),
  161. new Date().toString(), cmd);
  162. while (skipLine(in) > 0){
  163. }
  164. OutputStream out = new BufferedOutputStream(clnt.getOutputStream());
  165. try {
  166. doReply(in, out, cmd);
  167. }
  168. catch (BadHttpRequest e) {
  169. replyError(out, e);
  170. }
  171. out.flush();
  172. in.close();
  173. out.close();
  174. clnt.close();
  175. }
  176. private String readLine(InputStream in) throws IOException {
  177. StringBuffer buf = new StringBuffer();
  178. int c;
  179. while ((c = in.read()) >= 0 && c != 0x0d)
  180. buf.append((char)c);
  181. in.read(); /* skip 0x0a (LF) */
  182. return buf.toString();
  183. }
  184. private int skipLine(InputStream in) throws IOException {
  185. int c;
  186. int len = 0;
  187. while ((c = in.read()) >= 0 && c != 0x0d)
  188. ++len;
  189. in.read(); /* skip 0x0a (LF) */
  190. return len;
  191. }
  192. /**
  193. * Proceses a HTTP request from a client.
  194. *
  195. * @param out the output stream to a client
  196. * @param cmd the command received from a client
  197. */
  198. public void doReply(InputStream in, OutputStream out, String cmd)
  199. throws IOException, BadHttpRequest
  200. {
  201. int len;
  202. int fileType;
  203. String filename, urlName;
  204. if (cmd.startsWith("GET /"))
  205. filename = urlName = cmd.substring(5, cmd.indexOf(' ', 5));
  206. else
  207. throw new BadHttpRequest();
  208. if (filename.endsWith(".class"))
  209. fileType = typeClass;
  210. else if (filename.endsWith(".html") || filename.endsWith(".htm"))
  211. fileType = typeHtml;
  212. else if (filename.endsWith(".gif"))
  213. fileType = typeGif;
  214. else if (filename.endsWith(".jpg"))
  215. fileType = typeJpeg;
  216. else
  217. fileType = typeText; // or textUnknown
  218. len = filename.length();
  219. if (fileType == typeClass
  220. && letUsersSendClassfile(out, filename, len))
  221. return;
  222. checkFilename(filename, len);
  223. if (htmlfileBase != null)
  224. filename = htmlfileBase + filename;
  225. if (File.separatorChar != '/')
  226. filename = filename.replace('/', File.separatorChar);
  227. File file = new File(filename);
  228. if (file.canRead()) {
  229. sendHeader(out, file.length(), fileType);
  230. FileInputStream fin = new FileInputStream(file);
  231. for (;;) {
  232. len = fin.read(filebuffer);
  233. if (len <= 0)
  234. break;
  235. else
  236. out.write(filebuffer, 0, len);
  237. }
  238. fin.close();
  239. return;
  240. }
  241. // If the file is not found under the html-file directory,
  242. // then Class.getResourceAsStream() is tried.
  243. if (fileType == typeClass) {
  244. InputStream fin
  245. = getClass().getResourceAsStream("/" + urlName);
  246. if (fin != null) {
  247. ByteArrayOutputStream barray = new ByteArrayOutputStream();
  248. for (;;) {
  249. len = fin.read(filebuffer);
  250. if (len <= 0)
  251. break;
  252. else
  253. barray.write(filebuffer, 0, len);
  254. }
  255. byte[] classfile = barray.toByteArray();
  256. sendHeader(out, classfile.length, typeClass);
  257. out.write(classfile);
  258. fin.close();
  259. return;
  260. }
  261. }
  262. throw new BadHttpRequest();
  263. }
  264. private void checkFilename(String filename, int len)
  265. throws BadHttpRequest
  266. {
  267. for (int i = 0; i < len; ++i) {
  268. char c = filename.charAt(i);
  269. if (!Character.isJavaIdentifierPart(c) && c != '.' && c != '/')
  270. throw new BadHttpRequest();
  271. }
  272. if (filename.indexOf("..") >= 0)
  273. throw new BadHttpRequest();
  274. }
  275. private boolean letUsersSendClassfile(OutputStream out,
  276. String filename, int length)
  277. throws IOException, BadHttpRequest
  278. {
  279. if (classPool == null)
  280. return false;
  281. byte[] classfile;
  282. String classname
  283. = filename.substring(0, length - 6).replace('/', '.');
  284. try {
  285. classfile = classPool.write(classname);
  286. if (debugDir != null)
  287. classPool.writeFile(classname, debugDir);
  288. }
  289. catch (Exception e) {
  290. throw new BadHttpRequest(e);
  291. }
  292. sendHeader(out, classfile.length, typeClass);
  293. out.write(classfile);
  294. return true;
  295. }
  296. private void sendHeader(OutputStream out, long dataLength, int filetype)
  297. throws IOException
  298. {
  299. out.write("HTTP/1.0 200 OK".getBytes());
  300. out.write(endofline);
  301. out.write("Content-Length: ".getBytes());
  302. out.write(Long.toString(dataLength).getBytes());
  303. out.write(endofline);
  304. if (filetype == typeClass)
  305. out.write("Content-Type: application/octet-stream".getBytes());
  306. else if (filetype == typeHtml)
  307. out.write("Content-Type: text/html".getBytes());
  308. else if (filetype == typeGif)
  309. out.write("Content-Type: image/gif".getBytes());
  310. else if (filetype == typeJpeg)
  311. out.write("Content-Type: image/jpg".getBytes());
  312. else if (filetype == typeText)
  313. out.write("Content-Type: text/plain".getBytes());
  314. out.write(endofline);
  315. out.write(endofline);
  316. }
  317. private void replyError(OutputStream out, BadHttpRequest e)
  318. throws IOException
  319. {
  320. logging2("bad request: " + e.toString());
  321. out.write("HTTP/1.0 400 Bad Request".getBytes());
  322. out.write(endofline);
  323. out.write(endofline);
  324. out.write("<H1>Bad Request</H1>".getBytes());
  325. }
  326. }
  327. class ServiceThread extends Thread {
  328. Webserver web;
  329. Socket sock;
  330. public ServiceThread(Webserver w, Socket s) {
  331. web = w;
  332. sock = s;
  333. }
  334. public void run() {
  335. try {
  336. web.process(sock);
  337. }
  338. catch (IOException e) {
  339. }
  340. }
  341. }