mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-23 20:44:41 +02:00
8187443: Forest Consolidation: Move files to unified layout
Reviewed-by: darcy, ihse
This commit is contained in:
parent
270fe13182
commit
3789983e89
56923 changed files with 3 additions and 15727 deletions
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (c) 2006, 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 com.sun.net.httpserver.*;
|
||||
import java.io.*;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import javax.security.auth.*;
|
||||
import javax.security.auth.callback.*;
|
||||
import javax.security.auth.login.*;
|
||||
|
||||
public class AuthFilter extends Filter {
|
||||
|
||||
private Authenticator authenticator;
|
||||
|
||||
public AuthFilter (Authenticator authenticator) {
|
||||
this.authenticator = authenticator;
|
||||
}
|
||||
|
||||
public String description () {
|
||||
return "Authentication filter";
|
||||
}
|
||||
|
||||
public void setAuthenticator (Authenticator a) {
|
||||
authenticator = a;
|
||||
}
|
||||
|
||||
public void consumeInput (HttpExchange t) throws IOException {
|
||||
InputStream i = t.getRequestBody();
|
||||
byte[] b = new byte [4096];
|
||||
while (i.read (b) != -1);
|
||||
i.close ();
|
||||
}
|
||||
|
||||
/**
|
||||
* The filter's implementation, which is invoked by the server
|
||||
*/
|
||||
public void doFilter (HttpExchange t, Filter.Chain chain) throws IOException
|
||||
{
|
||||
if (authenticator != null) {
|
||||
Authenticator.Result r = authenticator.authenticate (t);
|
||||
if (r instanceof Authenticator.Success) {
|
||||
Authenticator.Success s = (Authenticator.Success)r;
|
||||
ExchangeImpl e = ExchangeImpl.get (t);
|
||||
e.setPrincipal (s.getPrincipal());
|
||||
chain.doFilter (t);
|
||||
} else if (r instanceof Authenticator.Retry) {
|
||||
Authenticator.Retry ry = (Authenticator.Retry)r;
|
||||
consumeInput (t);
|
||||
t.sendResponseHeaders (ry.getResponseCode(), -1);
|
||||
} else if (r instanceof Authenticator.Failure) {
|
||||
Authenticator.Failure f = (Authenticator.Failure)r;
|
||||
consumeInput (t);
|
||||
t.sendResponseHeaders (f.getResponseCode(), -1);
|
||||
}
|
||||
} else {
|
||||
chain.doFilter (t);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2012, 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.io.*;
|
||||
import java.net.*;
|
||||
import com.sun.net.httpserver.*;
|
||||
import com.sun.net.httpserver.spi.*;
|
||||
|
||||
class ChunkedInputStream extends LeftOverInputStream {
|
||||
ChunkedInputStream (ExchangeImpl t, InputStream src) {
|
||||
super (t, src);
|
||||
}
|
||||
|
||||
private int remaining;
|
||||
|
||||
/* true when a chunk header needs to be read */
|
||||
|
||||
private boolean needToReadHeader = true;
|
||||
|
||||
final static char CR = '\r';
|
||||
final static char LF = '\n';
|
||||
/*
|
||||
* Maximum chunk header size of 2KB + 2 bytes for CRLF
|
||||
*/
|
||||
private final static int MAX_CHUNK_HEADER_SIZE = 2050;
|
||||
|
||||
private int numeric (char[] arr, int nchars) throws IOException {
|
||||
assert arr.length >= nchars;
|
||||
int len = 0;
|
||||
for (int i=0; i<nchars; i++) {
|
||||
char c = arr[i];
|
||||
int val=0;
|
||||
if (c>='0' && c <='9') {
|
||||
val = c - '0';
|
||||
} else if (c>='a' && c<= 'f') {
|
||||
val = c - 'a' + 10;
|
||||
} else if (c>='A' && c<= 'F') {
|
||||
val = c - 'A' + 10;
|
||||
} else {
|
||||
throw new IOException ("invalid chunk length");
|
||||
}
|
||||
len = len * 16 + val;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/* read the chunk header line and return the chunk length
|
||||
* any chunk extensions are ignored
|
||||
*/
|
||||
private int readChunkHeader () throws IOException {
|
||||
boolean gotCR = false;
|
||||
int c;
|
||||
char[] len_arr = new char [16];
|
||||
int len_size = 0;
|
||||
boolean end_of_len = false;
|
||||
int read = 0;
|
||||
|
||||
while ((c=in.read())!= -1) {
|
||||
char ch = (char) c;
|
||||
read++;
|
||||
if ((len_size == len_arr.length -1) ||
|
||||
(read > MAX_CHUNK_HEADER_SIZE))
|
||||
{
|
||||
throw new IOException ("invalid chunk header");
|
||||
}
|
||||
if (gotCR) {
|
||||
if (ch == LF) {
|
||||
int l = numeric (len_arr, len_size);
|
||||
return l;
|
||||
} else {
|
||||
gotCR = false;
|
||||
}
|
||||
if (!end_of_len) {
|
||||
len_arr[len_size++] = ch;
|
||||
}
|
||||
} else {
|
||||
if (ch == CR) {
|
||||
gotCR = true;
|
||||
} else if (ch == ';') {
|
||||
end_of_len = true;
|
||||
} else if (!end_of_len) {
|
||||
len_arr[len_size++] = ch;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IOException ("end of stream reading chunk header");
|
||||
}
|
||||
|
||||
protected int readImpl (byte[]b, int off, int len) throws IOException {
|
||||
if (eof) {
|
||||
return -1;
|
||||
}
|
||||
if (needToReadHeader) {
|
||||
remaining = readChunkHeader();
|
||||
if (remaining == 0) {
|
||||
eof = true;
|
||||
consumeCRLF();
|
||||
t.getServerImpl().requestCompleted (t.getConnection());
|
||||
return -1;
|
||||
}
|
||||
needToReadHeader = false;
|
||||
}
|
||||
if (len > remaining) {
|
||||
len = remaining;
|
||||
}
|
||||
int n = in.read(b, off, len);
|
||||
if (n > -1) {
|
||||
remaining -= n;
|
||||
}
|
||||
if (remaining == 0) {
|
||||
needToReadHeader = true;
|
||||
consumeCRLF();
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
private void consumeCRLF () throws IOException {
|
||||
char c;
|
||||
c = (char)in.read(); /* CR */
|
||||
if (c != CR) {
|
||||
throw new IOException ("invalid chunk end");
|
||||
}
|
||||
c = (char)in.read(); /* LF */
|
||||
if (c != LF) {
|
||||
throw new IOException ("invalid chunk end");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the number of bytes available to read in the current chunk
|
||||
* which may be less than the real amount, but we'll live with that
|
||||
* limitation for the moment. It only affects potential efficiency
|
||||
* rather than correctness.
|
||||
*/
|
||||
public int available () throws IOException {
|
||||
if (eof || closed) {
|
||||
return 0;
|
||||
}
|
||||
int n = in.available();
|
||||
return n > remaining? remaining: n;
|
||||
}
|
||||
|
||||
/* called after the stream is closed to see if bytes
|
||||
* have been read from the underlying channel
|
||||
* and buffered internally
|
||||
*/
|
||||
public boolean isDataBuffered () throws IOException {
|
||||
assert eof;
|
||||
return in.available() > 0;
|
||||
}
|
||||
|
||||
public boolean markSupported () {return false;}
|
||||
|
||||
public void mark (int l) {
|
||||
}
|
||||
|
||||
public void reset () throws IOException {
|
||||
throw new IOException ("mark/reset not supported");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* 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.io.*;
|
||||
import java.net.*;
|
||||
import com.sun.net.httpserver.*;
|
||||
import com.sun.net.httpserver.spi.*;
|
||||
|
||||
/**
|
||||
* a class which allows the caller to write an arbitrary
|
||||
* number of bytes to an underlying stream.
|
||||
* normal close() does not close the underlying stream
|
||||
*
|
||||
* This class is buffered.
|
||||
*
|
||||
* Each chunk is written in one go as :-
|
||||
* abcd\r\nxxxxxxxxxxxxxx\r\n
|
||||
*
|
||||
* abcd is the chunk-size, and xxx is the chunk data
|
||||
* If the length is less than 4 chars (in size) then the buffer
|
||||
* is written with an offset.
|
||||
* Final chunk is:
|
||||
* 0\r\n\r\n
|
||||
*/
|
||||
|
||||
class ChunkedOutputStream extends FilterOutputStream
|
||||
{
|
||||
private boolean closed = false;
|
||||
/* max. amount of user data per chunk */
|
||||
final static int CHUNK_SIZE = 4096;
|
||||
/* allow 4 bytes for chunk-size plus 4 for CRLFs */
|
||||
final static int OFFSET = 6; /* initial <=4 bytes for len + CRLF */
|
||||
private int pos = OFFSET;
|
||||
private int count = 0;
|
||||
private byte[] buf = new byte [CHUNK_SIZE+OFFSET+2];
|
||||
ExchangeImpl t;
|
||||
|
||||
ChunkedOutputStream (ExchangeImpl t, OutputStream src) {
|
||||
super (src);
|
||||
this.t = t;
|
||||
}
|
||||
|
||||
public void write (int b) throws IOException {
|
||||
if (closed) {
|
||||
throw new StreamClosedException ();
|
||||
}
|
||||
buf [pos++] = (byte)b;
|
||||
count ++;
|
||||
if (count == CHUNK_SIZE) {
|
||||
writeChunk();
|
||||
}
|
||||
assert count < CHUNK_SIZE;
|
||||
}
|
||||
|
||||
public void write (byte[]b, int off, int len) throws IOException {
|
||||
if (closed) {
|
||||
throw new StreamClosedException ();
|
||||
}
|
||||
int remain = CHUNK_SIZE - count;
|
||||
if (len > remain) {
|
||||
System.arraycopy (b,off,buf,pos,remain);
|
||||
count = CHUNK_SIZE;
|
||||
writeChunk();
|
||||
len -= remain;
|
||||
off += remain;
|
||||
while (len >= CHUNK_SIZE) {
|
||||
System.arraycopy (b,off,buf,OFFSET,CHUNK_SIZE);
|
||||
len -= CHUNK_SIZE;
|
||||
off += CHUNK_SIZE;
|
||||
count = CHUNK_SIZE;
|
||||
writeChunk();
|
||||
}
|
||||
}
|
||||
if (len > 0) {
|
||||
System.arraycopy (b,off,buf,pos,len);
|
||||
count += len;
|
||||
pos += len;
|
||||
}
|
||||
if (count == CHUNK_SIZE) {
|
||||
writeChunk();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* write out a chunk , and reset the pointers
|
||||
* chunk does not have to be CHUNK_SIZE bytes
|
||||
* count must == number of user bytes (<= CHUNK_SIZE)
|
||||
*/
|
||||
private void writeChunk () throws IOException {
|
||||
char[] c = Integer.toHexString (count).toCharArray();
|
||||
int clen = c.length;
|
||||
int startByte = 4 - clen;
|
||||
int i;
|
||||
for (i=0; i<clen; i++) {
|
||||
buf[startByte+i] = (byte)c[i];
|
||||
}
|
||||
buf[startByte + (i++)] = '\r';
|
||||
buf[startByte + (i++)] = '\n';
|
||||
buf[startByte + (i++) + count] = '\r';
|
||||
buf[startByte + (i++) + count] = '\n';
|
||||
out.write (buf, startByte, i+count);
|
||||
count = 0;
|
||||
pos = OFFSET;
|
||||
}
|
||||
|
||||
public void close () throws IOException {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
flush();
|
||||
try {
|
||||
/* write an empty chunk */
|
||||
writeChunk();
|
||||
out.flush();
|
||||
LeftOverInputStream is = t.getOriginalInputStream();
|
||||
if (!is.isClosed()) {
|
||||
is.close();
|
||||
}
|
||||
/* some clients close the connection before empty chunk is sent */
|
||||
} catch (IOException e) {
|
||||
|
||||
} finally {
|
||||
closed = true;
|
||||
}
|
||||
|
||||
WriteFinishedEvent e = new WriteFinishedEvent (t);
|
||||
t.getHttpContext().getServerImpl().addEvent (e);
|
||||
}
|
||||
|
||||
public void flush () throws IOException {
|
||||
if (closed) {
|
||||
throw new StreamClosedException ();
|
||||
}
|
||||
if (count > 0) {
|
||||
writeChunk();
|
||||
}
|
||||
out.flush();
|
||||
}
|
||||
}
|
109
src/jdk.httpserver/share/classes/sun/net/httpserver/Code.java
Normal file
109
src/jdk.httpserver/share/classes/sun/net/httpserver/Code.java
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2006, 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;
|
||||
|
||||
class Code {
|
||||
|
||||
public static final int HTTP_CONTINUE = 100;
|
||||
public static final int HTTP_OK = 200;
|
||||
public static final int HTTP_CREATED = 201;
|
||||
public static final int HTTP_ACCEPTED = 202;
|
||||
public static final int HTTP_NOT_AUTHORITATIVE = 203;
|
||||
public static final int HTTP_NO_CONTENT = 204;
|
||||
public static final int HTTP_RESET = 205;
|
||||
public static final int HTTP_PARTIAL = 206;
|
||||
public static final int HTTP_MULT_CHOICE = 300;
|
||||
public static final int HTTP_MOVED_PERM = 301;
|
||||
public static final int HTTP_MOVED_TEMP = 302;
|
||||
public static final int HTTP_SEE_OTHER = 303;
|
||||
public static final int HTTP_NOT_MODIFIED = 304;
|
||||
public static final int HTTP_USE_PROXY = 305;
|
||||
public static final int HTTP_BAD_REQUEST = 400;
|
||||
public static final int HTTP_UNAUTHORIZED = 401;
|
||||
public static final int HTTP_PAYMENT_REQUIRED = 402;
|
||||
public static final int HTTP_FORBIDDEN = 403;
|
||||
public static final int HTTP_NOT_FOUND = 404;
|
||||
public static final int HTTP_BAD_METHOD = 405;
|
||||
public static final int HTTP_NOT_ACCEPTABLE = 406;
|
||||
public static final int HTTP_PROXY_AUTH = 407;
|
||||
public static final int HTTP_CLIENT_TIMEOUT = 408;
|
||||
public static final int HTTP_CONFLICT = 409;
|
||||
public static final int HTTP_GONE = 410;
|
||||
public static final int HTTP_LENGTH_REQUIRED = 411;
|
||||
public static final int HTTP_PRECON_FAILED = 412;
|
||||
public static final int HTTP_ENTITY_TOO_LARGE = 413;
|
||||
public static final int HTTP_REQ_TOO_LONG = 414;
|
||||
public static final int HTTP_UNSUPPORTED_TYPE = 415;
|
||||
public static final int HTTP_INTERNAL_ERROR = 500;
|
||||
public static final int HTTP_NOT_IMPLEMENTED = 501;
|
||||
public static final int HTTP_BAD_GATEWAY = 502;
|
||||
public static final int HTTP_UNAVAILABLE = 503;
|
||||
public static final int HTTP_GATEWAY_TIMEOUT = 504;
|
||||
public static final int HTTP_VERSION = 505;
|
||||
|
||||
static String msg (int code) {
|
||||
|
||||
switch (code) {
|
||||
case HTTP_OK: return " OK";
|
||||
case HTTP_CONTINUE: return " Continue";
|
||||
case HTTP_CREATED: return " Created";
|
||||
case HTTP_ACCEPTED: return " Accepted";
|
||||
case HTTP_NOT_AUTHORITATIVE: return " Non-Authoritative Information";
|
||||
case HTTP_NO_CONTENT: return " No Content";
|
||||
case HTTP_RESET: return " Reset Content";
|
||||
case HTTP_PARTIAL: return " Partial Content";
|
||||
case HTTP_MULT_CHOICE: return " Multiple Choices";
|
||||
case HTTP_MOVED_PERM: return " Moved Permanently";
|
||||
case HTTP_MOVED_TEMP: return " Temporary Redirect";
|
||||
case HTTP_SEE_OTHER: return " See Other";
|
||||
case HTTP_NOT_MODIFIED: return " Not Modified";
|
||||
case HTTP_USE_PROXY: return " Use Proxy";
|
||||
case HTTP_BAD_REQUEST: return " Bad Request";
|
||||
case HTTP_UNAUTHORIZED: return " Unauthorized" ;
|
||||
case HTTP_PAYMENT_REQUIRED: return " Payment Required";
|
||||
case HTTP_FORBIDDEN: return " Forbidden";
|
||||
case HTTP_NOT_FOUND: return " Not Found";
|
||||
case HTTP_BAD_METHOD: return " Method Not Allowed";
|
||||
case HTTP_NOT_ACCEPTABLE: return " Not Acceptable";
|
||||
case HTTP_PROXY_AUTH: return " Proxy Authentication Required";
|
||||
case HTTP_CLIENT_TIMEOUT: return " Request Time-Out";
|
||||
case HTTP_CONFLICT: return " Conflict";
|
||||
case HTTP_GONE: return " Gone";
|
||||
case HTTP_LENGTH_REQUIRED: return " Length Required";
|
||||
case HTTP_PRECON_FAILED: return " Precondition Failed";
|
||||
case HTTP_ENTITY_TOO_LARGE: return " Request Entity Too Large";
|
||||
case HTTP_REQ_TOO_LONG: return " Request-URI Too Large";
|
||||
case HTTP_UNSUPPORTED_TYPE: return " Unsupported Media Type";
|
||||
case HTTP_INTERNAL_ERROR: return " Internal Server Error";
|
||||
case HTTP_NOT_IMPLEMENTED: return " Not Implemented";
|
||||
case HTTP_BAD_GATEWAY: return " Bad Gateway";
|
||||
case HTTP_UNAVAILABLE: return " Service Unavailable";
|
||||
case HTTP_GATEWAY_TIMEOUT: return " Gateway Timeout";
|
||||
case HTTP_VERSION: return " HTTP Version Not Supported";
|
||||
default: return " ";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 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 com.sun.net.httpserver.*;
|
||||
import com.sun.net.httpserver.spi.*;
|
||||
|
||||
class ContextList {
|
||||
|
||||
final static int MAX_CONTEXTS = 50;
|
||||
|
||||
LinkedList<HttpContextImpl> list = new LinkedList<HttpContextImpl>();
|
||||
|
||||
public synchronized void add (HttpContextImpl ctx) {
|
||||
assert ctx.getPath() != null;
|
||||
list.add (ctx);
|
||||
}
|
||||
|
||||
public synchronized int size () {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
/* initially contexts are located only by protocol:path.
|
||||
* Context with longest prefix matches (currently case-sensitive)
|
||||
*/
|
||||
synchronized HttpContextImpl findContext (String protocol, String path) {
|
||||
return findContext (protocol, path, false);
|
||||
}
|
||||
|
||||
synchronized HttpContextImpl findContext (String protocol, String path, boolean exact) {
|
||||
protocol = protocol.toLowerCase();
|
||||
String longest = "";
|
||||
HttpContextImpl lc = null;
|
||||
for (HttpContextImpl ctx: list) {
|
||||
if (!ctx.getProtocol().equals(protocol)) {
|
||||
continue;
|
||||
}
|
||||
String cpath = ctx.getPath();
|
||||
if (exact && !cpath.equals (path)) {
|
||||
continue;
|
||||
} else if (!exact && !path.startsWith(cpath)) {
|
||||
continue;
|
||||
}
|
||||
if (cpath.length() > longest.length()) {
|
||||
longest = cpath;
|
||||
lc = ctx;
|
||||
}
|
||||
}
|
||||
return lc;
|
||||
}
|
||||
|
||||
public synchronized void remove (String protocol, String path)
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
HttpContextImpl ctx = findContext (protocol, path, true);
|
||||
if (ctx == null) {
|
||||
throw new IllegalArgumentException ("cannot remove element from list");
|
||||
}
|
||||
list.remove (ctx);
|
||||
}
|
||||
|
||||
public synchronized void remove (HttpContextImpl context)
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
for (HttpContextImpl ctx: list) {
|
||||
if (ctx.equals (context)) {
|
||||
list.remove (ctx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException ("no such context in list");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 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.net.*;
|
||||
import java.io.*;
|
||||
import com.sun.net.httpserver.*;
|
||||
import com.sun.net.httpserver.spi.*;
|
||||
|
||||
public class DefaultHttpServerProvider extends HttpServerProvider {
|
||||
public HttpServer createHttpServer (InetSocketAddress addr, int backlog) throws IOException {
|
||||
return new HttpServerImpl (addr, backlog);
|
||||
}
|
||||
|
||||
public HttpsServer createHttpsServer (InetSocketAddress addr, int backlog) throws IOException {
|
||||
return new HttpsServerImpl (addr, backlog);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2012, 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 com.sun.net.httpserver.*;
|
||||
import com.sun.net.httpserver.spi.*;
|
||||
|
||||
class Event {
|
||||
|
||||
ExchangeImpl exchange;
|
||||
|
||||
protected Event (ExchangeImpl t) {
|
||||
this.exchange = t;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,457 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2010, 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.io.*;
|
||||
import java.net.*;
|
||||
import javax.net.ssl.*;
|
||||
import java.util.*;
|
||||
import java.lang.System.Logger;
|
||||
import java.lang.System.Logger.Level;
|
||||
import java.text.*;
|
||||
import com.sun.net.httpserver.*;
|
||||
|
||||
class ExchangeImpl {
|
||||
|
||||
Headers reqHdrs, rspHdrs;
|
||||
Request req;
|
||||
String method;
|
||||
boolean writefinished;
|
||||
URI uri;
|
||||
HttpConnection connection;
|
||||
long reqContentLen;
|
||||
long rspContentLen;
|
||||
/* raw streams which access the socket directly */
|
||||
InputStream ris;
|
||||
OutputStream ros;
|
||||
Thread thread;
|
||||
/* close the underlying connection when this exchange finished */
|
||||
boolean close;
|
||||
boolean closed;
|
||||
boolean http10 = false;
|
||||
|
||||
/* for formatting the Date: header */
|
||||
private static final String pattern = "EEE, dd MMM yyyy HH:mm:ss zzz";
|
||||
private static final TimeZone gmtTZ = TimeZone.getTimeZone("GMT");
|
||||
private static final ThreadLocal<DateFormat> dateFormat =
|
||||
new ThreadLocal<DateFormat>() {
|
||||
@Override protected DateFormat initialValue() {
|
||||
DateFormat df = new SimpleDateFormat(pattern, Locale.US);
|
||||
df.setTimeZone(gmtTZ);
|
||||
return df;
|
||||
}
|
||||
};
|
||||
|
||||
private static final String HEAD = "HEAD";
|
||||
|
||||
/* streams which take care of the HTTP protocol framing
|
||||
* and are passed up to higher layers
|
||||
*/
|
||||
InputStream uis;
|
||||
OutputStream uos;
|
||||
LeftOverInputStream uis_orig; // uis may have be a user supplied wrapper
|
||||
PlaceholderOutputStream uos_orig;
|
||||
|
||||
boolean sentHeaders; /* true after response headers sent */
|
||||
Map<String,Object> attributes;
|
||||
int rcode = -1;
|
||||
HttpPrincipal principal;
|
||||
ServerImpl server;
|
||||
|
||||
ExchangeImpl (
|
||||
String m, URI u, Request req, long len, HttpConnection connection
|
||||
) throws IOException {
|
||||
this.req = req;
|
||||
this.reqHdrs = req.headers();
|
||||
this.rspHdrs = new Headers();
|
||||
this.method = m;
|
||||
this.uri = u;
|
||||
this.connection = connection;
|
||||
this.reqContentLen = len;
|
||||
/* ros only used for headers, body written directly to stream */
|
||||
this.ros = req.outputStream();
|
||||
this.ris = req.inputStream();
|
||||
server = getServerImpl();
|
||||
server.startExchange();
|
||||
}
|
||||
|
||||
public Headers getRequestHeaders () {
|
||||
return new UnmodifiableHeaders (reqHdrs);
|
||||
}
|
||||
|
||||
public Headers getResponseHeaders () {
|
||||
return rspHdrs;
|
||||
}
|
||||
|
||||
public URI getRequestURI () {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public String getRequestMethod (){
|
||||
return method;
|
||||
}
|
||||
|
||||
public HttpContextImpl getHttpContext (){
|
||||
return connection.getHttpContext();
|
||||
}
|
||||
|
||||
private boolean isHeadRequest() {
|
||||
return HEAD.equals(getRequestMethod());
|
||||
}
|
||||
|
||||
public void close () {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
closed = true;
|
||||
|
||||
/* close the underlying connection if,
|
||||
* a) the streams not set up yet, no response can be sent, or
|
||||
* b) if the wrapper output stream is not set up, or
|
||||
* c) if the close of the input/outpu stream fails
|
||||
*/
|
||||
try {
|
||||
if (uis_orig == null || uos == null) {
|
||||
connection.close();
|
||||
return;
|
||||
}
|
||||
if (!uos_orig.isWrapped()) {
|
||||
connection.close();
|
||||
return;
|
||||
}
|
||||
if (!uis_orig.isClosed()) {
|
||||
uis_orig.close();
|
||||
}
|
||||
uos.close();
|
||||
} catch (IOException e) {
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
|
||||
public InputStream getRequestBody () {
|
||||
if (uis != null) {
|
||||
return uis;
|
||||
}
|
||||
if (reqContentLen == -1L) {
|
||||
uis_orig = new ChunkedInputStream (this, ris);
|
||||
uis = uis_orig;
|
||||
} else {
|
||||
uis_orig = new FixedLengthInputStream (this, ris, reqContentLen);
|
||||
uis = uis_orig;
|
||||
}
|
||||
return uis;
|
||||
}
|
||||
|
||||
LeftOverInputStream getOriginalInputStream () {
|
||||
return uis_orig;
|
||||
}
|
||||
|
||||
public int getResponseCode () {
|
||||
return rcode;
|
||||
}
|
||||
|
||||
public OutputStream getResponseBody () {
|
||||
/* TODO. Change spec to remove restriction below. Filters
|
||||
* cannot work with this restriction
|
||||
*
|
||||
* if (!sentHeaders) {
|
||||
* throw new IllegalStateException ("headers not sent");
|
||||
* }
|
||||
*/
|
||||
if (uos == null) {
|
||||
uos_orig = new PlaceholderOutputStream (null);
|
||||
uos = uos_orig;
|
||||
}
|
||||
return uos;
|
||||
}
|
||||
|
||||
|
||||
/* returns the place holder stream, which is the stream
|
||||
* returned from the 1st call to getResponseBody()
|
||||
* The "real" ouputstream is then placed inside this
|
||||
*/
|
||||
PlaceholderOutputStream getPlaceholderResponseBody () {
|
||||
getResponseBody();
|
||||
return uos_orig;
|
||||
}
|
||||
|
||||
public void sendResponseHeaders (int rCode, long contentLen)
|
||||
throws IOException
|
||||
{
|
||||
if (sentHeaders) {
|
||||
throw new IOException ("headers already sent");
|
||||
}
|
||||
this.rcode = rCode;
|
||||
String statusLine = "HTTP/1.1 "+rCode+Code.msg(rCode)+"\r\n";
|
||||
OutputStream tmpout = new BufferedOutputStream (ros);
|
||||
PlaceholderOutputStream o = getPlaceholderResponseBody();
|
||||
tmpout.write (bytes(statusLine, 0), 0, statusLine.length());
|
||||
boolean noContentToSend = false; // assume there is content
|
||||
rspHdrs.set ("Date", dateFormat.get().format (new Date()));
|
||||
|
||||
/* check for response type that is not allowed to send a body */
|
||||
|
||||
if ((rCode>=100 && rCode <200) /* informational */
|
||||
||(rCode == 204) /* no content */
|
||||
||(rCode == 304)) /* not modified */
|
||||
{
|
||||
if (contentLen != -1) {
|
||||
Logger logger = server.getLogger();
|
||||
String msg = "sendResponseHeaders: rCode = "+ rCode
|
||||
+ ": forcing contentLen = -1";
|
||||
logger.log (Level.WARNING, msg);
|
||||
}
|
||||
contentLen = -1;
|
||||
}
|
||||
|
||||
if (isHeadRequest()) {
|
||||
/* HEAD requests should not set a content length by passing it
|
||||
* through this API, but should instead manually set the required
|
||||
* headers.*/
|
||||
if (contentLen >= 0) {
|
||||
final Logger logger = server.getLogger();
|
||||
String msg =
|
||||
"sendResponseHeaders: being invoked with a content length for a HEAD request";
|
||||
logger.log (Level.WARNING, msg);
|
||||
}
|
||||
noContentToSend = true;
|
||||
contentLen = 0;
|
||||
} else { /* not a HEAD request */
|
||||
if (contentLen == 0) {
|
||||
if (http10) {
|
||||
o.setWrappedStream (new UndefLengthOutputStream (this, ros));
|
||||
close = true;
|
||||
} else {
|
||||
rspHdrs.set ("Transfer-encoding", "chunked");
|
||||
o.setWrappedStream (new ChunkedOutputStream (this, ros));
|
||||
}
|
||||
} else {
|
||||
if (contentLen == -1) {
|
||||
noContentToSend = true;
|
||||
contentLen = 0;
|
||||
}
|
||||
rspHdrs.set("Content-length", Long.toString(contentLen));
|
||||
o.setWrappedStream (new FixedLengthOutputStream (this, ros, contentLen));
|
||||
}
|
||||
}
|
||||
write (rspHdrs, tmpout);
|
||||
this.rspContentLen = contentLen;
|
||||
tmpout.flush() ;
|
||||
tmpout = null;
|
||||
sentHeaders = true;
|
||||
if (noContentToSend) {
|
||||
WriteFinishedEvent e = new WriteFinishedEvent (this);
|
||||
server.addEvent (e);
|
||||
closed = true;
|
||||
}
|
||||
server.logReply (rCode, req.requestLine(), null);
|
||||
}
|
||||
|
||||
void write (Headers map, OutputStream os) throws IOException {
|
||||
Set<Map.Entry<String,List<String>>> entries = map.entrySet();
|
||||
for (Map.Entry<String,List<String>> entry : entries) {
|
||||
String key = entry.getKey();
|
||||
byte[] buf;
|
||||
List<String> values = entry.getValue();
|
||||
for (String val : values) {
|
||||
int i = key.length();
|
||||
buf = bytes (key, 2);
|
||||
buf[i++] = ':';
|
||||
buf[i++] = ' ';
|
||||
os.write (buf, 0, i);
|
||||
buf = bytes (val, 2);
|
||||
i = val.length();
|
||||
buf[i++] = '\r';
|
||||
buf[i++] = '\n';
|
||||
os.write (buf, 0, i);
|
||||
}
|
||||
}
|
||||
os.write ('\r');
|
||||
os.write ('\n');
|
||||
}
|
||||
|
||||
private byte[] rspbuf = new byte [128]; // used by bytes()
|
||||
|
||||
/**
|
||||
* convert string to byte[], using rspbuf
|
||||
* Make sure that at least "extra" bytes are free at end
|
||||
* of rspbuf. Reallocate rspbuf if not big enough.
|
||||
* caller must check return value to see if rspbuf moved
|
||||
*/
|
||||
private byte[] bytes (String s, int extra) {
|
||||
int slen = s.length();
|
||||
if (slen+extra > rspbuf.length) {
|
||||
int diff = slen + extra - rspbuf.length;
|
||||
rspbuf = new byte [2* (rspbuf.length + diff)];
|
||||
}
|
||||
char c[] = s.toCharArray();
|
||||
for (int i=0; i<c.length; i++) {
|
||||
rspbuf[i] = (byte)c[i];
|
||||
}
|
||||
return rspbuf;
|
||||
}
|
||||
|
||||
public InetSocketAddress getRemoteAddress (){
|
||||
Socket s = connection.getChannel().socket();
|
||||
InetAddress ia = s.getInetAddress();
|
||||
int port = s.getPort();
|
||||
return new InetSocketAddress (ia, port);
|
||||
}
|
||||
|
||||
public InetSocketAddress getLocalAddress (){
|
||||
Socket s = connection.getChannel().socket();
|
||||
InetAddress ia = s.getLocalAddress();
|
||||
int port = s.getLocalPort();
|
||||
return new InetSocketAddress (ia, port);
|
||||
}
|
||||
|
||||
public String getProtocol (){
|
||||
String reqline = req.requestLine();
|
||||
int index = reqline.lastIndexOf (' ');
|
||||
return reqline.substring (index+1);
|
||||
}
|
||||
|
||||
public SSLSession getSSLSession () {
|
||||
SSLEngine e = connection.getSSLEngine();
|
||||
if (e == null) {
|
||||
return null;
|
||||
}
|
||||
return e.getSession();
|
||||
}
|
||||
|
||||
public Object getAttribute (String name) {
|
||||
if (name == null) {
|
||||
throw new NullPointerException ("null name parameter");
|
||||
}
|
||||
if (attributes == null) {
|
||||
attributes = getHttpContext().getAttributes();
|
||||
}
|
||||
return attributes.get (name);
|
||||
}
|
||||
|
||||
public void setAttribute (String name, Object value) {
|
||||
if (name == null) {
|
||||
throw new NullPointerException ("null name parameter");
|
||||
}
|
||||
if (attributes == null) {
|
||||
attributes = getHttpContext().getAttributes();
|
||||
}
|
||||
attributes.put (name, value);
|
||||
}
|
||||
|
||||
public void setStreams (InputStream i, OutputStream o) {
|
||||
assert uis != null;
|
||||
if (i != null) {
|
||||
uis = i;
|
||||
}
|
||||
if (o != null) {
|
||||
uos = o;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PP
|
||||
*/
|
||||
HttpConnection getConnection () {
|
||||
return connection;
|
||||
}
|
||||
|
||||
ServerImpl getServerImpl () {
|
||||
return getHttpContext().getServerImpl();
|
||||
}
|
||||
|
||||
public HttpPrincipal getPrincipal () {
|
||||
return principal;
|
||||
}
|
||||
|
||||
void setPrincipal (HttpPrincipal principal) {
|
||||
this.principal = principal;
|
||||
}
|
||||
|
||||
static ExchangeImpl get (HttpExchange t) {
|
||||
if (t instanceof HttpExchangeImpl) {
|
||||
return ((HttpExchangeImpl)t).getExchangeImpl();
|
||||
} else {
|
||||
assert t instanceof HttpsExchangeImpl;
|
||||
return ((HttpsExchangeImpl)t).getExchangeImpl();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An OutputStream which wraps another stream
|
||||
* which is supplied either at creation time, or sometime later.
|
||||
* If a caller/user tries to write to this stream before
|
||||
* the wrapped stream has been provided, then an IOException will
|
||||
* be thrown.
|
||||
*/
|
||||
class PlaceholderOutputStream extends java.io.OutputStream {
|
||||
|
||||
OutputStream wrapped;
|
||||
|
||||
PlaceholderOutputStream (OutputStream os) {
|
||||
wrapped = os;
|
||||
}
|
||||
|
||||
void setWrappedStream (OutputStream os) {
|
||||
wrapped = os;
|
||||
}
|
||||
|
||||
boolean isWrapped () {
|
||||
return wrapped != null;
|
||||
}
|
||||
|
||||
private void checkWrap () throws IOException {
|
||||
if (wrapped == null) {
|
||||
throw new IOException ("response headers not sent yet");
|
||||
}
|
||||
}
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
checkWrap();
|
||||
wrapped.write (b);
|
||||
}
|
||||
|
||||
public void write(byte b[]) throws IOException {
|
||||
checkWrap();
|
||||
wrapped.write (b);
|
||||
}
|
||||
|
||||
public void write(byte b[], int off, int len) throws IOException {
|
||||
checkWrap();
|
||||
wrapped.write (b, off, len);
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
checkWrap();
|
||||
wrapped.flush();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
checkWrap();
|
||||
wrapped.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2010, 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.io.*;
|
||||
import java.net.*;
|
||||
import com.sun.net.httpserver.*;
|
||||
import com.sun.net.httpserver.spi.*;
|
||||
|
||||
/**
|
||||
* a class which allows the caller to read up to a defined
|
||||
* number of bytes off an underlying stream
|
||||
* close() does not close the underlying stream
|
||||
*/
|
||||
|
||||
class FixedLengthInputStream extends LeftOverInputStream {
|
||||
private long remaining;
|
||||
|
||||
FixedLengthInputStream (ExchangeImpl t, InputStream src, long len) {
|
||||
super (t, src);
|
||||
this.remaining = len;
|
||||
}
|
||||
|
||||
protected int readImpl (byte[]b, int off, int len) throws IOException {
|
||||
|
||||
eof = (remaining == 0L);
|
||||
if (eof) {
|
||||
return -1;
|
||||
}
|
||||
if (len > remaining) {
|
||||
len = (int)remaining;
|
||||
}
|
||||
int n = in.read(b, off, len);
|
||||
if (n > -1) {
|
||||
remaining -= n;
|
||||
if (remaining == 0) {
|
||||
t.getServerImpl().requestCompleted (t.getConnection());
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
public int available () throws IOException {
|
||||
if (eof) {
|
||||
return 0;
|
||||
}
|
||||
int n = in.available();
|
||||
return n < remaining? n: (int)remaining;
|
||||
}
|
||||
|
||||
public boolean markSupported () {return false;}
|
||||
|
||||
public void mark (int l) {
|
||||
}
|
||||
|
||||
public void reset () throws IOException {
|
||||
throw new IOException ("mark/reset not supported");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2006, 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.io.*;
|
||||
import java.net.*;
|
||||
import com.sun.net.httpserver.*;
|
||||
import com.sun.net.httpserver.spi.*;
|
||||
|
||||
/**
|
||||
* a class which allows the caller to write up to a defined
|
||||
* number of bytes to an underlying stream. The caller *must*
|
||||
* write the pre-defined number or else an exception will be thrown
|
||||
* and the whole request aborted.
|
||||
* normal close() does not close the underlying stream
|
||||
*/
|
||||
|
||||
class FixedLengthOutputStream extends FilterOutputStream
|
||||
{
|
||||
private long remaining;
|
||||
private boolean eof = false;
|
||||
private boolean closed = false;
|
||||
ExchangeImpl t;
|
||||
|
||||
FixedLengthOutputStream (ExchangeImpl t, OutputStream src, long len) {
|
||||
super (src);
|
||||
this.t = t;
|
||||
this.remaining = len;
|
||||
}
|
||||
|
||||
public void write (int b) throws IOException {
|
||||
if (closed) {
|
||||
throw new IOException ("stream closed");
|
||||
}
|
||||
eof = (remaining == 0);
|
||||
if (eof) {
|
||||
throw new StreamClosedException();
|
||||
}
|
||||
out.write(b);
|
||||
remaining --;
|
||||
}
|
||||
|
||||
public void write (byte[]b, int off, int len) throws IOException {
|
||||
if (closed) {
|
||||
throw new IOException ("stream closed");
|
||||
}
|
||||
eof = (remaining == 0);
|
||||
if (eof) {
|
||||
throw new StreamClosedException();
|
||||
}
|
||||
if (len > remaining) {
|
||||
// stream is still open, caller can retry
|
||||
throw new IOException ("too many bytes to write to stream");
|
||||
}
|
||||
out.write(b, off, len);
|
||||
remaining -= len;
|
||||
}
|
||||
|
||||
public void close () throws IOException {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
closed = true;
|
||||
if (remaining > 0) {
|
||||
t.close();
|
||||
throw new IOException ("insufficient bytes written to stream");
|
||||
}
|
||||
flush();
|
||||
eof = true;
|
||||
LeftOverInputStream is = t.getOriginalInputStream();
|
||||
if (!is.isClosed()) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
WriteFinishedEvent e = new WriteFinishedEvent (t);
|
||||
t.getHttpContext().getServerImpl().addEvent (e);
|
||||
}
|
||||
|
||||
// flush is a pass-through
|
||||
}
|
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2010, 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.io.*;
|
||||
import javax.net.ssl.*;
|
||||
import java.nio.channels.*;
|
||||
import java.lang.System.Logger;
|
||||
import java.lang.System.Logger.Level;
|
||||
import com.sun.net.httpserver.*;
|
||||
import com.sun.net.httpserver.spi.*;
|
||||
|
||||
/**
|
||||
* encapsulates all the connection specific state for a HTTP/S connection
|
||||
* one of these is hung from the selector attachment and is used to locate
|
||||
* everything from that.
|
||||
*/
|
||||
class HttpConnection {
|
||||
|
||||
HttpContextImpl context;
|
||||
SSLEngine engine;
|
||||
SSLContext sslContext;
|
||||
SSLStreams sslStreams;
|
||||
|
||||
/* high level streams returned to application */
|
||||
InputStream i;
|
||||
|
||||
/* low level stream that sits directly over channel */
|
||||
InputStream raw;
|
||||
OutputStream rawout;
|
||||
|
||||
SocketChannel chan;
|
||||
SelectionKey selectionKey;
|
||||
String protocol;
|
||||
long time;
|
||||
volatile long creationTime; // time this connection was created
|
||||
volatile long rspStartedTime; // time we started writing the response
|
||||
int remaining;
|
||||
boolean closed = false;
|
||||
Logger logger;
|
||||
|
||||
public enum State {IDLE, REQUEST, RESPONSE};
|
||||
volatile State state;
|
||||
|
||||
public String toString() {
|
||||
String s = null;
|
||||
if (chan != null) {
|
||||
s = chan.toString();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
HttpConnection () {
|
||||
}
|
||||
|
||||
void setChannel (SocketChannel c) {
|
||||
chan = c;
|
||||
}
|
||||
|
||||
void setContext (HttpContextImpl ctx) {
|
||||
context = ctx;
|
||||
}
|
||||
|
||||
State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
void setState (State s) {
|
||||
state = s;
|
||||
}
|
||||
|
||||
void setParameters (
|
||||
InputStream in, OutputStream rawout, SocketChannel chan,
|
||||
SSLEngine engine, SSLStreams sslStreams, SSLContext sslContext, String protocol,
|
||||
HttpContextImpl context, InputStream raw
|
||||
)
|
||||
{
|
||||
this.context = context;
|
||||
this.i = in;
|
||||
this.rawout = rawout;
|
||||
this.raw = raw;
|
||||
this.protocol = protocol;
|
||||
this.engine = engine;
|
||||
this.chan = chan;
|
||||
this.sslContext = sslContext;
|
||||
this.sslStreams = sslStreams;
|
||||
this.logger = context.getLogger();
|
||||
}
|
||||
|
||||
SocketChannel getChannel () {
|
||||
return chan;
|
||||
}
|
||||
|
||||
synchronized void close () {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
closed = true;
|
||||
if (logger != null && chan != null) {
|
||||
logger.log (Level.TRACE, "Closing connection: " + chan.toString());
|
||||
}
|
||||
|
||||
if (!chan.isOpen()) {
|
||||
ServerImpl.dprint ("Channel already closed");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
/* need to ensure temporary selectors are closed */
|
||||
if (raw != null) {
|
||||
raw.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
ServerImpl.dprint (e);
|
||||
}
|
||||
try {
|
||||
if (rawout != null) {
|
||||
rawout.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
ServerImpl.dprint (e);
|
||||
}
|
||||
try {
|
||||
if (sslStreams != null) {
|
||||
sslStreams.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
ServerImpl.dprint (e);
|
||||
}
|
||||
try {
|
||||
chan.close();
|
||||
} catch (IOException e) {
|
||||
ServerImpl.dprint (e);
|
||||
}
|
||||
}
|
||||
|
||||
/* remaining is the number of bytes left on the lowest level inputstream
|
||||
* after the exchange is finished
|
||||
*/
|
||||
void setRemaining (int r) {
|
||||
remaining = r;
|
||||
}
|
||||
|
||||
int getRemaining () {
|
||||
return remaining;
|
||||
}
|
||||
|
||||
SelectionKey getSelectionKey () {
|
||||
return selectionKey;
|
||||
}
|
||||
|
||||
InputStream getInputStream () {
|
||||
return i;
|
||||
}
|
||||
|
||||
OutputStream getRawOutputStream () {
|
||||
return rawout;
|
||||
}
|
||||
|
||||
String getProtocol () {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
SSLEngine getSSLEngine () {
|
||||
return engine;
|
||||
}
|
||||
|
||||
SSLContext getSSLContext () {
|
||||
return sslContext;
|
||||
}
|
||||
|
||||
HttpContextImpl getHttpContext () {
|
||||
return context;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2006, 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.io.*;
|
||||
import java.util.*;
|
||||
import java.lang.System.Logger;
|
||||
import com.sun.net.httpserver.*;
|
||||
import com.sun.net.httpserver.spi.*;
|
||||
|
||||
/**
|
||||
* HttpContext represents a mapping between a protocol (http or https) together with a root URI path
|
||||
* to a {@link HttpHandler} which is invoked to handle requests destined
|
||||
* for the protocol/path on the associated HttpServer.
|
||||
* <p>
|
||||
* HttpContext instances are created by {@link HttpServer#createContext(String,String,HttpHandler,Object)}
|
||||
* <p>
|
||||
*/
|
||||
class HttpContextImpl extends HttpContext {
|
||||
|
||||
private String path;
|
||||
private String protocol;
|
||||
private HttpHandler handler;
|
||||
private Map<String,Object> attributes = new HashMap<String,Object>();
|
||||
private ServerImpl server;
|
||||
/* system filters, not visible to applications */
|
||||
private LinkedList<Filter> sfilters = new LinkedList<Filter>();
|
||||
/* user filters, set by applications */
|
||||
private LinkedList<Filter> ufilters = new LinkedList<Filter>();
|
||||
private Authenticator authenticator;
|
||||
private AuthFilter authfilter;
|
||||
|
||||
/**
|
||||
* constructor is package private.
|
||||
*/
|
||||
HttpContextImpl (String protocol, String path, HttpHandler cb, ServerImpl server) {
|
||||
if (path == null || protocol == null || path.length() < 1 || path.charAt(0) != '/') {
|
||||
throw new IllegalArgumentException ("Illegal value for path or protocol");
|
||||
}
|
||||
this.protocol = protocol.toLowerCase();
|
||||
this.path = path;
|
||||
if (!this.protocol.equals ("http") && !this.protocol.equals ("https")) {
|
||||
throw new IllegalArgumentException ("Illegal value for protocol");
|
||||
}
|
||||
this.handler = cb;
|
||||
this.server = server;
|
||||
authfilter = new AuthFilter(null);
|
||||
sfilters.add (authfilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the handler for this context
|
||||
* @return the HttpHandler for this context
|
||||
*/
|
||||
public HttpHandler getHandler () {
|
||||
return handler;
|
||||
}
|
||||
|
||||
public void setHandler (HttpHandler h) {
|
||||
if (h == null) {
|
||||
throw new NullPointerException ("Null handler parameter");
|
||||
}
|
||||
if (handler != null) {
|
||||
throw new IllegalArgumentException ("handler already set");
|
||||
}
|
||||
handler = h;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the path this context was created with
|
||||
* @return this context's path
|
||||
*/
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the server this context was created with
|
||||
* @return this context's server
|
||||
*/
|
||||
public HttpServer getServer () {
|
||||
return server.getWrapper();
|
||||
}
|
||||
|
||||
ServerImpl getServerImpl () {
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the protocol this context was created with
|
||||
* @return this context's path
|
||||
*/
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a mutable Map, which can be used to pass
|
||||
* configuration and other data to Filter modules
|
||||
* and to the context's exchange handler.
|
||||
* <p>
|
||||
* Every attribute stored in this Map will be visible to
|
||||
* every HttpExchange processed by this context
|
||||
*/
|
||||
public Map<String,Object> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public List<Filter> getFilters () {
|
||||
return ufilters;
|
||||
}
|
||||
|
||||
List<Filter> getSystemFilters () {
|
||||
return sfilters;
|
||||
}
|
||||
|
||||
public Authenticator setAuthenticator (Authenticator auth) {
|
||||
Authenticator old = authenticator;
|
||||
authenticator = auth;
|
||||
authfilter.setAuthenticator (auth);
|
||||
return old;
|
||||
}
|
||||
|
||||
public Authenticator getAuthenticator () {
|
||||
return authenticator;
|
||||
}
|
||||
Logger getLogger () {
|
||||
return server.getLogger();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* A Http error
|
||||
*/
|
||||
class HttpError extends RuntimeException {
|
||||
private static final long serialVersionUID = 8769596371344178179L;
|
||||
|
||||
public HttpError (String msg) {
|
||||
super (msg);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2006, 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.io.*;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.*;
|
||||
import java.net.*;
|
||||
import javax.net.ssl.*;
|
||||
import java.util.*;
|
||||
import com.sun.net.httpserver.*;
|
||||
import com.sun.net.httpserver.spi.*;
|
||||
|
||||
class HttpExchangeImpl extends HttpExchange {
|
||||
|
||||
ExchangeImpl impl;
|
||||
|
||||
HttpExchangeImpl (ExchangeImpl impl) {
|
||||
this.impl = impl;
|
||||
}
|
||||
|
||||
public Headers getRequestHeaders () {
|
||||
return impl.getRequestHeaders();
|
||||
}
|
||||
|
||||
public Headers getResponseHeaders () {
|
||||
return impl.getResponseHeaders();
|
||||
}
|
||||
|
||||
public URI getRequestURI () {
|
||||
return impl.getRequestURI();
|
||||
}
|
||||
|
||||
public String getRequestMethod (){
|
||||
return impl.getRequestMethod();
|
||||
}
|
||||
|
||||
public HttpContextImpl getHttpContext (){
|
||||
return impl.getHttpContext();
|
||||
}
|
||||
|
||||
public void close () {
|
||||
impl.close();
|
||||
}
|
||||
|
||||
public InputStream getRequestBody () {
|
||||
return impl.getRequestBody();
|
||||
}
|
||||
|
||||
public int getResponseCode () {
|
||||
return impl.getResponseCode();
|
||||
}
|
||||
|
||||
public OutputStream getResponseBody () {
|
||||
return impl.getResponseBody();
|
||||
}
|
||||
|
||||
|
||||
public void sendResponseHeaders (int rCode, long contentLen)
|
||||
throws IOException
|
||||
{
|
||||
impl.sendResponseHeaders (rCode, contentLen);
|
||||
}
|
||||
|
||||
public InetSocketAddress getRemoteAddress (){
|
||||
return impl.getRemoteAddress();
|
||||
}
|
||||
|
||||
public InetSocketAddress getLocalAddress (){
|
||||
return impl.getLocalAddress();
|
||||
}
|
||||
|
||||
public String getProtocol (){
|
||||
return impl.getProtocol();
|
||||
}
|
||||
|
||||
public Object getAttribute (String name) {
|
||||
return impl.getAttribute (name);
|
||||
}
|
||||
|
||||
public void setAttribute (String name, Object value) {
|
||||
impl.setAttribute (name, value);
|
||||
}
|
||||
|
||||
public void setStreams (InputStream i, OutputStream o) {
|
||||
impl.setStreams (i, o);
|
||||
}
|
||||
|
||||
public HttpPrincipal getPrincipal () {
|
||||
return impl.getPrincipal();
|
||||
}
|
||||
|
||||
ExchangeImpl getExchangeImpl () {
|
||||
return impl;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 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.net.*;
|
||||
import java.io.*;
|
||||
import java.nio.*;
|
||||
import java.security.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import javax.net.ssl.*;
|
||||
import com.sun.net.httpserver.*;
|
||||
import com.sun.net.httpserver.spi.*;
|
||||
|
||||
public class HttpServerImpl extends HttpServer {
|
||||
|
||||
ServerImpl server;
|
||||
|
||||
HttpServerImpl () throws IOException {
|
||||
this (new InetSocketAddress(80), 0);
|
||||
}
|
||||
|
||||
HttpServerImpl (
|
||||
InetSocketAddress addr, int backlog
|
||||
) throws IOException {
|
||||
server = new ServerImpl (this, "http", addr, backlog);
|
||||
}
|
||||
|
||||
public void bind (InetSocketAddress addr, int backlog) throws IOException {
|
||||
server.bind (addr, backlog);
|
||||
}
|
||||
|
||||
public void start () {
|
||||
server.start();
|
||||
}
|
||||
|
||||
public void setExecutor (Executor executor) {
|
||||
server.setExecutor(executor);
|
||||
}
|
||||
|
||||
public Executor getExecutor () {
|
||||
return server.getExecutor();
|
||||
}
|
||||
|
||||
public void stop (int delay) {
|
||||
server.stop (delay);
|
||||
}
|
||||
|
||||
public HttpContextImpl createContext (String path, HttpHandler handler) {
|
||||
return server.createContext (path, handler);
|
||||
}
|
||||
|
||||
public HttpContextImpl createContext (String path) {
|
||||
return server.createContext (path);
|
||||
}
|
||||
|
||||
public void removeContext (String path) throws IllegalArgumentException {
|
||||
server.removeContext (path);
|
||||
}
|
||||
|
||||
public void removeContext (HttpContext context) throws IllegalArgumentException {
|
||||
server.removeContext (context);
|
||||
}
|
||||
|
||||
public InetSocketAddress getAddress() {
|
||||
return server.getAddress();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2006, 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.io.*;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.*;
|
||||
import java.net.*;
|
||||
import javax.net.ssl.*;
|
||||
import java.util.*;
|
||||
import com.sun.net.httpserver.*;
|
||||
import com.sun.net.httpserver.spi.*;
|
||||
|
||||
class HttpsExchangeImpl extends HttpsExchange {
|
||||
|
||||
ExchangeImpl impl;
|
||||
|
||||
HttpsExchangeImpl (ExchangeImpl impl) throws IOException {
|
||||
this.impl = impl;
|
||||
}
|
||||
|
||||
public Headers getRequestHeaders () {
|
||||
return impl.getRequestHeaders();
|
||||
}
|
||||
|
||||
public Headers getResponseHeaders () {
|
||||
return impl.getResponseHeaders();
|
||||
}
|
||||
|
||||
public URI getRequestURI () {
|
||||
return impl.getRequestURI();
|
||||
}
|
||||
|
||||
public String getRequestMethod (){
|
||||
return impl.getRequestMethod();
|
||||
}
|
||||
|
||||
public HttpContextImpl getHttpContext (){
|
||||
return impl.getHttpContext();
|
||||
}
|
||||
|
||||
public void close () {
|
||||
impl.close();
|
||||
}
|
||||
|
||||
public InputStream getRequestBody () {
|
||||
return impl.getRequestBody();
|
||||
}
|
||||
|
||||
public int getResponseCode () {
|
||||
return impl.getResponseCode();
|
||||
}
|
||||
|
||||
public OutputStream getResponseBody () {
|
||||
return impl.getResponseBody();
|
||||
}
|
||||
|
||||
|
||||
public void sendResponseHeaders (int rCode, long contentLen)
|
||||
throws IOException
|
||||
{
|
||||
impl.sendResponseHeaders (rCode, contentLen);
|
||||
}
|
||||
|
||||
public InetSocketAddress getRemoteAddress (){
|
||||
return impl.getRemoteAddress();
|
||||
}
|
||||
|
||||
public InetSocketAddress getLocalAddress (){
|
||||
return impl.getLocalAddress();
|
||||
}
|
||||
|
||||
public String getProtocol (){
|
||||
return impl.getProtocol();
|
||||
}
|
||||
|
||||
public SSLSession getSSLSession () {
|
||||
return impl.getSSLSession ();
|
||||
}
|
||||
|
||||
public Object getAttribute (String name) {
|
||||
return impl.getAttribute (name);
|
||||
}
|
||||
|
||||
public void setAttribute (String name, Object value) {
|
||||
impl.setAttribute (name, value);
|
||||
}
|
||||
|
||||
public void setStreams (InputStream i, OutputStream o) {
|
||||
impl.setStreams (i, o);
|
||||
}
|
||||
|
||||
public HttpPrincipal getPrincipal () {
|
||||
return impl.getPrincipal();
|
||||
}
|
||||
|
||||
ExchangeImpl getExchangeImpl () {
|
||||
return impl;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 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.net.*;
|
||||
import java.io.*;
|
||||
import java.nio.*;
|
||||
import java.security.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import javax.net.ssl.*;
|
||||
import com.sun.net.httpserver.*;
|
||||
import com.sun.net.httpserver.spi.*;
|
||||
|
||||
public class HttpsServerImpl extends HttpsServer {
|
||||
|
||||
ServerImpl server;
|
||||
|
||||
HttpsServerImpl () throws IOException {
|
||||
this (new InetSocketAddress(443), 0);
|
||||
}
|
||||
|
||||
HttpsServerImpl (
|
||||
InetSocketAddress addr, int backlog
|
||||
) throws IOException {
|
||||
server = new ServerImpl (this, "https", addr, backlog);
|
||||
}
|
||||
|
||||
public void setHttpsConfigurator (HttpsConfigurator config) {
|
||||
server.setHttpsConfigurator (config);
|
||||
}
|
||||
|
||||
public HttpsConfigurator getHttpsConfigurator () {
|
||||
return server.getHttpsConfigurator();
|
||||
}
|
||||
|
||||
public void bind (InetSocketAddress addr, int backlog) throws IOException {
|
||||
server.bind (addr, backlog);
|
||||
}
|
||||
|
||||
public void start () {
|
||||
server.start();
|
||||
}
|
||||
|
||||
public void setExecutor (Executor executor) {
|
||||
server.setExecutor(executor);
|
||||
}
|
||||
|
||||
public Executor getExecutor () {
|
||||
return server.getExecutor();
|
||||
}
|
||||
|
||||
public void stop (int delay) {
|
||||
server.stop (delay);
|
||||
}
|
||||
|
||||
public HttpContextImpl createContext (String path, HttpHandler handler) {
|
||||
return server.createContext (path, handler);
|
||||
}
|
||||
|
||||
public HttpContextImpl createContext (String path) {
|
||||
return server.createContext (path);
|
||||
}
|
||||
|
||||
public void removeContext (String path) throws IllegalArgumentException {
|
||||
server.removeContext (path);
|
||||
}
|
||||
|
||||
public void removeContext (HttpContext context) throws IllegalArgumentException {
|
||||
server.removeContext (context);
|
||||
}
|
||||
|
||||
public InetSocketAddress getAddress() {
|
||||
return server.getAddress();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2007, 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.io.*;
|
||||
import com.sun.net.httpserver.*;
|
||||
import com.sun.net.httpserver.spi.*;
|
||||
|
||||
/**
|
||||
* a (filter) input stream which can tell us if bytes are "left over"
|
||||
* on the underlying stream which can be read (without blocking)
|
||||
* on another instance of this class.
|
||||
*
|
||||
* The class can also report if all bytes "expected" to be read
|
||||
* were read, by the time close() was called. In that case,
|
||||
* bytes may be drained to consume them (by calling drain() ).
|
||||
*
|
||||
* isEOF() returns true, when all expected bytes have been read
|
||||
*/
|
||||
abstract class LeftOverInputStream extends FilterInputStream {
|
||||
final ExchangeImpl t;
|
||||
final ServerImpl server;
|
||||
protected boolean closed = false;
|
||||
protected boolean eof = false;
|
||||
byte[] one = new byte [1];
|
||||
|
||||
public LeftOverInputStream (ExchangeImpl t, InputStream src) {
|
||||
super (src);
|
||||
this.t = t;
|
||||
this.server = t.getServerImpl();
|
||||
}
|
||||
/**
|
||||
* if bytes are left over buffered on *the UNDERLYING* stream
|
||||
*/
|
||||
public boolean isDataBuffered () throws IOException {
|
||||
assert eof;
|
||||
return super.available() > 0;
|
||||
}
|
||||
|
||||
public void close () throws IOException {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
closed = true;
|
||||
if (!eof) {
|
||||
eof = drain (ServerConfig.getDrainAmount());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isClosed () {
|
||||
return closed;
|
||||
}
|
||||
|
||||
public boolean isEOF () {
|
||||
return eof;
|
||||
}
|
||||
|
||||
protected abstract int readImpl (byte[]b, int off, int len) throws IOException;
|
||||
|
||||
public synchronized int read () throws IOException {
|
||||
if (closed) {
|
||||
throw new IOException ("Stream is closed");
|
||||
}
|
||||
int c = readImpl (one, 0, 1);
|
||||
if (c == -1 || c == 0) {
|
||||
return c;
|
||||
} else {
|
||||
return one[0] & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized int read (byte[]b, int off, int len) throws IOException {
|
||||
if (closed) {
|
||||
throw new IOException ("Stream is closed");
|
||||
}
|
||||
return readImpl (b, off, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* read and discard up to l bytes or "eof" occurs,
|
||||
* (whichever is first). Then return true if the stream
|
||||
* is at eof (ie. all bytes were read) or false if not
|
||||
* (still bytes to be read)
|
||||
*/
|
||||
public boolean drain (long l) throws IOException {
|
||||
int bufSize = 2048;
|
||||
byte[] db = new byte [bufSize];
|
||||
while (l > 0) {
|
||||
if (server.isFinishing()) {
|
||||
break;
|
||||
}
|
||||
long len = readImpl (db, 0, bufSize);
|
||||
if (len == -1) {
|
||||
eof = true;
|
||||
return true;
|
||||
} else {
|
||||
l = l - len;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
406
src/jdk.httpserver/share/classes/sun/net/httpserver/Request.java
Normal file
406
src/jdk.httpserver/share/classes/sun/net/httpserver/Request.java
Normal file
|
@ -0,0 +1,406 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2013, 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.nio.*;
|
||||
import java.io.*;
|
||||
import java.nio.channels.*;
|
||||
import com.sun.net.httpserver.*;
|
||||
|
||||
/**
|
||||
*/
|
||||
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 {
|
||||
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;
|
||||
@SuppressWarnings("fallthrough")
|
||||
Headers headers () throws IOException {
|
||||
if (hdrs != null) {
|
||||
return hdrs;
|
||||
}
|
||||
hdrs = new Headers();
|
||||
|
||||
char s[] = new char[10];
|
||||
int len = 0;
|
||||
|
||||
int firstc = is.read();
|
||||
|
||||
// check for empty headers
|
||||
if (firstc == CR || firstc == LF) {
|
||||
int c = is.read();
|
||||
if (c == CR || c == LF) {
|
||||
return hdrs;
|
||||
}
|
||||
s[0] = (char)firstc;
|
||||
len = 1;
|
||||
firstc = c;
|
||||
}
|
||||
|
||||
while (firstc != LF && firstc != CR && firstc >= 0) {
|
||||
int keyend = -1;
|
||||
int c;
|
||||
boolean inKey = firstc > ' ';
|
||||
s[len++] = (char) firstc;
|
||||
parseloop:{
|
||||
while ((c = is.read()) >= 0) {
|
||||
switch (c) {
|
||||
/*fallthrough*/
|
||||
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);
|
||||
|
||||
if (hdrs.size() >= ServerConfig.getMaxReqHeaders()) {
|
||||
throw new IOException("Maximum number of request headers (" +
|
||||
"sun.net.httpserver.maxReqHeaders) exceeded, " +
|
||||
ServerConfig.getMaxReqHeaders() + ".");
|
||||
}
|
||||
|
||||
hdrs.add (k,v);
|
||||
len = 0;
|
||||
}
|
||||
return hdrs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements blocking reading semantics on top of a non-blocking channel
|
||||
*/
|
||||
|
||||
static class ReadStream extends InputStream {
|
||||
SocketChannel channel;
|
||||
ByteBuffer chanbuf;
|
||||
byte[] one;
|
||||
private boolean closed = false, eof = false;
|
||||
ByteBuffer markBuf; /* reads may be satisfied from this buffer */
|
||||
boolean marked;
|
||||
boolean reset;
|
||||
int readlimit;
|
||||
static long readTimeout;
|
||||
ServerImpl server;
|
||||
final static int BUFSIZE = 8 * 1024;
|
||||
|
||||
public ReadStream (ServerImpl server, SocketChannel chan) throws IOException {
|
||||
this.channel = chan;
|
||||
this.server = server;
|
||||
chanbuf = ByteBuffer.allocate (BUFSIZE);
|
||||
chanbuf.clear();
|
||||
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;
|
||||
}
|
||||
|
||||
assert channel.isBlocking();
|
||||
|
||||
if (off < 0 || srclen < 0|| srclen > (b.length-off)) {
|
||||
throw new IndexOutOfBoundsException ();
|
||||
}
|
||||
|
||||
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 */
|
||||
chanbuf.clear ();
|
||||
if (srclen < BUFSIZE) {
|
||||
chanbuf.limit (srclen);
|
||||
}
|
||||
do {
|
||||
willreturn = channel.read (chanbuf);
|
||||
} while (willreturn == 0);
|
||||
if (willreturn == -1) {
|
||||
eof = true;
|
||||
return -1;
|
||||
}
|
||||
chanbuf.flip ();
|
||||
chanbuf.get(b, off, willreturn);
|
||||
|
||||
if (marked) { /* copy into markBuf */
|
||||
try {
|
||||
markBuf.put (b, off, willreturn);
|
||||
} catch (BufferOverflowException e) {
|
||||
marked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return willreturn;
|
||||
}
|
||||
|
||||
public boolean markSupported () {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Does not query the OS socket */
|
||||
public synchronized int available () throws IOException {
|
||||
if (closed)
|
||||
throw new IOException ("Stream is closed");
|
||||
|
||||
if (eof)
|
||||
return -1;
|
||||
|
||||
if (reset)
|
||||
return markBuf.remaining();
|
||||
|
||||
return chanbuf.remaining();
|
||||
}
|
||||
|
||||
public void close () throws IOException {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
channel.close ();
|
||||
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;
|
||||
boolean closed;
|
||||
byte[] one;
|
||||
ServerImpl server;
|
||||
|
||||
public WriteStream (ServerImpl server, SocketChannel channel) throws IOException {
|
||||
this.channel = channel;
|
||||
this.server = server;
|
||||
assert channel.isBlocking();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public void close () throws IOException {
|
||||
if (closed)
|
||||
return;
|
||||
//server.logStackTrace ("Request.OS.close: isOpen="+channel.isOpen());
|
||||
channel.close ();
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,660 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2011, 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.net.*;
|
||||
import java.nio.*;
|
||||
import java.io.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.concurrent.locks.*;
|
||||
import javax.net.ssl.*;
|
||||
import javax.net.ssl.SSLEngineResult.*;
|
||||
import com.sun.net.httpserver.*;
|
||||
|
||||
/**
|
||||
* given a non-blocking SocketChannel, it produces
|
||||
* (blocking) streams which encrypt/decrypt the SSL content
|
||||
* and handle the SSL handshaking automatically.
|
||||
*/
|
||||
|
||||
class SSLStreams {
|
||||
|
||||
SSLContext sslctx;
|
||||
SocketChannel chan;
|
||||
TimeSource time;
|
||||
ServerImpl server;
|
||||
SSLEngine engine;
|
||||
EngineWrapper wrapper;
|
||||
OutputStream os;
|
||||
InputStream is;
|
||||
|
||||
/* held by thread doing the hand-shake on this connection */
|
||||
Lock handshaking = new ReentrantLock();
|
||||
|
||||
SSLStreams (ServerImpl server, SSLContext sslctx, SocketChannel chan) throws IOException {
|
||||
this.server = server;
|
||||
this.time= (TimeSource)server;
|
||||
this.sslctx= sslctx;
|
||||
this.chan= chan;
|
||||
InetSocketAddress addr =
|
||||
(InetSocketAddress)chan.socket().getRemoteSocketAddress();
|
||||
engine = sslctx.createSSLEngine (addr.getHostName(), addr.getPort());
|
||||
engine.setUseClientMode (false);
|
||||
HttpsConfigurator cfg = server.getHttpsConfigurator();
|
||||
configureEngine (cfg, addr);
|
||||
wrapper = new EngineWrapper (chan, engine);
|
||||
}
|
||||
|
||||
private void configureEngine(HttpsConfigurator cfg, InetSocketAddress addr){
|
||||
if (cfg != null) {
|
||||
Parameters params = new Parameters (cfg, addr);
|
||||
//BEGIN_TIGER_EXCLUDE
|
||||
cfg.configure (params);
|
||||
SSLParameters sslParams = params.getSSLParameters();
|
||||
if (sslParams != null) {
|
||||
engine.setSSLParameters (sslParams);
|
||||
} else
|
||||
//END_TIGER_EXCLUDE
|
||||
{
|
||||
/* tiger compatibility */
|
||||
if (params.getCipherSuites() != null) {
|
||||
try {
|
||||
engine.setEnabledCipherSuites (
|
||||
params.getCipherSuites()
|
||||
);
|
||||
} catch (IllegalArgumentException e) { /* LOG */}
|
||||
}
|
||||
engine.setNeedClientAuth (params.getNeedClientAuth());
|
||||
engine.setWantClientAuth (params.getWantClientAuth());
|
||||
if (params.getProtocols() != null) {
|
||||
try {
|
||||
engine.setEnabledProtocols (
|
||||
params.getProtocols()
|
||||
);
|
||||
} catch (IllegalArgumentException e) { /* LOG */}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Parameters extends HttpsParameters {
|
||||
InetSocketAddress addr;
|
||||
HttpsConfigurator cfg;
|
||||
|
||||
Parameters (HttpsConfigurator cfg, InetSocketAddress addr) {
|
||||
this.addr = addr;
|
||||
this.cfg = cfg;
|
||||
}
|
||||
public InetSocketAddress getClientAddress () {
|
||||
return addr;
|
||||
}
|
||||
public HttpsConfigurator getHttpsConfigurator() {
|
||||
return cfg;
|
||||
}
|
||||
//BEGIN_TIGER_EXCLUDE
|
||||
SSLParameters params;
|
||||
public void setSSLParameters (SSLParameters p) {
|
||||
params = p;
|
||||
}
|
||||
SSLParameters getSSLParameters () {
|
||||
return params;
|
||||
}
|
||||
//END_TIGER_EXCLUDE
|
||||
}
|
||||
|
||||
/**
|
||||
* cleanup resources allocated inside this object
|
||||
*/
|
||||
void close () throws IOException {
|
||||
wrapper.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* return the SSL InputStream
|
||||
*/
|
||||
InputStream getInputStream () throws IOException {
|
||||
if (is == null) {
|
||||
is = new InputStream();
|
||||
}
|
||||
return is;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the SSL OutputStream
|
||||
*/
|
||||
OutputStream getOutputStream () throws IOException {
|
||||
if (os == null) {
|
||||
os = new OutputStream();
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
SSLEngine getSSLEngine () {
|
||||
return engine;
|
||||
}
|
||||
|
||||
/**
|
||||
* request the engine to repeat the handshake on this session
|
||||
* the handshake must be driven by reads/writes on the streams
|
||||
* Normally, not necessary to call this.
|
||||
*/
|
||||
void beginHandshake() throws SSLException {
|
||||
engine.beginHandshake();
|
||||
}
|
||||
|
||||
class WrapperResult {
|
||||
SSLEngineResult result;
|
||||
|
||||
/* if passed in buffer was not big enough then the
|
||||
* a reallocated buffer is returned here
|
||||
*/
|
||||
ByteBuffer buf;
|
||||
}
|
||||
|
||||
int app_buf_size;
|
||||
int packet_buf_size;
|
||||
|
||||
enum BufType {
|
||||
PACKET, APPLICATION
|
||||
};
|
||||
|
||||
private ByteBuffer allocate (BufType type) {
|
||||
return allocate (type, -1);
|
||||
}
|
||||
|
||||
private ByteBuffer allocate (BufType type, int len) {
|
||||
assert engine != null;
|
||||
synchronized (this) {
|
||||
int size;
|
||||
if (type == BufType.PACKET) {
|
||||
if (packet_buf_size == 0) {
|
||||
SSLSession sess = engine.getSession();
|
||||
packet_buf_size = sess.getPacketBufferSize();
|
||||
}
|
||||
if (len > packet_buf_size) {
|
||||
packet_buf_size = len;
|
||||
}
|
||||
size = packet_buf_size;
|
||||
} else {
|
||||
if (app_buf_size == 0) {
|
||||
SSLSession sess = engine.getSession();
|
||||
app_buf_size = sess.getApplicationBufferSize();
|
||||
}
|
||||
if (len > app_buf_size) {
|
||||
app_buf_size = len;
|
||||
}
|
||||
size = app_buf_size;
|
||||
}
|
||||
return ByteBuffer.allocate (size);
|
||||
}
|
||||
}
|
||||
|
||||
/* reallocates the buffer by :-
|
||||
* 1. creating a new buffer double the size of the old one
|
||||
* 2. putting the contents of the old buffer into the new one
|
||||
* 3. set xx_buf_size to the new size if it was smaller than new size
|
||||
*
|
||||
* flip is set to true if the old buffer needs to be flipped
|
||||
* before it is copied.
|
||||
*/
|
||||
private ByteBuffer realloc (ByteBuffer b, boolean flip, BufType type) {
|
||||
synchronized (this) {
|
||||
int nsize = 2 * b.capacity();
|
||||
ByteBuffer n = allocate (type, nsize);
|
||||
if (flip) {
|
||||
b.flip();
|
||||
}
|
||||
n.put(b);
|
||||
b = n;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
/**
|
||||
* This is a thin wrapper over SSLEngine and the SocketChannel,
|
||||
* which guarantees the ordering of wraps/unwraps with respect to the underlying
|
||||
* channel read/writes. It handles the UNDER/OVERFLOW status codes
|
||||
* It does not handle the handshaking status codes, or the CLOSED status code
|
||||
* though once the engine is closed, any attempt to read/write to it
|
||||
* will get an exception. The overall result is returned.
|
||||
* It functions synchronously/blocking
|
||||
*/
|
||||
class EngineWrapper {
|
||||
|
||||
SocketChannel chan;
|
||||
SSLEngine engine;
|
||||
Object wrapLock, unwrapLock;
|
||||
ByteBuffer unwrap_src, wrap_dst;
|
||||
boolean closed = false;
|
||||
int u_remaining; // the number of bytes left in unwrap_src after an unwrap()
|
||||
|
||||
EngineWrapper (SocketChannel chan, SSLEngine engine) throws IOException {
|
||||
this.chan = chan;
|
||||
this.engine = engine;
|
||||
wrapLock = new Object();
|
||||
unwrapLock = new Object();
|
||||
unwrap_src = allocate(BufType.PACKET);
|
||||
wrap_dst = allocate(BufType.PACKET);
|
||||
}
|
||||
|
||||
void close () throws IOException {
|
||||
}
|
||||
|
||||
/* try to wrap and send the data in src. Handles OVERFLOW.
|
||||
* Might block if there is an outbound blockage or if another
|
||||
* thread is calling wrap(). Also, might not send any data
|
||||
* if an unwrap is needed.
|
||||
*/
|
||||
WrapperResult wrapAndSend(ByteBuffer src) throws IOException {
|
||||
return wrapAndSendX(src, false);
|
||||
}
|
||||
|
||||
WrapperResult wrapAndSendX(ByteBuffer src, boolean ignoreClose) throws IOException {
|
||||
if (closed && !ignoreClose) {
|
||||
throw new IOException ("Engine is closed");
|
||||
}
|
||||
Status status;
|
||||
WrapperResult r = new WrapperResult();
|
||||
synchronized (wrapLock) {
|
||||
wrap_dst.clear();
|
||||
do {
|
||||
r.result = engine.wrap (src, wrap_dst);
|
||||
status = r.result.getStatus();
|
||||
if (status == Status.BUFFER_OVERFLOW) {
|
||||
wrap_dst = realloc (wrap_dst, true, BufType.PACKET);
|
||||
}
|
||||
} while (status == Status.BUFFER_OVERFLOW);
|
||||
if (status == Status.CLOSED && !ignoreClose) {
|
||||
closed = true;
|
||||
return r;
|
||||
}
|
||||
if (r.result.bytesProduced() > 0) {
|
||||
wrap_dst.flip();
|
||||
int l = wrap_dst.remaining();
|
||||
assert l == r.result.bytesProduced();
|
||||
while (l>0) {
|
||||
l -= chan.write (wrap_dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/* block until a complete message is available and return it
|
||||
* in dst, together with the Result. dst may have been re-allocated
|
||||
* so caller should check the returned value in Result
|
||||
* If handshaking is in progress then, possibly no data is returned
|
||||
*/
|
||||
WrapperResult recvAndUnwrap(ByteBuffer dst) throws IOException {
|
||||
Status status = Status.OK;
|
||||
WrapperResult r = new WrapperResult();
|
||||
r.buf = dst;
|
||||
if (closed) {
|
||||
throw new IOException ("Engine is closed");
|
||||
}
|
||||
boolean needData;
|
||||
if (u_remaining > 0) {
|
||||
unwrap_src.compact();
|
||||
unwrap_src.flip();
|
||||
needData = false;
|
||||
} else {
|
||||
unwrap_src.clear();
|
||||
needData = true;
|
||||
}
|
||||
synchronized (unwrapLock) {
|
||||
int x;
|
||||
do {
|
||||
if (needData) {
|
||||
do {
|
||||
x = chan.read (unwrap_src);
|
||||
} while (x == 0);
|
||||
if (x == -1) {
|
||||
throw new IOException ("connection closed for reading");
|
||||
}
|
||||
unwrap_src.flip();
|
||||
}
|
||||
r.result = engine.unwrap (unwrap_src, r.buf);
|
||||
status = r.result.getStatus();
|
||||
if (status == Status.BUFFER_UNDERFLOW) {
|
||||
if (unwrap_src.limit() == unwrap_src.capacity()) {
|
||||
/* buffer not big enough */
|
||||
unwrap_src = realloc (
|
||||
unwrap_src, false, BufType.PACKET
|
||||
);
|
||||
} else {
|
||||
/* Buffer not full, just need to read more
|
||||
* data off the channel. Reset pointers
|
||||
* for reading off SocketChannel
|
||||
*/
|
||||
unwrap_src.position (unwrap_src.limit());
|
||||
unwrap_src.limit (unwrap_src.capacity());
|
||||
}
|
||||
needData = true;
|
||||
} else if (status == Status.BUFFER_OVERFLOW) {
|
||||
r.buf = realloc (r.buf, true, BufType.APPLICATION);
|
||||
needData = false;
|
||||
} else if (status == Status.CLOSED) {
|
||||
closed = true;
|
||||
r.buf.flip();
|
||||
return r;
|
||||
}
|
||||
} while (status != Status.OK);
|
||||
}
|
||||
u_remaining = unwrap_src.remaining();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* send the data in the given ByteBuffer. If a handshake is needed
|
||||
* then this is handled within this method. When this call returns,
|
||||
* all of the given user data has been sent and any handshake has been
|
||||
* completed. Caller should check if engine has been closed.
|
||||
*/
|
||||
public WrapperResult sendData (ByteBuffer src) throws IOException {
|
||||
WrapperResult r=null;
|
||||
while (src.remaining() > 0) {
|
||||
r = wrapper.wrapAndSend(src);
|
||||
Status status = r.result.getStatus();
|
||||
if (status == Status.CLOSED) {
|
||||
doClosure ();
|
||||
return r;
|
||||
}
|
||||
HandshakeStatus hs_status = r.result.getHandshakeStatus();
|
||||
if (hs_status != HandshakeStatus.FINISHED &&
|
||||
hs_status != HandshakeStatus.NOT_HANDSHAKING)
|
||||
{
|
||||
doHandshake(hs_status);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* read data thru the engine into the given ByteBuffer. If the
|
||||
* given buffer was not large enough, a new one is allocated
|
||||
* and returned. This call handles handshaking automatically.
|
||||
* Caller should check if engine has been closed.
|
||||
*/
|
||||
public WrapperResult recvData (ByteBuffer dst) throws IOException {
|
||||
/* we wait until some user data arrives */
|
||||
WrapperResult r = null;
|
||||
assert dst.position() == 0;
|
||||
while (dst.position() == 0) {
|
||||
r = wrapper.recvAndUnwrap (dst);
|
||||
dst = (r.buf != dst) ? r.buf: dst;
|
||||
Status status = r.result.getStatus();
|
||||
if (status == Status.CLOSED) {
|
||||
doClosure ();
|
||||
return r;
|
||||
}
|
||||
|
||||
HandshakeStatus hs_status = r.result.getHandshakeStatus();
|
||||
if (hs_status != HandshakeStatus.FINISHED &&
|
||||
hs_status != HandshakeStatus.NOT_HANDSHAKING)
|
||||
{
|
||||
doHandshake (hs_status);
|
||||
}
|
||||
}
|
||||
dst.flip();
|
||||
return r;
|
||||
}
|
||||
|
||||
/* we've received a close notify. Need to call wrap to send
|
||||
* the response
|
||||
*/
|
||||
void doClosure () throws IOException {
|
||||
try {
|
||||
handshaking.lock();
|
||||
ByteBuffer tmp = allocate(BufType.APPLICATION);
|
||||
WrapperResult r;
|
||||
do {
|
||||
tmp.clear();
|
||||
tmp.flip ();
|
||||
r = wrapper.wrapAndSendX (tmp, true);
|
||||
} while (r.result.getStatus() != Status.CLOSED);
|
||||
} finally {
|
||||
handshaking.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/* do the (complete) handshake after acquiring the handshake lock.
|
||||
* If two threads call this at the same time, then we depend
|
||||
* on the wrapper methods being idempotent. eg. if wrapAndSend()
|
||||
* is called with no data to send then there must be no problem
|
||||
*/
|
||||
@SuppressWarnings("fallthrough")
|
||||
void doHandshake (HandshakeStatus hs_status) throws IOException {
|
||||
try {
|
||||
handshaking.lock();
|
||||
ByteBuffer tmp = allocate(BufType.APPLICATION);
|
||||
while (hs_status != HandshakeStatus.FINISHED &&
|
||||
hs_status != HandshakeStatus.NOT_HANDSHAKING)
|
||||
{
|
||||
WrapperResult r = null;
|
||||
switch (hs_status) {
|
||||
case NEED_TASK:
|
||||
Runnable task;
|
||||
while ((task = engine.getDelegatedTask()) != null) {
|
||||
/* run in current thread, because we are already
|
||||
* running an external Executor
|
||||
*/
|
||||
task.run();
|
||||
}
|
||||
/* fall thru - call wrap again */
|
||||
case NEED_WRAP:
|
||||
tmp.clear();
|
||||
tmp.flip();
|
||||
r = wrapper.wrapAndSend(tmp);
|
||||
break;
|
||||
|
||||
case NEED_UNWRAP:
|
||||
tmp.clear();
|
||||
r = wrapper.recvAndUnwrap (tmp);
|
||||
if (r.buf != tmp) {
|
||||
tmp = r.buf;
|
||||
}
|
||||
assert tmp.position() == 0;
|
||||
break;
|
||||
}
|
||||
hs_status = r.result.getHandshakeStatus();
|
||||
}
|
||||
} finally {
|
||||
handshaking.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* represents an SSL input stream. Multiple https requests can
|
||||
* be sent over one stream. closing this stream causes an SSL close
|
||||
* input.
|
||||
*/
|
||||
class InputStream extends java.io.InputStream {
|
||||
|
||||
ByteBuffer bbuf;
|
||||
boolean closed = false;
|
||||
|
||||
/* this stream eof */
|
||||
boolean eof = false;
|
||||
|
||||
boolean needData = true;
|
||||
|
||||
InputStream () {
|
||||
bbuf = allocate (BufType.APPLICATION);
|
||||
}
|
||||
|
||||
public int read (byte[] buf, int off, int len) throws IOException {
|
||||
if (closed) {
|
||||
throw new IOException ("SSL stream is closed");
|
||||
}
|
||||
if (eof) {
|
||||
return 0;
|
||||
}
|
||||
int available=0;
|
||||
if (!needData) {
|
||||
available = bbuf.remaining();
|
||||
needData = (available==0);
|
||||
}
|
||||
if (needData) {
|
||||
bbuf.clear();
|
||||
WrapperResult r = recvData (bbuf);
|
||||
bbuf = r.buf== bbuf? bbuf: r.buf;
|
||||
if ((available=bbuf.remaining()) == 0) {
|
||||
eof = true;
|
||||
return 0;
|
||||
} else {
|
||||
needData = false;
|
||||
}
|
||||
}
|
||||
/* copy as much as possible from buf into users buf */
|
||||
if (len > available) {
|
||||
len = available;
|
||||
}
|
||||
bbuf.get (buf, off, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
public int available () throws IOException {
|
||||
return bbuf.remaining();
|
||||
}
|
||||
|
||||
public boolean markSupported () {
|
||||
return false; /* not possible with SSLEngine */
|
||||
}
|
||||
|
||||
public void reset () throws IOException {
|
||||
throw new IOException ("mark/reset not supported");
|
||||
}
|
||||
|
||||
public long skip (long s) throws IOException {
|
||||
int n = (int)s;
|
||||
if (closed) {
|
||||
throw new IOException ("SSL stream is closed");
|
||||
}
|
||||
if (eof) {
|
||||
return 0;
|
||||
}
|
||||
int ret = n;
|
||||
while (n > 0) {
|
||||
if (bbuf.remaining() >= n) {
|
||||
bbuf.position (bbuf.position()+n);
|
||||
return ret;
|
||||
} else {
|
||||
n -= bbuf.remaining();
|
||||
bbuf.clear();
|
||||
WrapperResult r = recvData (bbuf);
|
||||
bbuf = r.buf==bbuf? bbuf: r.buf;
|
||||
}
|
||||
}
|
||||
return ret; /* not reached */
|
||||
}
|
||||
|
||||
/**
|
||||
* close the SSL connection. All data must have been consumed
|
||||
* before this is called. Otherwise an exception will be thrown.
|
||||
* [Note. May need to revisit this. not quite the normal close() symantics
|
||||
*/
|
||||
public void close () throws IOException {
|
||||
eof = true;
|
||||
engine.closeInbound ();
|
||||
}
|
||||
|
||||
public int read (byte[] buf) throws IOException {
|
||||
return read (buf, 0, buf.length);
|
||||
}
|
||||
|
||||
byte single[] = new byte [1];
|
||||
|
||||
public int read () throws IOException {
|
||||
int n = read (single, 0, 1);
|
||||
if (n == 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return single[0] & 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* represents an SSL output stream. plain text data written to this stream
|
||||
* is encrypted by the stream. Multiple HTTPS responses can be sent on
|
||||
* one stream. closing this stream initiates an SSL closure
|
||||
*/
|
||||
class OutputStream extends java.io.OutputStream {
|
||||
ByteBuffer buf;
|
||||
boolean closed = false;
|
||||
byte single[] = new byte[1];
|
||||
|
||||
OutputStream() {
|
||||
buf = allocate(BufType.APPLICATION);
|
||||
}
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
single[0] = (byte)b;
|
||||
write (single, 0, 1);
|
||||
}
|
||||
|
||||
public void write(byte b[]) throws IOException {
|
||||
write (b, 0, b.length);
|
||||
}
|
||||
public void write(byte b[], int off, int len) throws IOException {
|
||||
if (closed) {
|
||||
throw new IOException ("output stream is closed");
|
||||
}
|
||||
while (len > 0) {
|
||||
int l = len > buf.capacity() ? buf.capacity() : len;
|
||||
buf.clear();
|
||||
buf.put (b, off, l);
|
||||
len -= l;
|
||||
off += l;
|
||||
buf.flip();
|
||||
WrapperResult r = sendData (buf);
|
||||
if (r.result.getStatus() == Status.CLOSED) {
|
||||
closed = true;
|
||||
if (len > 0) {
|
||||
throw new IOException ("output stream is closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
/* no-op */
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
WrapperResult r=null;
|
||||
engine.closeOutbound();
|
||||
closed = true;
|
||||
HandshakeStatus stat = HandshakeStatus.NEED_WRAP;
|
||||
buf.clear();
|
||||
while (stat == HandshakeStatus.NEED_WRAP) {
|
||||
r = wrapper.wrapAndSend (buf);
|
||||
stat = r.result.getHandshakeStatus();
|
||||
}
|
||||
assert r.result.getStatus() == Status.CLOSED;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2012, 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.lang.System.Logger;
|
||||
import java.lang.System.Logger.Level;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
/**
|
||||
* Parameters that users will not likely need to set
|
||||
* but are useful for debugging
|
||||
*/
|
||||
|
||||
class ServerConfig {
|
||||
|
||||
private static final int DEFAULT_CLOCK_TICK = 10000 ; // 10 sec.
|
||||
|
||||
/* These values must be a reasonable multiple of clockTick */
|
||||
private static final long DEFAULT_IDLE_INTERVAL = 30 ; // 5 min
|
||||
private static final int DEFAULT_MAX_IDLE_CONNECTIONS = 200 ;
|
||||
|
||||
private static final long DEFAULT_MAX_REQ_TIME = -1; // default: forever
|
||||
private static final long DEFAULT_MAX_RSP_TIME = -1; // default: forever
|
||||
private static final long DEFAULT_TIMER_MILLIS = 1000;
|
||||
private static final int DEFAULT_MAX_REQ_HEADERS = 200;
|
||||
private static final long DEFAULT_DRAIN_AMOUNT = 64 * 1024;
|
||||
|
||||
private static int clockTick;
|
||||
private static long idleInterval;
|
||||
// The maximum number of bytes to drain from an inputstream
|
||||
private static long drainAmount;
|
||||
private static int maxIdleConnections;
|
||||
// The maximum number of request headers allowable
|
||||
private static int maxReqHeaders;
|
||||
// max time a request or response is allowed to take
|
||||
private static long maxReqTime;
|
||||
private static long maxRspTime;
|
||||
private static long timerMillis;
|
||||
private static boolean debug;
|
||||
|
||||
// the value of the TCP_NODELAY socket-level option
|
||||
private static boolean noDelay;
|
||||
|
||||
static {
|
||||
java.security.AccessController.doPrivileged(
|
||||
new PrivilegedAction<Void>() {
|
||||
@Override
|
||||
public Void run () {
|
||||
idleInterval = Long.getLong("sun.net.httpserver.idleInterval",
|
||||
DEFAULT_IDLE_INTERVAL) * 1000;
|
||||
|
||||
clockTick = Integer.getInteger("sun.net.httpserver.clockTick",
|
||||
DEFAULT_CLOCK_TICK);
|
||||
|
||||
maxIdleConnections = Integer.getInteger(
|
||||
"sun.net.httpserver.maxIdleConnections",
|
||||
DEFAULT_MAX_IDLE_CONNECTIONS);
|
||||
|
||||
drainAmount = Long.getLong("sun.net.httpserver.drainAmount",
|
||||
DEFAULT_DRAIN_AMOUNT);
|
||||
|
||||
maxReqHeaders = Integer.getInteger(
|
||||
"sun.net.httpserver.maxReqHeaders",
|
||||
DEFAULT_MAX_REQ_HEADERS);
|
||||
|
||||
maxReqTime = Long.getLong("sun.net.httpserver.maxReqTime",
|
||||
DEFAULT_MAX_REQ_TIME);
|
||||
|
||||
maxRspTime = Long.getLong("sun.net.httpserver.maxRspTime",
|
||||
DEFAULT_MAX_RSP_TIME);
|
||||
|
||||
timerMillis = Long.getLong("sun.net.httpserver.timerMillis",
|
||||
DEFAULT_TIMER_MILLIS);
|
||||
|
||||
debug = Boolean.getBoolean("sun.net.httpserver.debug");
|
||||
|
||||
noDelay = Boolean.getBoolean("sun.net.httpserver.nodelay");
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
static void checkLegacyProperties(final Logger logger) {
|
||||
|
||||
// legacy properties that are no longer used
|
||||
// print a warning to logger if they are set.
|
||||
|
||||
java.security.AccessController.doPrivileged(
|
||||
new PrivilegedAction<Void>() {
|
||||
public Void run () {
|
||||
if (System.getProperty("sun.net.httpserver.readTimeout")
|
||||
!=null)
|
||||
{
|
||||
logger.log (Level.WARNING,
|
||||
"sun.net.httpserver.readTimeout "+
|
||||
"property is no longer used. "+
|
||||
"Use sun.net.httpserver.maxReqTime instead."
|
||||
);
|
||||
}
|
||||
if (System.getProperty("sun.net.httpserver.writeTimeout")
|
||||
!=null)
|
||||
{
|
||||
logger.log (Level.WARNING,
|
||||
"sun.net.httpserver.writeTimeout "+
|
||||
"property is no longer used. Use "+
|
||||
"sun.net.httpserver.maxRspTime instead."
|
||||
);
|
||||
}
|
||||
if (System.getProperty("sun.net.httpserver.selCacheTimeout")
|
||||
!=null)
|
||||
{
|
||||
logger.log (Level.WARNING,
|
||||
"sun.net.httpserver.selCacheTimeout "+
|
||||
"property is no longer used."
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
static boolean debugEnabled() {
|
||||
return debug;
|
||||
}
|
||||
|
||||
static long getIdleInterval() {
|
||||
return idleInterval;
|
||||
}
|
||||
|
||||
static int getClockTick() {
|
||||
return clockTick;
|
||||
}
|
||||
|
||||
static int getMaxIdleConnections() {
|
||||
return maxIdleConnections;
|
||||
}
|
||||
|
||||
static long getDrainAmount() {
|
||||
return drainAmount;
|
||||
}
|
||||
|
||||
static int getMaxReqHeaders() {
|
||||
return maxReqHeaders;
|
||||
}
|
||||
|
||||
static long getMaxReqTime() {
|
||||
return maxReqTime;
|
||||
}
|
||||
|
||||
static long getMaxRspTime() {
|
||||
return maxRspTime;
|
||||
}
|
||||
|
||||
static long getTimerMillis() {
|
||||
return timerMillis;
|
||||
}
|
||||
|
||||
static boolean noDelay() {
|
||||
return noDelay;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,894 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2014, 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.net.*;
|
||||
import java.io.*;
|
||||
import java.nio.channels.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.lang.System.Logger;
|
||||
import java.lang.System.Logger.Level;
|
||||
import javax.net.ssl.*;
|
||||
import com.sun.net.httpserver.*;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import sun.net.httpserver.HttpConnection.State;
|
||||
|
||||
/**
|
||||
* Provides implementation for both HTTP and HTTPS
|
||||
*/
|
||||
class ServerImpl implements TimeSource {
|
||||
|
||||
private String protocol;
|
||||
private boolean https;
|
||||
private Executor executor;
|
||||
private HttpsConfigurator httpsConfig;
|
||||
private SSLContext sslContext;
|
||||
private ContextList contexts;
|
||||
private InetSocketAddress address;
|
||||
private ServerSocketChannel schan;
|
||||
private Selector selector;
|
||||
private SelectionKey listenerKey;
|
||||
private Set<HttpConnection> idleConnections;
|
||||
private Set<HttpConnection> allConnections;
|
||||
/* following two are used to keep track of the times
|
||||
* when a connection/request is first received
|
||||
* and when we start to send the response
|
||||
*/
|
||||
private Set<HttpConnection> reqConnections;
|
||||
private Set<HttpConnection> rspConnections;
|
||||
private List<Event> events;
|
||||
private Object lolock = new Object();
|
||||
private volatile boolean finished = false;
|
||||
private volatile boolean terminating = false;
|
||||
private boolean bound = false;
|
||||
private boolean started = false;
|
||||
private volatile long time; /* current time */
|
||||
private volatile long subticks = 0;
|
||||
private volatile long ticks; /* number of clock ticks since server started */
|
||||
private HttpServer wrapper;
|
||||
|
||||
final static int CLOCK_TICK = ServerConfig.getClockTick();
|
||||
final static long IDLE_INTERVAL = ServerConfig.getIdleInterval();
|
||||
final static int MAX_IDLE_CONNECTIONS = ServerConfig.getMaxIdleConnections();
|
||||
final static long TIMER_MILLIS = ServerConfig.getTimerMillis ();
|
||||
final static long MAX_REQ_TIME=getTimeMillis(ServerConfig.getMaxReqTime());
|
||||
final static long MAX_RSP_TIME=getTimeMillis(ServerConfig.getMaxRspTime());
|
||||
final static boolean timer1Enabled = MAX_REQ_TIME != -1 || MAX_RSP_TIME != -1;
|
||||
|
||||
private Timer timer, timer1;
|
||||
private final Logger logger;
|
||||
private Thread dispatcherThread;
|
||||
|
||||
ServerImpl (
|
||||
HttpServer wrapper, String protocol, InetSocketAddress addr, int backlog
|
||||
) throws IOException {
|
||||
|
||||
this.protocol = protocol;
|
||||
this.wrapper = wrapper;
|
||||
this.logger = System.getLogger ("com.sun.net.httpserver");
|
||||
ServerConfig.checkLegacyProperties (logger);
|
||||
https = protocol.equalsIgnoreCase ("https");
|
||||
this.address = addr;
|
||||
contexts = new ContextList();
|
||||
schan = ServerSocketChannel.open();
|
||||
if (addr != null) {
|
||||
ServerSocket socket = schan.socket();
|
||||
socket.bind (addr, backlog);
|
||||
bound = true;
|
||||
}
|
||||
selector = Selector.open ();
|
||||
schan.configureBlocking (false);
|
||||
listenerKey = schan.register (selector, SelectionKey.OP_ACCEPT);
|
||||
dispatcher = new Dispatcher();
|
||||
idleConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
|
||||
allConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
|
||||
reqConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
|
||||
rspConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
|
||||
time = System.currentTimeMillis();
|
||||
timer = new Timer ("server-timer", true);
|
||||
timer.schedule (new ServerTimerTask(), CLOCK_TICK, CLOCK_TICK);
|
||||
if (timer1Enabled) {
|
||||
timer1 = new Timer ("server-timer1", true);
|
||||
timer1.schedule (new ServerTimerTask1(),TIMER_MILLIS,TIMER_MILLIS);
|
||||
logger.log (Level.DEBUG, "HttpServer timer1 enabled period in ms: ", TIMER_MILLIS);
|
||||
logger.log (Level.DEBUG, "MAX_REQ_TIME: "+MAX_REQ_TIME);
|
||||
logger.log (Level.DEBUG, "MAX_RSP_TIME: "+MAX_RSP_TIME);
|
||||
}
|
||||
events = new LinkedList<Event>();
|
||||
logger.log (Level.DEBUG, "HttpServer created "+protocol+" "+ addr);
|
||||
}
|
||||
|
||||
public void bind (InetSocketAddress addr, int backlog) throws IOException {
|
||||
if (bound) {
|
||||
throw new BindException ("HttpServer already bound");
|
||||
}
|
||||
if (addr == null) {
|
||||
throw new NullPointerException ("null address");
|
||||
}
|
||||
ServerSocket socket = schan.socket();
|
||||
socket.bind (addr, backlog);
|
||||
bound = true;
|
||||
}
|
||||
|
||||
public void start () {
|
||||
if (!bound || started || finished) {
|
||||
throw new IllegalStateException ("server in wrong state");
|
||||
}
|
||||
if (executor == null) {
|
||||
executor = new DefaultExecutor();
|
||||
}
|
||||
dispatcherThread = new Thread(null, dispatcher, "HTTP-Dispatcher", 0, false);
|
||||
started = true;
|
||||
dispatcherThread.start();
|
||||
}
|
||||
|
||||
public void setExecutor (Executor executor) {
|
||||
if (started) {
|
||||
throw new IllegalStateException ("server already started");
|
||||
}
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
private static class DefaultExecutor implements Executor {
|
||||
public void execute (Runnable task) {
|
||||
task.run();
|
||||
}
|
||||
}
|
||||
|
||||
public Executor getExecutor () {
|
||||
return executor;
|
||||
}
|
||||
|
||||
public void setHttpsConfigurator (HttpsConfigurator config) {
|
||||
if (config == null) {
|
||||
throw new NullPointerException ("null HttpsConfigurator");
|
||||
}
|
||||
if (started) {
|
||||
throw new IllegalStateException ("server already started");
|
||||
}
|
||||
this.httpsConfig = config;
|
||||
sslContext = config.getSSLContext();
|
||||
}
|
||||
|
||||
public HttpsConfigurator getHttpsConfigurator () {
|
||||
return httpsConfig;
|
||||
}
|
||||
|
||||
public final boolean isFinishing() {
|
||||
return finished;
|
||||
}
|
||||
|
||||
public void stop (int delay) {
|
||||
if (delay < 0) {
|
||||
throw new IllegalArgumentException ("negative delay parameter");
|
||||
}
|
||||
terminating = true;
|
||||
try { schan.close(); } catch (IOException e) {}
|
||||
selector.wakeup();
|
||||
long latest = System.currentTimeMillis() + delay * 1000;
|
||||
while (System.currentTimeMillis() < latest) {
|
||||
delay();
|
||||
if (finished) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
finished = true;
|
||||
selector.wakeup();
|
||||
synchronized (allConnections) {
|
||||
for (HttpConnection c : allConnections) {
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
allConnections.clear();
|
||||
idleConnections.clear();
|
||||
timer.cancel();
|
||||
if (timer1Enabled) {
|
||||
timer1.cancel();
|
||||
}
|
||||
if (dispatcherThread != null) {
|
||||
try {
|
||||
dispatcherThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
logger.log (Level.TRACE, "ServerImpl.stop: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Dispatcher dispatcher;
|
||||
|
||||
public synchronized HttpContextImpl createContext (String path, HttpHandler handler) {
|
||||
if (handler == null || path == null) {
|
||||
throw new NullPointerException ("null handler, or path parameter");
|
||||
}
|
||||
HttpContextImpl context = new HttpContextImpl (protocol, path, handler, this);
|
||||
contexts.add (context);
|
||||
logger.log (Level.DEBUG, "context created: " + path);
|
||||
return context;
|
||||
}
|
||||
|
||||
public synchronized HttpContextImpl createContext (String path) {
|
||||
if (path == null) {
|
||||
throw new NullPointerException ("null path parameter");
|
||||
}
|
||||
HttpContextImpl context = new HttpContextImpl (protocol, path, null, this);
|
||||
contexts.add (context);
|
||||
logger.log (Level.DEBUG, "context created: " + path);
|
||||
return context;
|
||||
}
|
||||
|
||||
public synchronized void removeContext (String path) throws IllegalArgumentException {
|
||||
if (path == null) {
|
||||
throw new NullPointerException ("null path parameter");
|
||||
}
|
||||
contexts.remove (protocol, path);
|
||||
logger.log (Level.DEBUG, "context removed: " + path);
|
||||
}
|
||||
|
||||
public synchronized void removeContext (HttpContext context) throws IllegalArgumentException {
|
||||
if (!(context instanceof HttpContextImpl)) {
|
||||
throw new IllegalArgumentException ("wrong HttpContext type");
|
||||
}
|
||||
contexts.remove ((HttpContextImpl)context);
|
||||
logger.log (Level.DEBUG, "context removed: " + context.getPath());
|
||||
}
|
||||
|
||||
public InetSocketAddress getAddress() {
|
||||
return AccessController.doPrivileged(
|
||||
new PrivilegedAction<InetSocketAddress>() {
|
||||
public InetSocketAddress run() {
|
||||
return
|
||||
(InetSocketAddress)schan.socket()
|
||||
.getLocalSocketAddress();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Selector getSelector () {
|
||||
return selector;
|
||||
}
|
||||
|
||||
void addEvent (Event r) {
|
||||
synchronized (lolock) {
|
||||
events.add (r);
|
||||
selector.wakeup();
|
||||
}
|
||||
}
|
||||
|
||||
/* main server listener task */
|
||||
|
||||
class Dispatcher implements Runnable {
|
||||
|
||||
private void handleEvent (Event r) {
|
||||
ExchangeImpl t = r.exchange;
|
||||
HttpConnection c = t.getConnection();
|
||||
try {
|
||||
if (r instanceof WriteFinishedEvent) {
|
||||
|
||||
int exchanges = endExchange();
|
||||
if (terminating && exchanges == 0) {
|
||||
finished = true;
|
||||
}
|
||||
responseCompleted (c);
|
||||
LeftOverInputStream is = t.getOriginalInputStream();
|
||||
if (!is.isEOF()) {
|
||||
t.close = true;
|
||||
}
|
||||
if (t.close || idleConnections.size() >= MAX_IDLE_CONNECTIONS) {
|
||||
c.close();
|
||||
allConnections.remove (c);
|
||||
} else {
|
||||
if (is.isDataBuffered()) {
|
||||
/* don't re-enable the interestops, just handle it */
|
||||
requestStarted (c);
|
||||
handle (c.getChannel(), c);
|
||||
} else {
|
||||
connsToRegister.add (c);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.log (
|
||||
Level.TRACE, "Dispatcher (1)", e
|
||||
);
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
final LinkedList<HttpConnection> connsToRegister =
|
||||
new LinkedList<HttpConnection>();
|
||||
|
||||
void reRegister (HttpConnection c) {
|
||||
/* re-register with selector */
|
||||
try {
|
||||
SocketChannel chan = c.getChannel();
|
||||
chan.configureBlocking (false);
|
||||
SelectionKey key = chan.register (selector, SelectionKey.OP_READ);
|
||||
key.attach (c);
|
||||
c.selectionKey = key;
|
||||
c.time = getTime() + IDLE_INTERVAL;
|
||||
idleConnections.add (c);
|
||||
} catch (IOException e) {
|
||||
dprint(e);
|
||||
logger.log (Level.TRACE, "Dispatcher(8)", e);
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while (!finished) {
|
||||
try {
|
||||
List<Event> list = null;
|
||||
synchronized (lolock) {
|
||||
if (events.size() > 0) {
|
||||
list = events;
|
||||
events = new LinkedList<Event>();
|
||||
}
|
||||
}
|
||||
|
||||
if (list != null) {
|
||||
for (Event r: list) {
|
||||
handleEvent (r);
|
||||
}
|
||||
}
|
||||
|
||||
for (HttpConnection c : connsToRegister) {
|
||||
reRegister(c);
|
||||
}
|
||||
connsToRegister.clear();
|
||||
|
||||
selector.select(1000);
|
||||
|
||||
/* process the selected list now */
|
||||
Set<SelectionKey> selected = selector.selectedKeys();
|
||||
Iterator<SelectionKey> iter = selected.iterator();
|
||||
while (iter.hasNext()) {
|
||||
SelectionKey key = iter.next();
|
||||
iter.remove ();
|
||||
if (key.equals (listenerKey)) {
|
||||
if (terminating) {
|
||||
continue;
|
||||
}
|
||||
SocketChannel chan = schan.accept();
|
||||
|
||||
// optimist there's a channel
|
||||
if (chan != null) {
|
||||
// Set TCP_NODELAY, if appropriate
|
||||
if (ServerConfig.noDelay()) {
|
||||
chan.socket().setTcpNoDelay(true);
|
||||
}
|
||||
chan.configureBlocking (false);
|
||||
SelectionKey newkey =
|
||||
chan.register (selector, SelectionKey.OP_READ);
|
||||
HttpConnection c = new HttpConnection ();
|
||||
c.selectionKey = newkey;
|
||||
c.setChannel (chan);
|
||||
newkey.attach (c);
|
||||
requestStarted (c);
|
||||
allConnections.add (c);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
if (key.isReadable()) {
|
||||
boolean closed;
|
||||
SocketChannel chan = (SocketChannel)key.channel();
|
||||
HttpConnection conn = (HttpConnection)key.attachment();
|
||||
|
||||
key.cancel();
|
||||
chan.configureBlocking (true);
|
||||
if (idleConnections.remove(conn)) {
|
||||
// was an idle connection so add it
|
||||
// to reqConnections set.
|
||||
requestStarted (conn);
|
||||
}
|
||||
handle (chan, conn);
|
||||
} else {
|
||||
assert false;
|
||||
}
|
||||
} catch (CancelledKeyException e) {
|
||||
handleException(key, null);
|
||||
} catch (IOException e) {
|
||||
handleException(key, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
// call the selector just to process the cancelled keys
|
||||
selector.selectNow();
|
||||
} catch (IOException e) {
|
||||
logger.log (Level.TRACE, "Dispatcher (4)", e);
|
||||
} catch (Exception e) {
|
||||
logger.log (Level.TRACE, "Dispatcher (7)", e);
|
||||
}
|
||||
}
|
||||
try {selector.close(); } catch (Exception e) {}
|
||||
}
|
||||
|
||||
private void handleException (SelectionKey key, Exception e) {
|
||||
HttpConnection conn = (HttpConnection)key.attachment();
|
||||
if (e != null) {
|
||||
logger.log (Level.TRACE, "Dispatcher (2)", e);
|
||||
}
|
||||
closeConnection(conn);
|
||||
}
|
||||
|
||||
public void handle (SocketChannel chan, HttpConnection conn)
|
||||
throws IOException
|
||||
{
|
||||
try {
|
||||
Exchange t = new Exchange (chan, protocol, conn);
|
||||
executor.execute (t);
|
||||
} catch (HttpError e1) {
|
||||
logger.log (Level.TRACE, "Dispatcher (4)", e1);
|
||||
closeConnection(conn);
|
||||
} catch (IOException e) {
|
||||
logger.log (Level.TRACE, "Dispatcher (5)", e);
|
||||
closeConnection(conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static boolean debug = ServerConfig.debugEnabled ();
|
||||
|
||||
static synchronized void dprint (String s) {
|
||||
if (debug) {
|
||||
System.out.println (s);
|
||||
}
|
||||
}
|
||||
|
||||
static synchronized void dprint (Exception e) {
|
||||
if (debug) {
|
||||
System.out.println (e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
Logger getLogger () {
|
||||
return logger;
|
||||
}
|
||||
|
||||
private void closeConnection(HttpConnection conn) {
|
||||
conn.close();
|
||||
allConnections.remove(conn);
|
||||
switch (conn.getState()) {
|
||||
case REQUEST:
|
||||
reqConnections.remove(conn);
|
||||
break;
|
||||
case RESPONSE:
|
||||
rspConnections.remove(conn);
|
||||
break;
|
||||
case IDLE:
|
||||
idleConnections.remove(conn);
|
||||
break;
|
||||
}
|
||||
assert !reqConnections.remove(conn);
|
||||
assert !rspConnections.remove(conn);
|
||||
assert !idleConnections.remove(conn);
|
||||
}
|
||||
|
||||
/* per exchange task */
|
||||
|
||||
class Exchange implements Runnable {
|
||||
SocketChannel chan;
|
||||
HttpConnection connection;
|
||||
HttpContextImpl context;
|
||||
InputStream rawin;
|
||||
OutputStream rawout;
|
||||
String protocol;
|
||||
ExchangeImpl tx;
|
||||
HttpContextImpl ctx;
|
||||
boolean rejected = false;
|
||||
|
||||
Exchange (SocketChannel chan, String protocol, HttpConnection conn) throws IOException {
|
||||
this.chan = chan;
|
||||
this.connection = conn;
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public void run () {
|
||||
/* context will be null for new connections */
|
||||
context = connection.getHttpContext();
|
||||
boolean newconnection;
|
||||
SSLEngine engine = null;
|
||||
String requestLine = null;
|
||||
SSLStreams sslStreams = null;
|
||||
try {
|
||||
if (context != null ) {
|
||||
this.rawin = connection.getInputStream();
|
||||
this.rawout = connection.getRawOutputStream();
|
||||
newconnection = false;
|
||||
} else {
|
||||
/* figure out what kind of connection this is */
|
||||
newconnection = true;
|
||||
if (https) {
|
||||
if (sslContext == null) {
|
||||
logger.log (Level.WARNING,
|
||||
"SSL connection received. No https contxt created");
|
||||
throw new HttpError ("No SSL context established");
|
||||
}
|
||||
sslStreams = new SSLStreams (ServerImpl.this, sslContext, chan);
|
||||
rawin = sslStreams.getInputStream();
|
||||
rawout = sslStreams.getOutputStream();
|
||||
engine = sslStreams.getSSLEngine();
|
||||
connection.sslStreams = sslStreams;
|
||||
} else {
|
||||
rawin = new BufferedInputStream(
|
||||
new Request.ReadStream (
|
||||
ServerImpl.this, chan
|
||||
));
|
||||
rawout = new Request.WriteStream (
|
||||
ServerImpl.this, chan
|
||||
);
|
||||
}
|
||||
connection.raw = rawin;
|
||||
connection.rawout = rawout;
|
||||
}
|
||||
Request req = new Request (rawin, rawout);
|
||||
requestLine = req.requestLine();
|
||||
if (requestLine == null) {
|
||||
/* connection closed */
|
||||
closeConnection(connection);
|
||||
return;
|
||||
}
|
||||
int space = requestLine.indexOf (' ');
|
||||
if (space == -1) {
|
||||
reject (Code.HTTP_BAD_REQUEST,
|
||||
requestLine, "Bad request line");
|
||||
return;
|
||||
}
|
||||
String method = requestLine.substring (0, space);
|
||||
int start = space+1;
|
||||
space = requestLine.indexOf(' ', start);
|
||||
if (space == -1) {
|
||||
reject (Code.HTTP_BAD_REQUEST,
|
||||
requestLine, "Bad request line");
|
||||
return;
|
||||
}
|
||||
String uriStr = requestLine.substring (start, space);
|
||||
URI uri = new URI (uriStr);
|
||||
start = space+1;
|
||||
String version = requestLine.substring (start);
|
||||
Headers headers = req.headers();
|
||||
String s = headers.getFirst ("Transfer-encoding");
|
||||
long clen = 0L;
|
||||
if (s !=null && s.equalsIgnoreCase ("chunked")) {
|
||||
clen = -1L;
|
||||
} else {
|
||||
s = headers.getFirst ("Content-Length");
|
||||
if (s != null) {
|
||||
clen = Long.parseLong(s);
|
||||
}
|
||||
if (clen == 0) {
|
||||
requestCompleted (connection);
|
||||
}
|
||||
}
|
||||
ctx = contexts.findContext (protocol, uri.getPath());
|
||||
if (ctx == null) {
|
||||
reject (Code.HTTP_NOT_FOUND,
|
||||
requestLine, "No context found for request");
|
||||
return;
|
||||
}
|
||||
connection.setContext (ctx);
|
||||
if (ctx.getHandler() == null) {
|
||||
reject (Code.HTTP_INTERNAL_ERROR,
|
||||
requestLine, "No handler for context");
|
||||
return;
|
||||
}
|
||||
tx = new ExchangeImpl (
|
||||
method, uri, req, clen, connection
|
||||
);
|
||||
String chdr = headers.getFirst("Connection");
|
||||
Headers rheaders = tx.getResponseHeaders();
|
||||
|
||||
if (chdr != null && chdr.equalsIgnoreCase ("close")) {
|
||||
tx.close = true;
|
||||
}
|
||||
if (version.equalsIgnoreCase ("http/1.0")) {
|
||||
tx.http10 = true;
|
||||
if (chdr == null) {
|
||||
tx.close = true;
|
||||
rheaders.set ("Connection", "close");
|
||||
} else if (chdr.equalsIgnoreCase ("keep-alive")) {
|
||||
rheaders.set ("Connection", "keep-alive");
|
||||
int idle=(int)(ServerConfig.getIdleInterval()/1000);
|
||||
int max=ServerConfig.getMaxIdleConnections();
|
||||
String val = "timeout="+idle+", max="+max;
|
||||
rheaders.set ("Keep-Alive", val);
|
||||
}
|
||||
}
|
||||
|
||||
if (newconnection) {
|
||||
connection.setParameters (
|
||||
rawin, rawout, chan, engine, sslStreams,
|
||||
sslContext, protocol, ctx, rawin
|
||||
);
|
||||
}
|
||||
/* check if client sent an Expect 100 Continue.
|
||||
* In that case, need to send an interim response.
|
||||
* In future API may be modified to allow app to
|
||||
* be involved in this process.
|
||||
*/
|
||||
String exp = headers.getFirst("Expect");
|
||||
if (exp != null && exp.equalsIgnoreCase ("100-continue")) {
|
||||
logReply (100, requestLine, null);
|
||||
sendReply (
|
||||
Code.HTTP_CONTINUE, false, null
|
||||
);
|
||||
}
|
||||
/* uf is the list of filters seen/set by the user.
|
||||
* sf is the list of filters established internally
|
||||
* and which are not visible to the user. uc and sc
|
||||
* are the corresponding Filter.Chains.
|
||||
* They are linked together by a LinkHandler
|
||||
* so that they can both be invoked in one call.
|
||||
*/
|
||||
List<Filter> sf = ctx.getSystemFilters();
|
||||
List<Filter> uf = ctx.getFilters();
|
||||
|
||||
Filter.Chain sc = new Filter.Chain(sf, ctx.getHandler());
|
||||
Filter.Chain uc = new Filter.Chain(uf, new LinkHandler (sc));
|
||||
|
||||
/* set up the two stream references */
|
||||
tx.getRequestBody();
|
||||
tx.getResponseBody();
|
||||
if (https) {
|
||||
uc.doFilter (new HttpsExchangeImpl (tx));
|
||||
} else {
|
||||
uc.doFilter (new HttpExchangeImpl (tx));
|
||||
}
|
||||
|
||||
} catch (IOException e1) {
|
||||
logger.log (Level.TRACE, "ServerImpl.Exchange (1)", e1);
|
||||
closeConnection(connection);
|
||||
} catch (NumberFormatException e3) {
|
||||
reject (Code.HTTP_BAD_REQUEST,
|
||||
requestLine, "NumberFormatException thrown");
|
||||
} catch (URISyntaxException e) {
|
||||
reject (Code.HTTP_BAD_REQUEST,
|
||||
requestLine, "URISyntaxException thrown");
|
||||
} catch (Exception e4) {
|
||||
logger.log (Level.TRACE, "ServerImpl.Exchange (2)", e4);
|
||||
closeConnection(connection);
|
||||
}
|
||||
}
|
||||
|
||||
/* used to link to 2 or more Filter.Chains together */
|
||||
|
||||
class LinkHandler implements HttpHandler {
|
||||
Filter.Chain nextChain;
|
||||
|
||||
LinkHandler (Filter.Chain nextChain) {
|
||||
this.nextChain = nextChain;
|
||||
}
|
||||
|
||||
public void handle (HttpExchange exchange) throws IOException {
|
||||
nextChain.doFilter (exchange);
|
||||
}
|
||||
}
|
||||
|
||||
void reject (int code, String requestStr, String message) {
|
||||
rejected = true;
|
||||
logReply (code, requestStr, message);
|
||||
sendReply (
|
||||
code, false, "<h1>"+code+Code.msg(code)+"</h1>"+message
|
||||
);
|
||||
closeConnection(connection);
|
||||
}
|
||||
|
||||
void sendReply (
|
||||
int code, boolean closeNow, String text)
|
||||
{
|
||||
try {
|
||||
StringBuilder builder = new StringBuilder (512);
|
||||
builder.append ("HTTP/1.1 ")
|
||||
.append (code).append (Code.msg(code)).append ("\r\n");
|
||||
|
||||
if (text != null && text.length() != 0) {
|
||||
builder.append ("Content-Length: ")
|
||||
.append (text.length()).append ("\r\n")
|
||||
.append ("Content-Type: text/html\r\n");
|
||||
} else {
|
||||
builder.append ("Content-Length: 0\r\n");
|
||||
text = "";
|
||||
}
|
||||
if (closeNow) {
|
||||
builder.append ("Connection: close\r\n");
|
||||
}
|
||||
builder.append ("\r\n").append (text);
|
||||
String s = builder.toString();
|
||||
byte[] b = s.getBytes("ISO8859_1");
|
||||
rawout.write (b);
|
||||
rawout.flush();
|
||||
if (closeNow) {
|
||||
closeConnection(connection);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.log (Level.TRACE, "ServerImpl.sendReply", e);
|
||||
closeConnection(connection);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void logReply (int code, String requestStr, String text) {
|
||||
if (!logger.isLoggable(Level.DEBUG)) {
|
||||
return;
|
||||
}
|
||||
if (text == null) {
|
||||
text = "";
|
||||
}
|
||||
String r;
|
||||
if (requestStr.length() > 80) {
|
||||
r = requestStr.substring (0, 80) + "<TRUNCATED>";
|
||||
} else {
|
||||
r = requestStr;
|
||||
}
|
||||
String message = r + " [" + code + " " +
|
||||
Code.msg(code) + "] ("+text+")";
|
||||
logger.log (Level.DEBUG, message);
|
||||
}
|
||||
|
||||
long getTicks() {
|
||||
return ticks;
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
void delay () {
|
||||
Thread.yield();
|
||||
try {
|
||||
Thread.sleep (200);
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
|
||||
private int exchangeCount = 0;
|
||||
|
||||
synchronized void startExchange () {
|
||||
exchangeCount ++;
|
||||
}
|
||||
|
||||
synchronized int endExchange () {
|
||||
exchangeCount --;
|
||||
assert exchangeCount >= 0;
|
||||
return exchangeCount;
|
||||
}
|
||||
|
||||
HttpServer getWrapper () {
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
void requestStarted (HttpConnection c) {
|
||||
c.creationTime = getTime();
|
||||
c.setState (State.REQUEST);
|
||||
reqConnections.add (c);
|
||||
}
|
||||
|
||||
// called after a request has been completely read
|
||||
// by the server. This stops the timer which would
|
||||
// close the connection if the request doesn't arrive
|
||||
// quickly enough. It then starts the timer
|
||||
// that ensures the client reads the response in a timely
|
||||
// fashion.
|
||||
|
||||
void requestCompleted (HttpConnection c) {
|
||||
assert c.getState() == State.REQUEST;
|
||||
reqConnections.remove (c);
|
||||
c.rspStartedTime = getTime();
|
||||
rspConnections.add (c);
|
||||
c.setState (State.RESPONSE);
|
||||
}
|
||||
|
||||
// called after response has been sent
|
||||
void responseCompleted (HttpConnection c) {
|
||||
assert c.getState() == State.RESPONSE;
|
||||
rspConnections.remove (c);
|
||||
c.setState (State.IDLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* TimerTask run every CLOCK_TICK ms
|
||||
*/
|
||||
class ServerTimerTask extends TimerTask {
|
||||
public void run () {
|
||||
LinkedList<HttpConnection> toClose = new LinkedList<HttpConnection>();
|
||||
time = System.currentTimeMillis();
|
||||
ticks ++;
|
||||
synchronized (idleConnections) {
|
||||
for (HttpConnection c : idleConnections) {
|
||||
if (c.time <= time) {
|
||||
toClose.add (c);
|
||||
}
|
||||
}
|
||||
for (HttpConnection c : toClose) {
|
||||
idleConnections.remove (c);
|
||||
allConnections.remove (c);
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ServerTimerTask1 extends TimerTask {
|
||||
|
||||
// runs every TIMER_MILLIS
|
||||
public void run () {
|
||||
LinkedList<HttpConnection> toClose = new LinkedList<HttpConnection>();
|
||||
time = System.currentTimeMillis();
|
||||
synchronized (reqConnections) {
|
||||
if (MAX_REQ_TIME != -1) {
|
||||
for (HttpConnection c : reqConnections) {
|
||||
if (c.creationTime + TIMER_MILLIS + MAX_REQ_TIME <= time) {
|
||||
toClose.add (c);
|
||||
}
|
||||
}
|
||||
for (HttpConnection c : toClose) {
|
||||
logger.log (Level.DEBUG, "closing: no request: " + c);
|
||||
reqConnections.remove (c);
|
||||
allConnections.remove (c);
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
toClose = new LinkedList<HttpConnection>();
|
||||
synchronized (rspConnections) {
|
||||
if (MAX_RSP_TIME != -1) {
|
||||
for (HttpConnection c : rspConnections) {
|
||||
if (c.rspStartedTime + TIMER_MILLIS +MAX_RSP_TIME <= time) {
|
||||
toClose.add (c);
|
||||
}
|
||||
}
|
||||
for (HttpConnection c : toClose) {
|
||||
logger.log (Level.DEBUG, "closing: no response: " + c);
|
||||
rspConnections.remove (c);
|
||||
allConnections.remove (c);
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void logStackTrace (String s) {
|
||||
logger.log (Level.TRACE, s);
|
||||
StringBuilder b = new StringBuilder ();
|
||||
StackTraceElement[] e = Thread.currentThread().getStackTrace();
|
||||
for (int i=0; i<e.length; i++) {
|
||||
b.append (e[i].toString()).append("\n");
|
||||
}
|
||||
logger.log (Level.TRACE, b.toString());
|
||||
}
|
||||
|
||||
static long getTimeMillis(long secs) {
|
||||
if (secs == -1) {
|
||||
return -1;
|
||||
} else {
|
||||
return secs * 1000;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.io.*;
|
||||
|
||||
class StreamClosedException extends IOException {
|
||||
private static final long serialVersionUID = -4485921499356327937L;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 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;
|
||||
|
||||
interface TimeSource {
|
||||
public long getTime();
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (c) 2007, 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.io.*;
|
||||
import java.net.*;
|
||||
import com.sun.net.httpserver.*;
|
||||
import com.sun.net.httpserver.spi.*;
|
||||
|
||||
/**
|
||||
* a class which allows the caller to write an indefinite
|
||||
* number of bytes to an underlying stream , but without using
|
||||
* chunked encoding. Used for http/1.0 clients only
|
||||
* The underlying connection needs to be closed afterwards.
|
||||
*/
|
||||
|
||||
class UndefLengthOutputStream extends FilterOutputStream
|
||||
{
|
||||
private boolean closed = false;
|
||||
ExchangeImpl t;
|
||||
|
||||
UndefLengthOutputStream (ExchangeImpl t, OutputStream src) {
|
||||
super (src);
|
||||
this.t = t;
|
||||
}
|
||||
|
||||
public void write (int b) throws IOException {
|
||||
if (closed) {
|
||||
throw new IOException ("stream closed");
|
||||
}
|
||||
out.write(b);
|
||||
}
|
||||
|
||||
public void write (byte[]b, int off, int len) throws IOException {
|
||||
if (closed) {
|
||||
throw new IOException ("stream closed");
|
||||
}
|
||||
out.write(b, off, len);
|
||||
}
|
||||
|
||||
public void close () throws IOException {
|
||||
if (closed) {
|
||||
return;
|
||||
}
|
||||
closed = true;
|
||||
flush();
|
||||
LeftOverInputStream is = t.getOriginalInputStream();
|
||||
if (!is.isClosed()) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
WriteFinishedEvent e = new WriteFinishedEvent (t);
|
||||
t.getHttpContext().getServerImpl().addEvent (e);
|
||||
}
|
||||
|
||||
// flush is a pass-through
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 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 com.sun.net.httpserver.*;
|
||||
|
||||
class UnmodifiableHeaders extends Headers {
|
||||
|
||||
Headers map;
|
||||
|
||||
UnmodifiableHeaders(Headers map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public int size() {return map.size();}
|
||||
|
||||
public boolean isEmpty() {return map.isEmpty();}
|
||||
|
||||
public boolean containsKey(Object key) {
|
||||
return map.containsKey (key);
|
||||
}
|
||||
|
||||
public boolean containsValue(Object value) {
|
||||
return map.containsValue(value);
|
||||
}
|
||||
|
||||
public List<String> get(Object key) {
|
||||
return map.get(key);
|
||||
}
|
||||
|
||||
public String getFirst (String key) {
|
||||
return map.getFirst(key);
|
||||
}
|
||||
|
||||
|
||||
public List<String> put(String key, List<String> value) {
|
||||
return map.put (key, value);
|
||||
}
|
||||
|
||||
public void add (String key, String value) {
|
||||
throw new UnsupportedOperationException ("unsupported operation");
|
||||
}
|
||||
|
||||
public void set (String key, String value) {
|
||||
throw new UnsupportedOperationException ("unsupported operation");
|
||||
}
|
||||
|
||||
public List<String> remove(Object key) {
|
||||
throw new UnsupportedOperationException ("unsupported operation");
|
||||
}
|
||||
|
||||
public void putAll(Map<? extends String,? extends List<String>> t) {
|
||||
throw new UnsupportedOperationException ("unsupported operation");
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException ("unsupported operation");
|
||||
}
|
||||
|
||||
public Set<String> keySet() {
|
||||
return Collections.unmodifiableSet (map.keySet());
|
||||
}
|
||||
|
||||
public Collection<List<String>> values() {
|
||||
return Collections.unmodifiableCollection(map.values());
|
||||
}
|
||||
|
||||
/* TODO check that contents of set are not modifable : security */
|
||||
|
||||
public Set<Map.Entry<String, List<String>>> entrySet() {
|
||||
return Collections.unmodifiableSet (map.entrySet());
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {return map.equals(o);}
|
||||
|
||||
public int hashCode() {return map.hashCode();}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2005, 2012, 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;
|
||||
|
||||
class WriteFinishedEvent extends Event {
|
||||
WriteFinishedEvent (ExchangeImpl t) {
|
||||
super (t);
|
||||
assert !t.writefinished;
|
||||
t.writefinished = true;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue