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"); | |
0de51374 | 11 | private boolean running = false; |
13e578b1 FT |
12 | public String headcs = "UTF-8"; |
13 | ||
14 | public Server(ServerSocket sk) { | |
15 | this.sk = sk; | |
16 | } | |
17 | ||
18 | private static int readnslen(InputStream in) throws IOException { | |
19 | int ret = 0; | |
20 | while(true) { | |
21 | int c = in.read(); | |
22 | if(c == ':') | |
23 | return(ret); | |
24 | else if((c >= '0') && (c <= '9')) | |
25 | ret = (ret * 10) + (c - '0'); | |
26 | else | |
27 | throw(new InvalidRequestException("Malformed netstring length")); | |
28 | } | |
29 | } | |
30 | ||
31 | private static byte[] readns(InputStream in) throws IOException { | |
32 | byte[] buf = new byte[readnslen(in)]; | |
33 | int off = 0; | |
34 | while(off < buf.length) { | |
35 | int ret = in.read(buf, off, buf.length - off); | |
36 | if(ret < 0) | |
37 | throw(new InvalidRequestException("Unexpected EOS in netstring")); | |
38 | off += ret; | |
39 | } | |
40 | if(in.read() != ',') | |
41 | throw(new InvalidRequestException("Unterminated netstring")); | |
42 | return(buf); | |
43 | } | |
44 | ||
45 | private Map<String, String> readhead(InputStream in) throws IOException { | |
46 | byte[] rawhead = readns(in); | |
47 | String head = new String(rawhead, headcs); | |
48 | Map<String, String> ret = new HashMap<String, String>(); | |
49 | int p = 0; | |
50 | while(true) { | |
51 | int p2 = head.indexOf(0, p); | |
52 | if(p2 < 0) { | |
53 | if(p == head.length()) | |
54 | return(ret); | |
55 | throw(new InvalidRequestException("Malformed headers")); | |
56 | } | |
57 | String key = head.substring(p, p2); | |
58 | int p3 = head.indexOf(0, p2 + 1); | |
59 | if(p3 < 0) | |
60 | throw(new InvalidRequestException("Malformed headers")); | |
61 | String val = head.substring(p2 + 1, p3); | |
62 | ret.put(key, val); | |
63 | p = p3 + 1; | |
64 | } | |
65 | } | |
66 | ||
67 | private boolean checkhead(Map<String, String> head) { | |
68 | if(!head.containsKey("SCGI") || !head.get("SCGI").equals("1")) | |
69 | return(false); | |
70 | return(true); | |
71 | } | |
72 | ||
73 | protected abstract void handle(Map<String, String> head, Socket sk) throws Exception; | |
74 | ||
75 | private void serve(Socket sk) { | |
76 | try { | |
77 | try { | |
78 | InputStream in = sk.getInputStream(); | |
79 | Map<String, String> head = readhead(in); | |
80 | if(!checkhead(head)) | |
81 | return; | |
82 | try { | |
83 | handle(head, sk); | |
84 | } catch(Exception e) { | |
85 | logger.log(Level.WARNING, "Could not handle request", e); | |
86 | return; | |
87 | } | |
88 | sk = null; | |
89 | } finally { | |
90 | if(sk != null) | |
91 | sk.close(); | |
92 | } | |
93 | } catch(IOException e) { | |
94 | logger.log(Level.WARNING, "I/O error encountered while serving SCGI request", e); | |
95 | } | |
96 | } | |
97 | ||
98 | public void run() { | |
99 | try { | |
100 | try { | |
0de51374 FT |
101 | synchronized(this) { |
102 | if(running) | |
103 | throw(new IllegalStateException("SCGI server is already running")); | |
104 | running = true; | |
105 | } | |
106 | while(running) { | |
13e578b1 FT |
107 | Socket nsk = sk.accept(); |
108 | serve(nsk); | |
109 | } | |
110 | } finally { | |
111 | sk.close(); | |
112 | } | |
113 | } catch(IOException e) { | |
0de51374 FT |
114 | if((e instanceof SocketException) && !running) { |
115 | /* Assume that stop() has closed the socket. */ | |
116 | } else { | |
117 | logger.log(Level.SEVERE, "SCGI server encountered I/O error", e); | |
118 | } | |
119 | } finally { | |
120 | shutdown(); | |
121 | running = false; | |
13e578b1 FT |
122 | } |
123 | } | |
0de51374 FT |
124 | |
125 | public void stop() { | |
126 | try { | |
127 | running = false; | |
128 | sk.close(); | |
129 | } catch(IOException e) { | |
130 | throw(new RuntimeException(e)); | |
131 | } | |
132 | } | |
133 | ||
134 | protected void shutdown() { | |
135 | } | |
13e578b1 | 136 | } |