// Copyright 2004-2014 Jim Voris // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // package com.qumasoft.webserver; import com.qumasoft.qvcslib.Utility; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.net.Socket; import java.util.Date; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; class Worker implements Runnable, HttpConstants { static final int BUF_SIZE = 2048; static final byte[] EOL = {(byte) '\r', (byte) '\n'}; /* * buffer to use for requests */ private byte[] buf; /* * Socket to client we're handling */ private Socket socket; // Create our logger object private static Logger logger = Logger.getLogger("com.qumasoft.webserver"); Worker() { buf = new byte[BUF_SIZE]; socket = null; } synchronized void setSocket(Socket s) { this.socket = s; notifyAll(); } @Override public synchronized void run() { while (true) { if (socket == null) { /* * nothing to do */ try { wait(); } catch (InterruptedException e) { /* * should not happen */ continue; } } try { handleClient(); } catch (Exception e) { logger.log(Level.WARNING, Utility.expandStackTraceToString(e)); } /* * go back in wait queue if there'm_Socket fewer than numHandler connections. */ socket = null; Vector<Worker> pool = WebServer.getWorkerThreads(); synchronized (pool) { if (pool.size() >= WebServer.getMaxWorkerThreadCount()) { /* * too many workerThreads, exit this one */ return; } else { pool.addElement(this); } } } } void handleClient() throws IOException { InputStream is = new BufferedInputStream(socket.getInputStream()); PrintStream ps = new PrintStream(socket.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. */ socket.setSoTimeout(WebServer.getClientTimeout()); socket.setTcpNoDelay(true); /* * 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. */ int nread = 0, r = 0; outerloop: while (nread < BUF_SIZE) { r = is.read(buf, nread, BUF_SIZE - nread); if (r == -1) { /* * EOF */ return; } int i = nread; nread += r; for (; i < nread; i++) { if (buf[i] == (byte) '\n' || buf[i] == (byte) '\r') { /* * read one line */ break outerloop; } } } /* * beginning of file name */ // <editor-fold> int index; if (buf[0] == (byte) 'G' && buf[1] == (byte) 'E' && buf[2] == (byte) 'T' && buf[3] == (byte) ' ') { index = 4; } else if (buf[0] == (byte) 'H' && buf[1] == (byte) 'E' && buf[2] == (byte) 'A' && buf[3] == (byte) 'D' && buf[4] == (byte) ' ') { index = 5; // </editor-fold> } else { /* * we don't support this method */ ps.print("HTTP/1.0 " + HTTP_BAD_METHOD + " unsupported method type: "); ps.write(buf, 0, 5); ps.write(EOL); ps.flush(); socket.close(); return; } int i = 0; /* * find the file name, from: GET /foo/bar.html HTTP/1.0 extract "/foo/bar.html" */ for (i = index; i < nread; i++) { if (buf[i] == (byte) ' ') { break; } } String fname = new String(buf, index, i - index); if (fname.compareTo("/") == 0) { fname = "/index.html"; } String resourceName = "/ServerWebSite" + fname; InputStream streamFromJar = this.getClass().getResourceAsStream(resourceName); if (printStreamHeaders(streamFromJar, resourceName, ps)) { sendStream(streamFromJar, ps); } else { send404(resourceName, ps); } } finally { socket.close(); } } boolean printStreamHeaders(InputStream inputStream, String fileName, PrintStream ps) throws IOException { boolean ret = false; int rCode = 0; if (inputStream == 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; } WebServer.log("From " + socket.getInetAddress().getHostAddress() + ": GET " + fileName + "-->" + rCode); ps.print("Server: Simple QVCS-Enterprise Java Web Server"); ps.write(EOL); ps.print("Date: " + (new Date())); ps.write(EOL); if (ret) { int size = inputStream.available(); ps.print("Content-length: " + size); ps.write(EOL); ps.print("Last Modified: " + (new Date())); ps.write(EOL); String name = fileName; int ind = name.lastIndexOf('.'); String ct = null; if (ind > 0) { ct = (String) map.get(name.substring(ind).toLowerCase()); } if (ct == null) { ct = "unknown/unknown"; } ps.print("Content-type: " + ct); ps.write(EOL); } return ret; } void send404(String fileName, PrintStream ps) throws IOException { ps.write(EOL); ps.write(EOL); ps.println("Not Found\n\n" + "The requested resource'" + fileName + "'was not found.\n"); } void sendStream(InputStream is, PrintStream ps) throws IOException { if (is != null) { ps.write(EOL); try { int n; while ((n = is.read(buf)) > 0) { ps.write(buf, 0, n); } } finally { is.close(); } } } /* * mapping of file extensions to content-types */ private static java.util.Hashtable map = new java.util.Hashtable(); static { fillMap(); } static void setSuffix(String k, String v) { map.put(k, v); } static void fillMap() { setSuffix("", "content/unknown"); setSuffix(".uu", "application/octet-stream"); setSuffix(".exe", "application/octet-stream"); setSuffix(".ps", "application/postscript"); setSuffix(".zip", "application/zip"); setSuffix(".sh", "application/x-shar"); setSuffix(".tar", "application/x-tar"); 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(".c", "text/plain"); setSuffix(".cc", "text/plain"); setSuffix(".c++", "text/plain"); setSuffix(".cpp", "text/plain"); setSuffix(".h", "text/plain"); setSuffix(".pl", "text/plain"); setSuffix(".txt", "text/plain"); setSuffix(".java", "text/plain"); } }