Commit | Line | Data |
---|---|---|
13e578b1 FT |
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 | } |