Added initial SCGI server and a handler for serving JARs from the filesystem.
[jsvc.git] / src / dolda / jsvc / scgi / Server.java
1 package dolda.jsvc.scgi;
2
3 import java.util.logging.*;
4 import java.io.*;
5 import java.net.*;
6 import java.util.*;
7
8 public abstract class Server implements Runnable {
9     private final ServerSocket sk;
10     private final Logger logger = Logger.getLogger("dolda.jsvc.scgi");
11     public String headcs = "UTF-8";
12     
13     public Server(ServerSocket sk) {
14         this.sk = sk;
15     }
16     
17     private static int readnslen(InputStream in) throws IOException {
18         int ret = 0;
19         while(true) {
20             int c = in.read();
21             if(c == ':')
22                 return(ret);
23             else if((c >= '0') && (c <= '9'))
24                 ret = (ret * 10) + (c - '0');
25             else
26                 throw(new InvalidRequestException("Malformed netstring length"));
27         }
28     }
29     
30     private static byte[] readns(InputStream in) throws IOException {
31         byte[] buf = new byte[readnslen(in)];
32         int off = 0;
33         while(off < buf.length) {
34             int ret = in.read(buf, off, buf.length - off);
35             if(ret < 0)
36                 throw(new InvalidRequestException("Unexpected EOS in netstring"));
37             off += ret;
38         }
39         if(in.read() != ',')
40             throw(new InvalidRequestException("Unterminated netstring"));
41         return(buf);
42     }
43
44     private Map<String, String> readhead(InputStream in) throws IOException {
45         byte[] rawhead = readns(in);
46         String head = new String(rawhead, headcs);
47         Map<String, String> ret = new HashMap<String, String>();
48         int p = 0;
49         while(true) {
50             int p2 = head.indexOf(0, p);
51             if(p2 < 0) {
52                 if(p == head.length())
53                     return(ret);
54                 throw(new InvalidRequestException("Malformed headers"));
55             }
56             String key = head.substring(p, p2);
57             int p3 = head.indexOf(0, p2 + 1);
58             if(p3 < 0)
59                 throw(new InvalidRequestException("Malformed headers"));
60             String val = head.substring(p2 + 1, p3);
61             ret.put(key, val);
62             p = p3 + 1;
63         }
64     }
65
66     private boolean checkhead(Map<String, String> head) {
67         if(!head.containsKey("SCGI") || !head.get("SCGI").equals("1"))
68             return(false);
69         return(true);
70     }
71
72     protected abstract void handle(Map<String, String> head, Socket sk) throws Exception;
73
74     private void serve(Socket sk) {
75         try {
76             try {
77                 InputStream in = sk.getInputStream();
78                 Map<String, String> head = readhead(in);
79                 if(!checkhead(head))
80                     return;
81                 try {
82                     handle(head, sk);
83                 } catch(Exception e) {
84                     logger.log(Level.WARNING, "Could not handle request", e);
85                     return;
86                 }
87                 sk = null;
88             } finally {
89                 if(sk != null)
90                     sk.close();
91             }
92         } catch(IOException e) {
93             logger.log(Level.WARNING, "I/O error encountered while serving SCGI request", e);
94         }
95     }
96
97     public void run() {
98         try {
99             try {
100                 while(true) {
101                     Socket nsk = sk.accept();
102                     serve(nsk);
103                 }
104             } finally {
105                 sk.close();
106             }
107         } catch(IOException e) {
108             logger.log(Level.SEVERE, "SCGI server encountered I/O error", e);
109         }
110     }
111 }