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