| /* |
| * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.net.httpserver; |
| |
| import java.util.*; |
| import java.nio.*; |
| import java.net.*; |
| import java.io.*; |
| import java.nio.channels.*; |
| import com.sun.net.httpserver.*; |
| import com.sun.net.httpserver.spi.*; |
| |
| /** |
| */ |
| class Request { |
| |
| final static int BUF_LEN = 2048; |
| final static byte CR = 13; |
| final static byte LF = 10; |
| |
| private String startLine; |
| private SocketChannel chan; |
| private InputStream is; |
| private OutputStream os; |
| |
| Request (InputStream rawInputStream, OutputStream rawout) throws IOException { |
| this.chan = chan; |
| is = rawInputStream; |
| os = rawout; |
| do { |
| startLine = readLine(); |
| if (startLine == null) { |
| return; |
| } |
| /* skip blank lines */ |
| } while (startLine == null ? false : startLine.equals ("")); |
| } |
| |
| |
| char[] buf = new char [BUF_LEN]; |
| int pos; |
| StringBuffer lineBuf; |
| |
| public InputStream inputStream () { |
| return is; |
| } |
| |
| public OutputStream outputStream () { |
| return os; |
| } |
| |
| /** |
| * read a line from the stream returning as a String. |
| * Not used for reading headers. |
| */ |
| |
| public String readLine () throws IOException { |
| boolean gotCR = false, gotLF = false; |
| pos = 0; lineBuf = new StringBuffer(); |
| while (!gotLF) { |
| int c = is.read(); |
| if (c == -1) { |
| return null; |
| } |
| if (gotCR) { |
| if (c == LF) { |
| gotLF = true; |
| } else { |
| gotCR = false; |
| consume (CR); |
| consume (c); |
| } |
| } else { |
| if (c == CR) { |
| gotCR = true; |
| } else { |
| consume (c); |
| } |
| } |
| } |
| lineBuf.append (buf, 0, pos); |
| return new String (lineBuf); |
| } |
| |
| private void consume (int c) { |
| if (pos == BUF_LEN) { |
| lineBuf.append (buf); |
| pos = 0; |
| } |
| buf[pos++] = (char)c; |
| } |
| |
| /** |
| * returns the request line (first line of a request) |
| */ |
| public String requestLine () { |
| return startLine; |
| } |
| |
| Headers hdrs = null; |
| |
| Headers headers () throws IOException { |
| if (hdrs != null) { |
| return hdrs; |
| } |
| hdrs = new Headers(); |
| |
| char s[] = new char[10]; |
| int firstc = is.read(); |
| while (firstc != LF && firstc != CR && firstc >= 0) { |
| int len = 0; |
| int keyend = -1; |
| int c; |
| boolean inKey = firstc > ' '; |
| s[len++] = (char) firstc; |
| parseloop:{ |
| while ((c = is.read()) >= 0) { |
| switch (c) { |
| case ':': |
| if (inKey && len > 0) |
| keyend = len; |
| inKey = false; |
| break; |
| case '\t': |
| c = ' '; |
| case ' ': |
| inKey = false; |
| break; |
| case CR: |
| case LF: |
| firstc = is.read(); |
| if (c == CR && firstc == LF) { |
| firstc = is.read(); |
| if (firstc == CR) |
| firstc = is.read(); |
| } |
| if (firstc == LF || firstc == CR || firstc > ' ') |
| break parseloop; |
| /* continuation */ |
| c = ' '; |
| break; |
| } |
| if (len >= s.length) { |
| char ns[] = new char[s.length * 2]; |
| System.arraycopy(s, 0, ns, 0, len); |
| s = ns; |
| } |
| s[len++] = (char) c; |
| } |
| firstc = -1; |
| } |
| while (len > 0 && s[len - 1] <= ' ') |
| len--; |
| String k; |
| if (keyend <= 0) { |
| k = null; |
| keyend = 0; |
| } else { |
| k = String.copyValueOf(s, 0, keyend); |
| if (keyend < len && s[keyend] == ':') |
| keyend++; |
| while (keyend < len && s[keyend] <= ' ') |
| keyend++; |
| } |
| String v; |
| if (keyend >= len) |
| v = new String(); |
| else |
| v = String.copyValueOf(s, keyend, len - keyend); |
| hdrs.add (k,v); |
| } |
| return hdrs; |
| } |
| |
| /** |
| * Implements blocking reading semantics on top of a non-blocking channel |
| */ |
| |
| static class ReadStream extends InputStream { |
| SocketChannel channel; |
| SelectorCache sc; |
| Selector selector; |
| ByteBuffer chanbuf; |
| SelectionKey key; |
| int available; |
| byte[] one; |
| boolean closed = false, eof = false; |
| ByteBuffer markBuf; /* reads may be satisifed from this buffer */ |
| boolean marked; |
| boolean reset; |
| int readlimit; |
| static long readTimeout; |
| ServerImpl server; |
| |
| static { |
| readTimeout = ServerConfig.getReadTimeout(); |
| } |
| |
| public ReadStream (ServerImpl server, SocketChannel chan) throws IOException { |
| this.channel = chan; |
| this.server = server; |
| sc = SelectorCache.getSelectorCache(); |
| selector = sc.getSelector(); |
| chanbuf = ByteBuffer.allocate (8* 1024); |
| key = chan.register (selector, SelectionKey.OP_READ); |
| available = 0; |
| one = new byte[1]; |
| closed = marked = reset = false; |
| } |
| |
| public synchronized int read (byte[] b) throws IOException { |
| return read (b, 0, b.length); |
| } |
| |
| public synchronized int read () throws IOException { |
| int result = read (one, 0, 1); |
| if (result == 1) { |
| return one[0] & 0xFF; |
| } else { |
| return -1; |
| } |
| } |
| |
| public synchronized int read (byte[] b, int off, int srclen) throws IOException { |
| |
| int canreturn, willreturn; |
| |
| if (closed) |
| throw new IOException ("Stream closed"); |
| |
| if (eof) { |
| return -1; |
| } |
| |
| if (reset) { /* satisfy from markBuf */ |
| canreturn = markBuf.remaining (); |
| willreturn = canreturn>srclen ? srclen : canreturn; |
| markBuf.get(b, off, willreturn); |
| if (canreturn == willreturn) { |
| reset = false; |
| } |
| } else { /* satisfy from channel */ |
| canreturn = available(); |
| while (canreturn == 0 && !eof) { |
| block (); |
| canreturn = available(); |
| } |
| if (eof) { |
| return -1; |
| } |
| willreturn = canreturn>srclen ? srclen : canreturn; |
| chanbuf.get(b, off, willreturn); |
| available -= willreturn; |
| |
| if (marked) { /* copy into markBuf */ |
| try { |
| markBuf.put (b, off, willreturn); |
| } catch (BufferOverflowException e) { |
| marked = false; |
| } |
| } |
| } |
| return willreturn; |
| } |
| |
| public synchronized int available () throws IOException { |
| if (closed) |
| throw new IOException ("Stream is closed"); |
| |
| if (eof) |
| return -1; |
| |
| if (reset) |
| return markBuf.remaining(); |
| |
| if (available > 0) |
| return available; |
| |
| chanbuf.clear (); |
| available = channel.read (chanbuf); |
| if (available > 0) { |
| chanbuf.flip(); |
| } else if (available == -1) { |
| eof = true; |
| available = 0; |
| } |
| return available; |
| } |
| |
| /** |
| * block() only called when available==0 and buf is empty |
| */ |
| private synchronized void block () throws IOException { |
| long currtime = server.getTime(); |
| long maxtime = currtime + readTimeout; |
| |
| while (currtime < maxtime) { |
| if (selector.select (readTimeout) == 1) { |
| selector.selectedKeys().clear(); |
| available (); |
| return; |
| } |
| currtime = server.getTime(); |
| } |
| throw new SocketTimeoutException ("no data received"); |
| } |
| |
| public void close () throws IOException { |
| if (closed) { |
| return; |
| } |
| channel.close (); |
| selector.selectNow(); |
| sc.freeSelector(selector); |
| closed = true; |
| } |
| |
| public synchronized void mark (int readlimit) { |
| if (closed) |
| return; |
| this.readlimit = readlimit; |
| markBuf = ByteBuffer.allocate (readlimit); |
| marked = true; |
| reset = false; |
| } |
| |
| public synchronized void reset () throws IOException { |
| if (closed ) |
| return; |
| if (!marked) |
| throw new IOException ("Stream not marked"); |
| marked = false; |
| reset = true; |
| markBuf.flip (); |
| } |
| } |
| |
| static class WriteStream extends java.io.OutputStream { |
| SocketChannel channel; |
| ByteBuffer buf; |
| SelectionKey key; |
| SelectorCache sc; |
| Selector selector; |
| boolean closed; |
| byte[] one; |
| ServerImpl server; |
| static long writeTimeout; |
| |
| static { |
| writeTimeout = ServerConfig.getWriteTimeout(); |
| } |
| |
| public WriteStream (ServerImpl server, SocketChannel channel) throws IOException { |
| this.channel = channel; |
| this.server = server; |
| sc = SelectorCache.getSelectorCache(); |
| selector = sc.getSelector(); |
| key = channel.register (selector, SelectionKey.OP_WRITE); |
| closed = false; |
| one = new byte [1]; |
| buf = ByteBuffer.allocate (4096); |
| } |
| |
| public synchronized void write (int b) throws IOException { |
| one[0] = (byte)b; |
| write (one, 0, 1); |
| } |
| |
| public synchronized void write (byte[] b) throws IOException { |
| write (b, 0, b.length); |
| } |
| |
| public synchronized void write (byte[] b, int off, int len) throws IOException { |
| int l = len; |
| if (closed) |
| throw new IOException ("stream is closed"); |
| |
| int cap = buf.capacity(); |
| if (cap < len) { |
| int diff = len - cap; |
| buf = ByteBuffer.allocate (2*(cap+diff)); |
| } |
| buf.clear(); |
| buf.put (b, off, len); |
| buf.flip (); |
| int n; |
| while ((n = channel.write (buf)) < l) { |
| l -= n; |
| if (l == 0) |
| return; |
| block(); |
| } |
| } |
| |
| void block () throws IOException { |
| long currtime = server.getTime(); |
| long maxtime = currtime + writeTimeout; |
| |
| while (currtime < maxtime) { |
| if (selector.select (writeTimeout) == 1) { |
| selector.selectedKeys().clear (); |
| return; |
| } |
| currtime = server.getTime(); |
| } |
| throw new SocketTimeoutException ("write blocked too long"); |
| } |
| |
| |
| public void close () throws IOException { |
| if (closed) |
| return; |
| channel.close (); |
| selector.selectNow(); |
| sc.freeSelector(selector); |
| closed = true; |
| } |
| } |
| } |