File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\server\web\WebThread.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.server.web;
8
9 import java.io.BufferedInputStream;
10 import java.io.BufferedOutputStream;
11 import java.io.File;
12 import java.io.FileOutputStream;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.io.OutputStream;
16 import java.io.RandomAccessFile;
17 import java.net.Socket;
18 import java.net.UnknownHostException;
19 import java.util.Iterator;
20 import java.util.Locale;
21 import java.util.Properties;
22 import java.util.StringTokenizer;
23 import org.h2.constant.SysProperties;
24 import org.h2.engine.Constants;
25 import org.h2.message.TraceSystem;
26 import org.h2.util.IOUtils;
27 import org.h2.util.NetUtils;
28 import org.h2.util.StringUtils;
29 import org.h2.util.Utils;
30
31 /**
32 * For each connection to a session, an object of this class is created.
33 * This class is used by the H2 Console.
34 */
35 class WebThread extends WebApp implements Runnable {
36
37 protected OutputStream output;
38 protected final Socket socket;
39 private final Thread thread;
40 private InputStream input;
41 private int headerBytes;
42 private String ifModifiedSince;
43
44 WebThread(Socket socket, WebServer server) {
45 super(server);
46 this.socket = socket;
47 thread = new Thread(this, "H2 Console thread");
48 }
49
50 /**
51 * Start the thread.
52 */
53 void start() {
54 thread.start();
55 }
56
57 /**
58 * Wait until the thread is stopped.
59 *
60 * @param millis the maximum number of milliseconds to wait
61 */
62 void join(int millis) throws InterruptedException {
63 thread.join(millis);
64 }
65
66 /**
67 * Close the connection now.
68 */
69 void stopNow() {
70 this.stop = true;
71 try {
72 socket.close();
73 } catch (IOException e) {
74 // ignore
75 }
76 }
77
78 private String getAllowedFile(String requestedFile) {
79 if (!allow()) {
80 return "notAllowed.jsp";
81 }
82 if (requestedFile.length() == 0) {
83 return "index.do";
84 }
85 return requestedFile;
86 }
87
88 public void run() {
89 try {
90 input = new BufferedInputStream(socket.getInputStream());
91 output = new BufferedOutputStream(socket.getOutputStream());
92 while (!stop) {
93 if (!process()) {
94 break;
95 }
96 }
97 } catch (Exception e) {
98 TraceSystem.traceThrowable(e);
99 }
100 IOUtils.closeSilently(output);
101 IOUtils.closeSilently(input);
102 try {
103 socket.close();
104 } catch (IOException e) {
105 // ignore
106 } finally {
107 server.remove(this);
108 }
109 }
110
111 @SuppressWarnings("unchecked")
112 private boolean process() throws IOException {
113 boolean keepAlive = false;
114 String head = readHeaderLine();
115 if (head.startsWith("GET ") || head.startsWith("POST ")) {
116 int begin = head.indexOf('/'), end = head.lastIndexOf(' ');
117 String file;
118 if (begin < 0 || end < begin) {
119 file = "";
120 } else {
121 file = head.substring(begin + 1, end).trim();
122 }
123 trace(head + ": " + file);
124 file = getAllowedFile(file);
125 attributes = new Properties();
126 int paramIndex = file.indexOf("?");
127 session = null;
128 if (paramIndex >= 0) {
129 String attrib = file.substring(paramIndex + 1);
130 parseAttributes(attrib);
131 String sessionId = attributes.getProperty("jsessionid");
132 file = file.substring(0, paramIndex);
133 session = server.getSession(sessionId);
134 }
135 keepAlive = parseHeader();
136 String hostAddr = socket.getInetAddress().getHostAddress();
137 file = processRequest(file, hostAddr);
138 if (file.length() == 0) {
139 // asynchronous request
140 return true;
141 }
142 String message;
143 byte[] bytes;
144 if (cache && ifModifiedSince != null && ifModifiedSince.equals(server.getStartDateTime())) {
145 bytes = null;
146 message = "HTTP/1.1 304 Not Modified\r\n";
147 } else {
148 bytes = server.getFile(file);
149 if (bytes == null) {
150 message = "HTTP/1.0 404 Not Found\r\n";
151 bytes = StringUtils.utf8Encode("File not found: " + file);
152 } else {
153 if (session != null && file.endsWith(".jsp")) {
154 String page = StringUtils.utf8Decode(bytes);
155 if (SysProperties.CONSOLE_STREAM) {
156 Iterator<String> it = (Iterator<String>) session.map.remove("chunks");
157 if (it != null) {
158 message = "HTTP/1.1 200 OK\r\n";
159 message += "Content-Type: " + mimeType + "\r\n";
160 message += "Cache-Control: no-cache\r\n";
161 message += "Transfer-Encoding: chunked\r\n";
162 message += "\r\n";
163 trace(message);
164 output.write(message.getBytes());
165 while (it.hasNext()) {
166 String s = it.next();
167 s = PageParser.parse(s, session.map);
168 bytes = StringUtils.utf8Encode(s);
169 if (bytes.length == 0) {
170 continue;
171 }
172 output.write(Integer.toHexString(bytes.length).getBytes());
173 output.write("\r\n".getBytes());
174 output.write(bytes);
175 output.write("\r\n".getBytes());
176 output.flush();
177 }
178 output.write("0\r\n\r\n".getBytes());
179 output.flush();
180 return keepAlive;
181 }
182 }
183 page = PageParser.parse(page, session.map);
184 bytes = StringUtils.utf8Encode(page);
185 }
186 message = "HTTP/1.1 200 OK\r\n";
187 message += "Content-Type: " + mimeType + "\r\n";
188 if (!cache) {
189 message += "Cache-Control: no-cache\r\n";
190 } else {
191 message += "Cache-Control: max-age=10\r\n";
192 message += "Last-Modified: " + server.getStartDateTime() + "\r\n";
193 }
194 message += "Content-Length: " + bytes.length + "\r\n";
195 }
196 }
197 message += "\r\n";
198 trace(message);
199 output.write(message.getBytes());
200 if (bytes != null) {
201 output.write(bytes);
202 }
203 output.flush();
204 }
205 return keepAlive;
206 }
207
208 private String readHeaderLine() throws IOException {
209 StringBuilder buff = new StringBuilder();
210 while (true) {
211 headerBytes++;
212 int c = input.read();
213 if (c == -1) {
214 throw new IOException("Unexpected EOF");
215 } else if (c == '\r') {
216 headerBytes++;
217 if (input.read() == '\n') {
218 return buff.length() > 0 ? buff.toString() : null;
219 }
220 } else if (c == '\n') {
221 return buff.length() > 0 ? buff.toString() : null;
222 } else {
223 buff.append((char) c);
224 }
225 }
226 }
227
228 private void parseAttributes(String s) {
229 trace("data=" + s);
230 while (s != null) {
231 int idx = s.indexOf('=');
232 if (idx >= 0) {
233 String property = s.substring(0, idx);
234 s = s.substring(idx + 1);
235 idx = s.indexOf('&');
236 String value;
237 if (idx >= 0) {
238 value = s.substring(0, idx);
239 s = s.substring(idx + 1);
240 } else {
241 value = s;
242 }
243 String attr = StringUtils.urlDecode(value);
244 attributes.put(property, attr);
245 } else {
246 break;
247 }
248 }
249 trace(attributes.toString());
250 }
251
252 private boolean parseHeader() throws IOException {
253 boolean keepAlive = false;
254 trace("parseHeader");
255 int len = 0;
256 ifModifiedSince = null;
257 boolean multipart = false;
258 while (true) {
259 String line = readHeaderLine();
260 if (line == null) {
261 break;
262 }
263 trace(" " + line);
264 String lower = StringUtils.toLowerEnglish(line);
265 if (lower.startsWith("if-modified-since")) {
266 ifModifiedSince = getHeaderLineValue(line);
267 } else if (lower.startsWith("connection")) {
268 String conn = getHeaderLineValue(line);
269 if ("keep-alive".equals(conn)) {
270 keepAlive = true;
271 }
272 } else if (lower.startsWith("content-type")) {
273 String type = getHeaderLineValue(line);
274 if (type.startsWith("multipart/form-data")) {
275 multipart = true;
276 }
277 } else if (lower.startsWith("content-length")) {
278 len = Integer.parseInt(getHeaderLineValue(line));
279 trace("len=" + len);
280 } else if (lower.startsWith("user-agent")) {
281 boolean isWebKit = lower.indexOf("webkit/") >= 0;
282 if (isWebKit && session != null) {
283 // workaround for what seems to be a WebKit bug:
284 // http://code.google.com/p/chromium/issues/detail?id=6402
285 session.put("frame-border", "1");
286 session.put("frameset-border", "2");
287 }
288 } else if (lower.startsWith("accept-language")) {
289 Locale locale = session == null ? null : session.locale;
290 if (locale == null) {
291 String languages = getHeaderLineValue(line);
292 StringTokenizer tokenizer = new StringTokenizer(languages, ",;");
293 while (tokenizer.hasMoreTokens()) {
294 String token = tokenizer.nextToken();
295 if (!token.startsWith("q=")) {
296 if (server.supportsLanguage(token)) {
297 int dash = token.indexOf('-');
298 if (dash >= 0) {
299 String language = token.substring(0, dash);
300 String country = token.substring(dash + 1);
301 locale = new Locale(language, country);
302 } else {
303 locale = new Locale(token, "");
304 }
305 headerLanguage = locale.getLanguage();
306 if (session != null) {
307 session.locale = locale;
308 session.put("language", headerLanguage);
309 server.readTranslations(session, headerLanguage);
310 }
311 break;
312 }
313 }
314 }
315 }
316 } else if (line.trim().length() == 0) {
317 break;
318 }
319 }
320 if (multipart) {
321 uploadMultipart(input, len);
322 } else if (session != null && len > 0) {
323 byte[] bytes = Utils.newBytes(len);
324 for (int pos = 0; pos < len;) {
325 pos += input.read(bytes, pos, len - pos);
326 }
327 String s = new String(bytes);
328 parseAttributes(s);
329 }
330 return keepAlive;
331 }
332
333 private void uploadMultipart(InputStream in, int len) throws IOException {
334 if (!new File(WebServer.TRANSFER).exists()) {
335 return;
336 }
337 String fileName = "temp.bin";
338 headerBytes = 0;
339 String boundary = readHeaderLine();
340 while (true) {
341 String line = readHeaderLine();
342 if (line == null) {
343 break;
344 }
345 int index = line.indexOf("filename=\"");
346 if (index > 0) {
347 fileName = line.substring(index + "filename=\"".length(), line.lastIndexOf('"'));
348 }
349 trace(" " + line);
350 }
351 if (!WebServer.isSimpleName(fileName)) {
352 return;
353 }
354 len -= headerBytes;
355 File file = new File(WebServer.TRANSFER, fileName);
356 OutputStream out = new FileOutputStream(file);
357 IOUtils.copy(in, out, len);
358 out.close();
359 // remove the boundary
360 RandomAccessFile f = new RandomAccessFile(file, "rw");
361 int testSize = (int) Math.min(f.length(), Constants.IO_BUFFER_SIZE);
362 f.seek(f.length() - testSize);
363 byte[] bytes = Utils.newBytes(Constants.IO_BUFFER_SIZE);
364 f.readFully(bytes, 0, testSize);
365 String s = new String(bytes, "ASCII");
366 int x = s.lastIndexOf(boundary);
367 f.setLength(f.length() - testSize + x - 2);
368 f.close();
369 }
370
371 private static String getHeaderLineValue(String line) {
372 return line.substring(line.indexOf(':') + 1).trim();
373 }
374
375 protected String adminShutdown() {
376 stopNow();
377 return super.adminShutdown();
378 }
379
380 private boolean allow() {
381 if (server.getAllowOthers()) {
382 return true;
383 }
384 try {
385 return NetUtils.isLocalAddress(socket);
386 } catch (UnknownHostException e) {
387 server.traceError(e);
388 return false;
389 }
390 }
391
392 private void trace(String s) {
393 server.trace(s);
394 }
395 }
396