Added initial SCGI server and a handler for serving JARs from the filesystem.
[jsvc.git] / src / dolda / jsvc / scgi / ScgiRequest.java
CommitLineData
13e578b1
FT
1package dolda.jsvc.scgi;
2
3import java.io.*;
4import java.net.*;
5import java.util.*;
6import dolda.jsvc.*;
7import dolda.jsvc.util.*;
8
9public class ScgiRequest extends ResponseBuffer {
10 final Socket sk;
11 private final Map<String, String> environ;
12 private final InputStream in;
13 private final String method, path;
14 private final URL url, context;
15 private MultiMap<String, String> params = null;
16 private MultiMap<String, String> inhead = new HeaderTreeMap();
17
18 public ScgiRequest(Socket sk, Map<String, String> environ) throws IOException {
19 this.sk = sk;
20 this.environ = environ;
21 for(Map.Entry<String, String> var : environ.entrySet()) {
22 String k = var.getKey();
23 if((k.length() > 5) && k.substring(0, 5).equals("HTTP_")) {
24 StringBuilder buf = new StringBuilder();
25 boolean f = true;
26 for(int i = 5; i < k.length(); i++) {
27 char c = k.charAt(i);
28 if(c == '_') {
29 buf.append('-');
30 f = true;
31 } else if(f) {
32 buf.append(Character.toUpperCase(c));
33 f = false;
34 } else {
35 buf.append(Character.toLowerCase(c));
36 }
37 }
38 inhead.add(buf.toString(), var.getValue());
39 }
40 }
41 long len;
42 {
43 String h = environ.get("CONTENT_LENGTH");
44 if(h == null) {
45 len = 0;
46 } else {
47 try {
48 len = Long.parseLong(h);
49 } catch(NumberFormatException e) {
50 throw(new InvalidRequestException("Invalid Content-Length header: " + h));
51 }
52 }
53 }
54 this.in = new LimitInputStream(sk.getInputStream(), len);
55 path = environ.get("PATH_INFO");
56 if(path == null)
57 throw(new InvalidRequestException("Missing PATH_INFO"));
58 method = environ.get("REQUEST_METHOD");
59 if(method == null)
60 throw(new InvalidRequestException("Missing REQUEST_METHOD"));
61 {
62 /* Ewwww, this is disgusting! */
63 String scheme = "http";
64 if(environ.get("HTTPS") != null)
65 scheme = "https";
66 int port = -1;
67 String host = environ.get("HTTP_HOST");
68 if((host == null) || (host.length() < 1)) {
69 if((host = environ.get("SERVER_NAME")) == null)
70 throw(new InvalidRequestException("Both HTTP_HOST and SERVER name are missing"));
71 String portnum = environ.get("SERVER_PORT");
72 if(portnum == null)
73 throw(new InvalidRequestException("Missing SERVER_PORT"));
74 try {
75 port = Integer.parseInt(portnum);
76 } catch(NumberFormatException e) {
77 throw(new InvalidRequestException("Bad SERVER_PORT: " + portnum));
78 }
79 if((port == 80) && scheme.equals("http"))
80 port = -1;
81 else if((port == 443) && scheme.equals("https"))
82 port = -1;
83 } else {
84 int p;
85 if((host.charAt(0) == '[') && ((p = host.indexOf(']', 1)) > 1)) {
86 String newhost = host.substring(1, p);
87 if((p = host.indexOf(':', p + 1)) >= 0) {
88 try {
89 port = Integer.parseInt(host.substring(p + 1));
90 } catch(NumberFormatException e) {}
91 }
92 host = newhost;
93 } else if((p = host.indexOf(':')) >= 0) {
94 try {
95 port = Integer.parseInt(host.substring(p + 1));
96 host = host.substring(0, p);
97 } catch(NumberFormatException e) {}
98 }
99 }
100 String nm = environ.get("SCRIPT_NAME");
101 if(nm == null)
102 throw(new InvalidRequestException("Missing SCRIPT_NAME"));
103 String q = environ.get("QUERY_STRING");
104 if(q != null)
105 q = "?" + q;
106 else
107 q = "";
108 try {
109 url = new URL(scheme, host, port, nm + path + q);
110 if(nm.charAt(nm.length() - 1) != '/')
111 nm += "/"; /* XXX? */
112 context = new URL(scheme, host, port, nm);
113 } catch(MalformedURLException e) {
114 throw(new Error(e));
115 }
116 }
117 }
118
119 public MultiMap<String, String> inheaders() {
120 return(inhead);
121 }
122
123 public ServerContext ctx() {
124 return(ThreadContext.current().server());
125 }
126
127 public InputStream input() {
128 return(in);
129 }
130
131 public URL url() {
132 return(url);
133 }
134
135 public URL rooturl() {
136 return(context);
137 }
138
139 public String path() {
140 return(path);
141 }
142
143 public String method() {
144 return(method);
145 }
146
147 public MultiMap<String, String> params() {
148 if(params == null)
149 params = Params.stdparams(this);
150 return(params);
151 }
152
153 public SocketAddress localaddr() {
154 String portnum = environ.get("SERVER_PORT");
155 int port = -1;
156 try {
157 if(portnum != null)
158 port = Integer.parseInt(portnum);
159 } catch(NumberFormatException e) {}
160 if(port < 0)
161 return(null); /* XXX? */
162 String addr;
163 addr = environ.get("X_ASH_SERVER_ADDRESS");
164 if(addr == null)
165 return(new InetSocketAddress(port)); /* XXX? */
166 else
167 return(new InetSocketAddress(addr, port));
168 }
169
170 public SocketAddress remoteaddr() {
171 String addr;
172 String portnum;
173 addr = environ.get("REMOTE_ADDR");
174 portnum = environ.get("X_ASH_PORT");
175 int port = -1;
176 try {
177 if(portnum != null)
178 port = Integer.parseInt(portnum);
179 } catch(NumberFormatException e) {}
180 if((addr != null) && (port >= 0))
181 return(new InetSocketAddress(addr, port));
182 return(null); /* XXX? */
183 }
184
185 private void checkstring(String s) {
186 for(int i = 0; i < s.length(); i++) {
187 char c = s.charAt(i);
188 if((c < 32) || (c >= 128))
189 throw(new RuntimeException("Invalid header string: " + s));
190 }
191 }
192
193 protected void backflush() throws IOException {
194 Writer out = new OutputStreamWriter(realoutput(), Misc.ascii);
195 out.write(String.format("Status: %d %s\n", respcode, resptext));
196 for(Map.Entry<String, String> e : outheaders().entrySet()) {
197 String k = e.getKey();
198 String v = e.getValue();
199 checkstring(k);
200 checkstring(v);
201 out.write(String.format("%s: %s\n", k, v));
202 }
203 out.write("\n");
204 out.flush();
205 }
206
207 protected OutputStream realoutput() {
208 try {
209 return(sk.getOutputStream());
210 } catch(IOException e) {
211 /* It is not obvious why this would happen, so I'll wait
212 * until I know whatever might happen to try and implement
213 * meaningful behavior. */
214 throw(new RuntimeException(e));
215 }
216 }
217}