blob: bdafe663c1baff18590fed7ed48699bd0afc4a91 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.net.httpserver;
27
28import java.util.*;
29import java.nio.*;
30import java.net.*;
31import java.io.*;
32import java.nio.channels.*;
33import com.sun.net.httpserver.*;
34import com.sun.net.httpserver.spi.*;
35
36/**
37 */
38class Request {
39
40 final static int BUF_LEN = 2048;
41 final static byte CR = 13;
42 final static byte LF = 10;
43
44 private String startLine;
45 private SocketChannel chan;
46 private InputStream is;
47 private OutputStream os;
48
49 Request (InputStream rawInputStream, OutputStream rawout) throws IOException {
50 this.chan = chan;
51 is = rawInputStream;
52 os = rawout;
53 do {
54 startLine = readLine();
55 /* skip blank lines */
56 } while (startLine.equals (""));
57 }
58
59
60 char[] buf = new char [BUF_LEN];
61 int pos;
62 StringBuffer lineBuf;
63
64 public InputStream inputStream () {
65 return is;
66 }
67
68 public OutputStream outputStream () {
69 return os;
70 }
71
72 /**
73 * read a line from the stream returning as a String.
74 * Not used for reading headers.
75 */
76
77 public String readLine () throws IOException {
78 boolean gotCR = false, gotLF = false;
79 pos = 0; lineBuf = new StringBuffer();
80 while (!gotLF) {
81 int c = is.read();
82 if (c == -1) {
83 return null;
84 }
85 if (gotCR) {
86 if (c == LF) {
87 gotLF = true;
88 } else {
89 gotCR = false;
90 consume (CR);
91 consume (c);
92 }
93 } else {
94 if (c == CR) {
95 gotCR = true;
96 } else {
97 consume (c);
98 }
99 }
100 }
101 lineBuf.append (buf, 0, pos);
102 return new String (lineBuf);
103 }
104
105 private void consume (int c) {
106 if (pos == BUF_LEN) {
107 lineBuf.append (buf);
108 pos = 0;
109 }
110 buf[pos++] = (char)c;
111 }
112
113 /**
114 * returns the request line (first line of a request)
115 */
116 public String requestLine () {
117 return startLine;
118 }
119
120 Headers hdrs = null;
121
122 Headers headers () throws IOException {
123 if (hdrs != null) {
124 return hdrs;
125 }
126 hdrs = new Headers();
127
128 char s[] = new char[10];
129 int firstc = is.read();
130 while (firstc != LF && firstc != CR && firstc >= 0) {
131 int len = 0;
132 int keyend = -1;
133 int c;
134 boolean inKey = firstc > ' ';
135 s[len++] = (char) firstc;
136 parseloop:{
137 while ((c = is.read()) >= 0) {
138 switch (c) {
139 case ':':
140 if (inKey && len > 0)
141 keyend = len;
142 inKey = false;
143 break;
144 case '\t':
145 c = ' ';
146 case ' ':
147 inKey = false;
148 break;
149 case CR:
150 case LF:
151 firstc = is.read();
152 if (c == CR && firstc == LF) {
153 firstc = is.read();
154 if (firstc == CR)
155 firstc = is.read();
156 }
157 if (firstc == LF || firstc == CR || firstc > ' ')
158 break parseloop;
159 /* continuation */
160 c = ' ';
161 break;
162 }
163 if (len >= s.length) {
164 char ns[] = new char[s.length * 2];
165 System.arraycopy(s, 0, ns, 0, len);
166 s = ns;
167 }
168 s[len++] = (char) c;
169 }
170 firstc = -1;
171 }
172 while (len > 0 && s[len - 1] <= ' ')
173 len--;
174 String k;
175 if (keyend <= 0) {
176 k = null;
177 keyend = 0;
178 } else {
179 k = String.copyValueOf(s, 0, keyend);
180 if (keyend < len && s[keyend] == ':')
181 keyend++;
182 while (keyend < len && s[keyend] <= ' ')
183 keyend++;
184 }
185 String v;
186 if (keyend >= len)
187 v = new String();
188 else
189 v = String.copyValueOf(s, keyend, len - keyend);
190 hdrs.add (k,v);
191 }
192 return hdrs;
193 }
194
195 /**
196 * Implements blocking reading semantics on top of a non-blocking channel
197 */
198
199 static class ReadStream extends InputStream {
200 SocketChannel channel;
201 SelectorCache sc;
202 Selector selector;
203 ByteBuffer chanbuf;
204 SelectionKey key;
205 int available;
206 byte[] one;
207 boolean closed = false, eof = false;
208 ByteBuffer markBuf; /* reads may be satisifed from this buffer */
209 boolean marked;
210 boolean reset;
211 int readlimit;
212 static long readTimeout;
213 ServerImpl server;
214
215 static {
216 readTimeout = ServerConfig.getReadTimeout();
217 }
218
219 public ReadStream (ServerImpl server, SocketChannel chan) throws IOException {
220 this.channel = chan;
221 this.server = server;
222 sc = SelectorCache.getSelectorCache();
223 selector = sc.getSelector();
224 chanbuf = ByteBuffer.allocate (8* 1024);
225 key = chan.register (selector, SelectionKey.OP_READ);
226 available = 0;
227 one = new byte[1];
228 closed = marked = reset = false;
229 }
230
231 public synchronized int read (byte[] b) throws IOException {
232 return read (b, 0, b.length);
233 }
234
235 public synchronized int read () throws IOException {
236 int result = read (one, 0, 1);
237 if (result == 1) {
238 return one[0] & 0xFF;
239 } else {
240 return -1;
241 }
242 }
243
244 public synchronized int read (byte[] b, int off, int srclen) throws IOException {
245
246 int canreturn, willreturn;
247
248 if (closed)
249 throw new IOException ("Stream closed");
250
251 if (eof) {
252 return -1;
253 }
254
255 if (reset) { /* satisfy from markBuf */
256 canreturn = markBuf.remaining ();
257 willreturn = canreturn>srclen ? srclen : canreturn;
258 markBuf.get(b, off, willreturn);
259 if (canreturn == willreturn) {
260 reset = false;
261 }
262 } else { /* satisfy from channel */
263 canreturn = available();
264 while (canreturn == 0 && !eof) {
265 block ();
266 canreturn = available();
267 }
268 if (eof) {
269 return -1;
270 }
271 willreturn = canreturn>srclen ? srclen : canreturn;
272 chanbuf.get(b, off, willreturn);
273 available -= willreturn;
274
275 if (marked) { /* copy into markBuf */
276 try {
277 markBuf.put (b, off, willreturn);
278 } catch (BufferOverflowException e) {
279 marked = false;
280 }
281 }
282 }
283 return willreturn;
284 }
285
286 public synchronized int available () throws IOException {
287 if (closed)
288 throw new IOException ("Stream is closed");
289
290 if (eof)
291 return -1;
292
293 if (reset)
294 return markBuf.remaining();
295
296 if (available > 0)
297 return available;
298
299 chanbuf.clear ();
300 available = channel.read (chanbuf);
301 if (available > 0) {
302 chanbuf.flip();
303 } else if (available == -1) {
304 eof = true;
305 available = 0;
306 }
307 return available;
308 }
309
310 /**
311 * block() only called when available==0 and buf is empty
312 */
313 private synchronized void block () throws IOException {
314 long currtime = server.getTime();
315 long maxtime = currtime + readTimeout;
316
317 while (currtime < maxtime) {
318 if (selector.select (readTimeout) == 1) {
319 selector.selectedKeys().clear();
320 available ();
321 return;
322 }
323 currtime = server.getTime();
324 }
325 throw new SocketTimeoutException ("no data received");
326 }
327
328 public void close () throws IOException {
329 if (closed) {
330 return;
331 }
332 channel.close ();
333 selector.selectNow();
334 sc.freeSelector(selector);
335 closed = true;
336 }
337
338 public synchronized void mark (int readlimit) {
339 if (closed)
340 return;
341 this.readlimit = readlimit;
342 markBuf = ByteBuffer.allocate (readlimit);
343 marked = true;
344 reset = false;
345 }
346
347 public synchronized void reset () throws IOException {
348 if (closed )
349 return;
350 if (!marked)
351 throw new IOException ("Stream not marked");
352 marked = false;
353 reset = true;
354 markBuf.flip ();
355 }
356 }
357
358 static class WriteStream extends java.io.OutputStream {
359 SocketChannel channel;
360 ByteBuffer buf;
361 SelectionKey key;
362 SelectorCache sc;
363 Selector selector;
364 boolean closed;
365 byte[] one;
366 ServerImpl server;
367 static long writeTimeout;
368
369 static {
370 writeTimeout = ServerConfig.getWriteTimeout();
371 }
372
373 public WriteStream (ServerImpl server, SocketChannel channel) throws IOException {
374 this.channel = channel;
375 this.server = server;
376 sc = SelectorCache.getSelectorCache();
377 selector = sc.getSelector();
378 key = channel.register (selector, SelectionKey.OP_WRITE);
379 closed = false;
380 one = new byte [1];
381 buf = ByteBuffer.allocate (4096);
382 }
383
384 public synchronized void write (int b) throws IOException {
385 one[0] = (byte)b;
386 write (one, 0, 1);
387 }
388
389 public synchronized void write (byte[] b) throws IOException {
390 write (b, 0, b.length);
391 }
392
393 public synchronized void write (byte[] b, int off, int len) throws IOException {
394 int l = len;
395 if (closed)
396 throw new IOException ("stream is closed");
397
398 int cap = buf.capacity();
399 if (cap < len) {
400 int diff = len - cap;
401 buf = ByteBuffer.allocate (2*(cap+diff));
402 }
403 buf.clear();
404 buf.put (b, off, len);
405 buf.flip ();
406 int n;
407 while ((n = channel.write (buf)) < l) {
408 l -= n;
409 if (l == 0)
410 return;
411 block();
412 }
413 }
414
415 void block () throws IOException {
416 long currtime = server.getTime();
417 long maxtime = currtime + writeTimeout;
418
419 while (currtime < maxtime) {
420 if (selector.select (writeTimeout) == 1) {
421 selector.selectedKeys().clear ();
422 return;
423 }
424 currtime = server.getTime();
425 }
426 throw new SocketTimeoutException ("write blocked too long");
427 }
428
429
430 public void close () throws IOException {
431 if (closed)
432 return;
433 channel.close ();
434 selector.selectNow();
435 sc.freeSelector(selector);
436 closed = true;
437 }
438 }
439}