Guest User

async

a guest
Oct 17th, 2013
134
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 10.63 KB | None | 0 0
  1. // Read Write Handler
  2.  
  3. import java.io.DataInputStream;
  4. import java.io.File;
  5. import java.io.FileInputStream;
  6. import java.io.FileNotFoundException;
  7. import java.io.IOException;
  8. import java.io.UnsupportedEncodingException;
  9. import java.nio.ByteBuffer;
  10. import java.nio.channels.FileChannel;
  11. import java.nio.channels.SelectionKey;
  12. import java.nio.channels.SocketChannel;
  13. import java.text.ParseException;
  14. import java.text.SimpleDateFormat;
  15. import java.util.Date;
  16. import java.util.Iterator;
  17. import java.util.Map;
  18.  
  19. public class HTTPReadWriteHandler implements SocketReadWriteHandler {
  20.  
  21.     private State state;
  22.     private Dispatcher d;
  23.     private SocketChannel client;
  24.     private String documentRoot, serverName;
  25.     private Map<String, ByteBuffer> cache;
  26.     private ByteBuffer in, out;
  27.  
  28.     private HTTPRequestV2 req;
  29.     private StringBuffer ln;
  30.  
  31.     private String fPath = "";
  32.  
  33.     private FileChannel f;
  34.     private ByteBuffer mapped;
  35.     private long fileSize;
  36.     private boolean sendFile;
  37.     private long pos;
  38.     private boolean inCache;
  39.     private long written;
  40.     private FileInputStream inputStream;
  41.  
  42.     // CGI
  43.     private boolean isCGI;
  44.     private String pathInfo = "";
  45.     private String queryString = "";
  46.  
  47.     private SimpleDateFormat s;
  48.  
  49.     public HTTPReadWriteHandler(Dispatcher d, SocketChannel client,
  50.             String documentRoot, String serverName,
  51.             Map<String, ByteBuffer> cache) {
  52.         state = State.READING_REQUEST;
  53.         this.d = d;
  54.         this.client = client;
  55.         this.documentRoot = documentRoot;
  56.         this.serverName = serverName;
  57.         this.cache = cache;
  58.  
  59.         in = ByteBuffer.allocate(4096);
  60.         out = ByteBuffer.allocate(4096);
  61.  
  62.         req = new HTTPRequestV2();
  63.         ln = new StringBuffer();
  64.         s = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z"); // date format
  65.  
  66.         inCache = true;
  67.         pos = 0;
  68.         fileSize = 0;
  69.         written = 0;
  70.     }
  71.  
  72.     public State state() {
  73.         return state;
  74.     }
  75.  
  76.     public void setState(State s) {
  77.         state = s;
  78.     }
  79.  
  80.     public int initialOperations() {
  81.         // We have to read first before being able to write.
  82.         return SelectionKey.OP_READ;
  83.     }
  84.  
  85.     public void handleRead(SelectionKey key) throws IOException {
  86.         int read = client.read(in);
  87.         if (read == -1) {
  88.             state = State.GENERATING_RESPONSE;
  89.         } else {
  90.             in.flip();
  91.             while (state == State.READING_REQUEST && in.hasRemaining()
  92.                     && ln.length() < 8096) {// Same max as
  93.                 // apache
  94.                 char c = (char) in.get();
  95.                 ln.append(c);
  96.                 if (c == '\n' && ln.charAt(ln.length() - 2) == '\r') {
  97.                     // We've got a line from the header
  98.                     int k = req.append(ln.toString());
  99.                     ln.setLength(0);
  100.                     if (k == 0) {
  101.                         state = State.GENERATING_RESPONSE;
  102.                         break;
  103.                     }
  104.                 }
  105.             }
  106.         }
  107.         in.clear();
  108.         if (state == State.GENERATING_RESPONSE)
  109.             generateResponse();
  110.     }
  111.  
  112.     public void handleWrite(SelectionKey key) throws IOException {
  113.         client.write(out); // Write the headers to the stream
  114.         if (state == State.SENDING_RESPONSE && out.remaining() == 0) {
  115.             // We are done writing the headers
  116.             if (sendFile && (mapped == null)) {
  117.                 // Send the file via direct transfer
  118.                 System.err.println("DT: " + fPath + " " + pos + "/" + fileSize);
  119.                 long transferred = f.transferTo(pos, fileSize, client);
  120.                 pos += transferred;
  121.                 System.err.println("DT: " + fPath + " " + pos + "/" + fileSize);
  122.             } else if (mapped != null){
  123.                 // Send the file (in mapped from either filesystem or from
  124.                 // cache)
  125.                 System.err.println("MAPPED: " + fPath + " " + mapped.position() + "/" + mapped.limit());
  126.                 written += client.write(mapped);
  127.                 System.err.println("MAPPED: " + fPath + " " + written + "/" + mapped.limit());
  128.             }
  129.         }
  130.         if (out.remaining() == 0
  131.                 && ((mapped == null && pos == fileSize) || (mapped != null && mapped
  132.                         .remaining() == 0))) {
  133.             // We are done transferring the file!
  134.             System.err.println(pos + " " + fileSize);
  135.             assert (pos == fileSize) || (mapped.position() == mapped.limit()) : "File not sent.";
  136.             // Must reset position if from cache
  137.             if (inCache) {
  138.                 mapped.position(0);
  139.             }
  140.             if (sendFile) {
  141.                 cache();
  142.                 f.close();
  143.                 inputStream.close();
  144.             }
  145.             state = State.SOCKET_CLOSED;
  146.             d.getKey(client).cancel();
  147.             client.close();
  148.         }
  149.     }
  150.  
  151.     private void generateResponse() throws IOException,
  152.             InvalidCGIResponseException {
  153.         assert req.isComplete() : "Request is not complete.";
  154.         try {
  155.             getFile();
  156.             processFile();
  157.         } catch (FileNotFoundException e) {
  158.             try {
  159.                 writeResponse(404, "Not Found");
  160.                 writeHeaderField("", "");
  161.             } catch (UnsupportedEncodingException e1) {
  162.                 e1.printStackTrace();
  163.             }
  164.         }
  165.         // Done generating the header
  166.         state = State.SENDING_RESPONSE;
  167.         out.flip();
  168.         d.getKey(client).interestOps(SelectionKey.OP_WRITE);
  169.     }
  170.    
  171.     private void getFile() throws IOException {
  172.         String[] parts = req.filePath.split("/");
  173.         String possible = documentRoot;
  174.         File f;
  175.         int i = 0;
  176.        
  177.         // find the first actual file
  178.         while (!(f = new File(possible)).isFile() && i < parts.length)
  179.             possible += "/" + parts[i++];
  180.  
  181.         if (f.isDirectory()) {
  182.             // We have to resolve to index
  183.             if (isMobile()) // mobile)
  184.                 f = new File(f.getAbsolutePath() + "/" + "m_index.html");
  185.             else
  186.                 f = new File(f.getAbsolutePath() + "/" + "index.html");
  187.         }
  188.  
  189.         if (!f.exists() || !(f.canRead() || f.canExecute()))
  190.             throw new FileNotFoundException();
  191.        
  192.         // possible now is /../../..File
  193.         fPath = f.getAbsolutePath();
  194.        
  195.         if (f.canExecute()) {
  196.             isCGI = true;
  197.             for (; i < parts.length; i++)
  198.                 pathInfo += "/" + parts[i];
  199.             queryString = parts[i - 1].split("\\?").length > 1 ? parts[i - 1]
  200.                     .split("\\?")[1] : "";
  201.         } else {
  202.             if (i != parts.length)
  203.                 throw new FileNotFoundException();
  204.             if ((this.mapped = cache.get(fPath)) == null) {
  205.                 this.inputStream = new FileInputStream(fPath);
  206.                 this.f = inputStream.getChannel();
  207.                 this.fileSize = this.f.size();
  208.                 if(this.fileSize > 50000) { // We're only going to map large files, we can direct transfer everything else
  209.                     this.mapped = this.f.map(FileChannel.MapMode.READ_ONLY, 0,
  210.                             this.fileSize);
  211.                 }
  212.                 inCache = false;
  213.             }
  214.         }
  215.     }
  216.  
  217.     private void processFile() throws IOException {
  218.         if (isCGI) {
  219.             processCGI();
  220.             sendFile = false;
  221.         } else {
  222.             sendFile = true;
  223.             // Check the date
  224.             if (!hasBeenUpdated()) {
  225.                 writeResponse(304, "Not Modified");
  226.                 writeHeaderField("Date", s.format(new Date())); // RFC 1123
  227.                 writeHeaderField("Server", serverName);
  228.                 writeHeaderField("Connection", "close");
  229.                 writeHeaderField("Last-Modified",
  230.                         s.format(new Date(new File(fPath).lastModified())));
  231.                 writeHeaderField("", "");
  232.                 sendFile = false;
  233.                 return;
  234.             }
  235.             // In cache
  236.             if (inCache()) {
  237.                 writeResponse(200, "OK");
  238.                 writeHeaderField("Date", s.format(new Date())); // RFC 1123 time
  239.                 writeHeaderField("Server", serverName);
  240.                 writeHeaderField("Connection", "close");
  241.                 writeHeaderField("Last-Modified",
  242.                         s.format(new Date(new File(fPath).lastModified())));
  243.                 writeHeaderField("Content-Length", mapped.limit() + "");
  244.                 writeHeaderField("", "");
  245.                 sendFile = false;
  246.                 return;
  247.             }
  248.  
  249.             // Other wise we're sending the file normally
  250.             writeResponse(200, "OK");
  251.             writeHeaderField("Date", s.format(new Date())); // RFC 1123 time
  252.             writeHeaderField("Server", serverName);
  253.             writeHeaderField("Connection", "close");
  254.             writeHeaderField("Last-Modified",
  255.                     s.format(new Date(new File(fPath).lastModified())));
  256.             writeHeaderField("Content-Length", f.size() + "");
  257.             writeHeaderField("", "");
  258.             return;
  259.         }
  260.     }
  261.  
  262.     private boolean inCache() {
  263.         return inCache;
  264.     }
  265.  
  266.     private boolean hasBeenUpdated() {
  267.         String date;
  268.         if ((date = req.header.get("if-modified-since")) != null) {
  269.             try {
  270.                 Date dt = s.parse(date);
  271.                 if (!dt.after(new Date())) {
  272.                     if (dt.after(new Date(new File(fPath).lastModified()))
  273.                             || dt.equals(new Date(new File(fPath)
  274.                                     .lastModified()))) {
  275.                         return false;
  276.                     }
  277.                 }
  278.             } catch (ParseException e) {
  279.             }
  280.         }
  281.         return true;
  282.     }
  283.  
  284.     private void writeResponse(int code, String message)
  285.             throws UnsupportedEncodingException {
  286.         out.put(("HTTP/1.0 " + code + " " + message + "\r\n")
  287.                 .getBytes("US-ASCII"));
  288.     }
  289.  
  290.     private void writeHeaderField(String arg, String val)
  291.             throws UnsupportedEncodingException {
  292.         if (arg.equals(""))
  293.             out.put("\r\n".getBytes("US-ASCII"));
  294.         else
  295.             out.put((arg + ": " + val + "\r\n").getBytes("US-ASCII"));
  296.     }
  297.  
  298.     private void processCGI() throws IOException {
  299.         ProcessBuilder p = new ProcessBuilder(fPath);
  300.         p.redirectErrorStream(true);
  301.         Map<String, String> env = p.environment();
  302.         env.put("GATEWAY_INTERFACE", "CGI/1.1");
  303.         env.put("REMOTE_ADDR", client.socket().getInetAddress().toString());
  304.         env.put("QUERY_STRING", queryString);
  305.         env.put("REQUEST_METHOD", "GET");
  306.         env.put("SERVER_NAME", serverName);
  307.         env.put("SERVER_PORT", client.socket().getPort() + "");
  308.         env.put("SERVER_PROTOCOL", "HTTP");
  309.         env.put("PATH_INFO", pathInfo);
  310.         Iterator<String> it = req.header.keySet().iterator();
  311.         while (it.hasNext()) {
  312.             String key = (String) it.next();
  313.             String val = req.header.get(key);
  314.             env.put("HTTP_" + key.replace('-', '_').toUpperCase(), val);
  315.         }
  316.         Process proc = p.start();
  317.         DataInputStream ds = new DataInputStream(proc.getInputStream());
  318.  
  319.         // The CGI file MUST output at least the content type header
  320.         int c;
  321.         String ln = "";
  322.         while ((c = ds.read()) != '\n') {
  323.             ln += (char) c;
  324.         }
  325.         if (!ln.startsWith("HTTP")) // Is it the correct response?
  326.             writeResponse(200, "OK");
  327.         else
  328.             out.put((ln + "\r\n").getBytes("US-ASCII"));
  329.         while (true) { // Read the rest of the headers
  330.             ln = "";
  331.             while ((c = ds.read()) != '\n') {
  332.                 if (c == -1)
  333.                     throw new InvalidCGIResponseException();
  334.                 ln += (char) c;
  335.             }
  336.             if (ln.equals("")) // end of header
  337.                 break;
  338.             out.put((ln + "\r\n").getBytes("US-ASCII"));
  339.         }
  340.         writeHeaderField("", ""); // writes \r\n
  341.         int count;
  342.         byte[] buffer = new byte[8192];
  343.         while ((count = ds.read(buffer)) != -1) {
  344.             out.put(buffer, 0, count);
  345.         }
  346.     }
  347.  
  348.     private boolean isMobile() {
  349.         String ua = "";
  350.         if ((ua = req.header.get("user-agent")) != null) {
  351.             if (ua.contains("iPhone"))
  352.                 return true;
  353.         }
  354.         return false;
  355.     }
  356.  
  357.     private void cache() throws IOException {
  358.         synchronized (cache) {
  359.             cache.put(
  360.                     fPath,
  361.                     (mapped != null) ? (ByteBuffer)mapped.position(0) : f.map(
  362.                             FileChannel.MapMode.READ_ONLY, 0, this.fileSize));
  363.         }
  364.         f.close();
  365.     }
  366.  
  367.     @Override
  368.     public void handleException(SelectionKey key) {
  369.         System.err.println("Read or write exception.");
  370.         key.cancel();
  371.         try {
  372.             ((SocketChannel) key.channel()).close();
  373.         } catch (IOException e) {
  374.             e.printStackTrace();
  375.         }
  376.     }
  377.  
  378. }
Advertisement
Add Comment
Please, Sign In to add comment