Showing the changes to java/pkg/sun/net/httpserver (27 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,86 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import com.sun.net.httpserver.*; | |
| 29 | +import java.io.*; | |
| 30 | +import java.nio.*; | |
| 31 | +import java.nio.channels.*; | |
| 32 | +import sun.net.www.MessageHeader; | |
| 33 | +import java.util.*; | |
| 34 | +import javax.security.auth.*; | |
| 35 | +import javax.security.auth.callback.*; | |
| 36 | +import javax.security.auth.login.*; | |
| 37 | + | |
| 38 | +public class AuthFilter extends Filter { | |
| 39 | + | |
| 40 | + private Authenticator authenticator; | |
| 41 | + | |
| 42 | + public AuthFilter (Authenticator authenticator) { | |
| 43 | + this.authenticator = authenticator; | |
| 44 | + } | |
| 45 | + | |
| 46 | + public String description () { | |
| 47 | + return "Authentication filter"; | |
| 48 | + } | |
| 49 | + | |
| 50 | + public void setAuthenticator (Authenticator a) { | |
| 51 | + authenticator = a; | |
| 52 | + } | |
| 53 | + | |
| 54 | + public void consumeInput (HttpExchange t) throws IOException { | |
| 55 | + InputStream i = t.getRequestBody(); | |
| 56 | + byte[] b = new byte [4096]; | |
| 57 | + while (i.read (b) != -1); | |
| 58 | + i.close (); | |
| 59 | + } | |
| 60 | + | |
| 61 | + /** | |
| 62 | + * The filter's implementation, which is invoked by the server | |
| 63 | + */ | |
| 64 | + public void doFilter (HttpExchange t, Filter.Chain chain) throws IOException | |
| 65 | + { | |
| 66 | + if (authenticator != null) { | |
| 67 | + Authenticator.Result r = authenticator.authenticate (t); | |
| 68 | + if (r instanceof Authenticator.Success) { | |
| 69 | + Authenticator.Success s = (Authenticator.Success)r; | |
| 70 | + ExchangeImpl e = ExchangeImpl.get (t); | |
| 71 | + e.setPrincipal (s.getPrincipal()); | |
| 72 | + chain.doFilter (t); | |
| 73 | + } else if (r instanceof Authenticator.Retry) { | |
| 74 | + Authenticator.Retry ry = (Authenticator.Retry)r; | |
| 75 | + consumeInput (t); | |
| 76 | + t.sendResponseHeaders (ry.getResponseCode(), -1); | |
| 77 | + } else if (r instanceof Authenticator.Failure) { | |
| 78 | + Authenticator.Failure f = (Authenticator.Failure)r; | |
| 79 | + consumeInput (t); | |
| 80 | + t.sendResponseHeaders (f.getResponseCode(), -1); | |
| 81 | + } | |
| 82 | + } else { | |
| 83 | + chain.doFilter (t); | |
| 84 | + } | |
| 85 | + } | |
| 86 | +} | |
| @@ -0,0 +1,176 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import java.io.*; | |
| 29 | +import java.net.*; | |
| 30 | +import com.sun.net.httpserver.*; | |
| 31 | +import com.sun.net.httpserver.spi.*; | |
| 32 | + | |
| 33 | +class ChunkedInputStream extends LeftOverInputStream { | |
| 34 | + ChunkedInputStream (ExchangeImpl t, InputStream src) { | |
| 35 | + super (t, src); | |
| 36 | + } | |
| 37 | + | |
| 38 | + private int remaining; | |
| 39 | + | |
| 40 | + /* true when a chunk header needs to be read */ | |
| 41 | + | |
| 42 | + private boolean needToReadHeader = true; | |
| 43 | + | |
| 44 | + static char CR = '\r'; | |
| 45 | + static char LF = '\n'; | |
| 46 | + | |
| 47 | + private int numeric (char[] arr, int nchars) throws IOException { | |
| 48 | + assert arr.length >= nchars; | |
| 49 | + int len = 0; | |
| 50 | + for (int i=0; i<nchars; i++) { | |
| 51 | + char c = arr[i]; | |
| 52 | + int val=0; | |
| 53 | + if (c>='0' && c <='9') { | |
| 54 | + val = c - '0'; | |
| 55 | + } else if (c>='a' && c<= 'f') { | |
| 56 | + val = c - 'a' + 10; | |
| 57 | + } else if (c>='A' && c<= 'F') { | |
| 58 | + val = c - 'A' + 10; | |
| 59 | + } else { | |
| 60 | + throw new IOException ("invalid chunk length"); | |
| 61 | + } | |
| 62 | + len = len * 16 + val; | |
| 63 | + } | |
| 64 | + return len; | |
| 65 | + } | |
| 66 | + | |
| 67 | + /* read the chunk header line and return the chunk length | |
| 68 | + * any chunk extensions are ignored | |
| 69 | + */ | |
| 70 | + private int readChunkHeader () throws IOException { | |
| 71 | + boolean gotCR = false; | |
| 72 | + int c; | |
| 73 | + char[] len_arr = new char [16]; | |
| 74 | + int len_size = 0; | |
| 75 | + boolean end_of_len = false; | |
| 76 | + | |
| 77 | + while ((c=in.read())!= -1) { | |
| 78 | + char ch = (char) c; | |
| 79 | + if (len_size == len_arr.length -1) { | |
| 80 | + throw new IOException ("invalid chunk header"); | |
| 81 | + } | |
| 82 | + if (gotCR) { | |
| 83 | + if (ch == LF) { | |
| 84 | + int l = numeric (len_arr, len_size); | |
| 85 | + return l; | |
| 86 | + } else { | |
| 87 | + gotCR = false; | |
| 88 | + } | |
| 89 | + if (!end_of_len) { | |
| 90 | + len_arr[len_size++] = ch; | |
| 91 | + } | |
| 92 | + } else { | |
| 93 | + if (ch == CR) { | |
| 94 | + gotCR = true; | |
| 95 | + } else if (ch == ';') { | |
| 96 | + end_of_len = true; | |
| 97 | + } else if (!end_of_len) { | |
| 98 | + len_arr[len_size++] = ch; | |
| 99 | + } | |
| 100 | + } | |
| 101 | + } | |
| 102 | + throw new IOException ("end of stream reading chunk header"); | |
| 103 | + } | |
| 104 | + | |
| 105 | + protected int readImpl (byte[]b, int off, int len) throws IOException { | |
| 106 | + if (eof) { | |
| 107 | + return -1; | |
| 108 | + } | |
| 109 | + if (needToReadHeader) { | |
| 110 | + remaining = readChunkHeader(); | |
| 111 | + if (remaining == 0) { | |
| 112 | + eof = true; | |
| 113 | + consumeCRLF(); | |
| 114 | + t.getServerImpl().requestCompleted (t.getConnection()); | |
| 115 | + return -1; | |
| 116 | + } | |
| 117 | + needToReadHeader = false; | |
| 118 | + } | |
| 119 | + if (len > remaining) { | |
| 120 | + len = remaining; | |
| 121 | + } | |
| 122 | + int n = in.read(b, off, len); | |
| 123 | + if (n > -1) { | |
| 124 | + remaining -= n; | |
| 125 | + } | |
| 126 | + if (remaining == 0) { | |
| 127 | + needToReadHeader = true; | |
| 128 | + consumeCRLF(); | |
| 129 | + } | |
| 130 | + return n; | |
| 131 | + } | |
| 132 | + | |
| 133 | + private void consumeCRLF () throws IOException { | |
| 134 | + char c; | |
| 135 | + c = (char)in.read(); /* CR */ | |
| 136 | + if (c != CR) { | |
| 137 | + throw new IOException ("invalid chunk end"); | |
| 138 | + } | |
| 139 | + c = (char)in.read(); /* LF */ | |
| 140 | + if (c != LF) { | |
| 141 | + throw new IOException ("invalid chunk end"); | |
| 142 | + } | |
| 143 | + } | |
| 144 | + | |
| 145 | + /** | |
| 146 | + * returns the number of bytes available to read in the current chunk | |
| 147 | + * which may be less than the real amount, but we'll live with that | |
| 148 | + * limitation for the moment. It only affects potential efficiency | |
| 149 | + * rather than correctness. | |
| 150 | + */ | |
| 151 | + public int available () throws IOException { | |
| 152 | + if (eof || closed) { | |
| 153 | + return 0; | |
| 154 | + } | |
| 155 | + int n = in.available(); | |
| 156 | + return n > remaining? remaining: n; | |
| 157 | + } | |
| 158 | + | |
| 159 | + /* called after the stream is closed to see if bytes | |
| 160 | + * have been read from the underlying channel | |
| 161 | + * and buffered internally | |
| 162 | + */ | |
| 163 | + public boolean isDataBuffered () throws IOException { | |
| 164 | + assert eof; | |
| 165 | + return in.available() > 0; | |
| 166 | + } | |
| 167 | + | |
| 168 | + public boolean markSupported () {return false;} | |
| 169 | + | |
| 170 | + public void mark (int l) { | |
| 171 | + } | |
| 172 | + | |
| 173 | + public void reset () throws IOException { | |
| 174 | + throw new IOException ("mark/reset not supported"); | |
| 175 | + } | |
| 176 | +} | |
| @@ -0,0 +1,163 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import java.io.*; | |
| 29 | +import java.net.*; | |
| 30 | +import com.sun.net.httpserver.*; | |
| 31 | +import com.sun.net.httpserver.spi.*; | |
| 32 | + | |
| 33 | +/** | |
| 34 | + * a class which allows the caller to write an arbitrary | |
| 35 | + * number of bytes to an underlying stream. | |
| 36 | + * normal close() does not close the underlying stream | |
| 37 | + * | |
| 38 | + * This class is buffered. | |
| 39 | + * | |
| 40 | + * Each chunk is written in one go as :- | |
| 41 | + * abcd\r\nxxxxxxxxxxxxxx\r\n | |
| 42 | + * | |
| 43 | + * abcd is the chunk-size, and xxx is the chunk data | |
| 44 | + * If the length is less than 4 chars (in size) then the buffer | |
| 45 | + * is written with an offset. | |
| 46 | + * Final chunk is: | |
| 47 | + * 0\r\n\r\n | |
| 48 | + */ | |
| 49 | + | |
| 50 | +class ChunkedOutputStream extends FilterOutputStream | |
| 51 | +{ | |
| 52 | + private boolean closed = false; | |
| 53 | + /* max. amount of user data per chunk */ | |
| 54 | + final static int CHUNK_SIZE = 4096; | |
| 55 | + /* allow 4 bytes for chunk-size plus 4 for CRLFs */ | |
| 56 | + final static int OFFSET = 6; /* initial <=4 bytes for len + CRLF */ | |
| 57 | + private int pos = OFFSET; | |
| 58 | + private int count = 0; | |
| 59 | + private byte[] buf = new byte [CHUNK_SIZE+OFFSET+2]; | |
| 60 | + ExchangeImpl t; | |
| 61 | + | |
| 62 | + ChunkedOutputStream (ExchangeImpl t, OutputStream src) { | |
| 63 | + super (src); | |
| 64 | + this.t = t; | |
| 65 | + } | |
| 66 | + | |
| 67 | + public void write (int b) throws IOException { | |
| 68 | + if (closed) { | |
| 69 | + throw new StreamClosedException (); | |
| 70 | + } | |
| 71 | + buf [pos++] = (byte)b; | |
| 72 | + count ++; | |
| 73 | + if (count == CHUNK_SIZE) { | |
| 74 | + writeChunk(); | |
| 75 | + } | |
| 76 | + assert count < CHUNK_SIZE; | |
| 77 | + } | |
| 78 | + | |
| 79 | + public void write (byte[]b, int off, int len) throws IOException { | |
| 80 | + if (closed) { | |
| 81 | + throw new StreamClosedException (); | |
| 82 | + } | |
| 83 | + int remain = CHUNK_SIZE - count; | |
| 84 | + if (len > remain) { | |
| 85 | + System.arraycopy (b,off,buf,pos,remain); | |
| 86 | + count = CHUNK_SIZE; | |
| 87 | + writeChunk(); | |
| 88 | + len -= remain; | |
| 89 | + off += remain; | |
| 90 | + while (len >= CHUNK_SIZE) { | |
| 91 | + System.arraycopy (b,off,buf,OFFSET,CHUNK_SIZE); | |
| 92 | + len -= CHUNK_SIZE; | |
| 93 | + off += CHUNK_SIZE; | |
| 94 | + count = CHUNK_SIZE; | |
| 95 | + writeChunk(); | |
| 96 | + } | |
| 97 | + } | |
| 98 | + if (len > 0) { | |
| 99 | + System.arraycopy (b,off,buf,pos,len); | |
| 100 | + count += len; | |
| 101 | + pos += len; | |
| 102 | + } | |
| 103 | + if (count == CHUNK_SIZE) { | |
| 104 | + writeChunk(); | |
| 105 | + } | |
| 106 | + } | |
| 107 | + | |
| 108 | + /** | |
| 109 | + * write out a chunk , and reset the pointers | |
| 110 | + * chunk does not have to be CHUNK_SIZE bytes | |
| 111 | + * count must == number of user bytes (<= CHUNK_SIZE) | |
| 112 | + */ | |
| 113 | + private void writeChunk () throws IOException { | |
| 114 | + char[] c = Integer.toHexString (count).toCharArray(); | |
| 115 | + int clen = c.length; | |
| 116 | + int startByte = 4 - clen; | |
| 117 | + int i; | |
| 118 | + for (i=0; i<clen; i++) { | |
| 119 | + buf[startByte+i] = (byte)c[i]; | |
| 120 | + } | |
| 121 | + buf[startByte + (i++)] = '\r'; | |
| 122 | + buf[startByte + (i++)] = '\n'; | |
| 123 | + buf[startByte + (i++) + count] = '\r'; | |
| 124 | + buf[startByte + (i++) + count] = '\n'; | |
| 125 | + out.write (buf, startByte, i+count); | |
| 126 | + count = 0; | |
| 127 | + pos = OFFSET; | |
| 128 | + } | |
| 129 | + | |
| 130 | + public void close () throws IOException { | |
| 131 | + if (closed) { | |
| 132 | + return; | |
| 133 | + } | |
| 134 | + flush(); | |
| 135 | + try { | |
| 136 | + /* write an empty chunk */ | |
| 137 | + writeChunk(); | |
| 138 | + out.flush(); | |
| 139 | + LeftOverInputStream is = t.getOriginalInputStream(); | |
| 140 | + if (!is.isClosed()) { | |
| 141 | + is.close(); | |
| 142 | + } | |
| 143 | + /* some clients close the connection before empty chunk is sent */ | |
| 144 | + } catch (IOException e) { | |
| 145 | + | |
| 146 | + } finally { | |
| 147 | + closed = true; | |
| 148 | + } | |
| 149 | + | |
| 150 | + WriteFinishedEvent e = new WriteFinishedEvent (t); | |
| 151 | + t.getHttpContext().getServerImpl().addEvent (e); | |
| 152 | + } | |
| 153 | + | |
| 154 | + public void flush () throws IOException { | |
| 155 | + if (closed) { | |
| 156 | + throw new StreamClosedException (); | |
| 157 | + } | |
| 158 | + if (count > 0) { | |
| 159 | + writeChunk(); | |
| 160 | + } | |
| 161 | + out.flush(); | |
| 162 | + } | |
| 163 | +} | |
| @@ -0,0 +1,109 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +class Code { | |
| 29 | + | |
| 30 | + public static final int HTTP_CONTINUE = 100; | |
| 31 | + public static final int HTTP_OK = 200; | |
| 32 | + public static final int HTTP_CREATED = 201; | |
| 33 | + public static final int HTTP_ACCEPTED = 202; | |
| 34 | + public static final int HTTP_NOT_AUTHORITATIVE = 203; | |
| 35 | + public static final int HTTP_NO_CONTENT = 204; | |
| 36 | + public static final int HTTP_RESET = 205; | |
| 37 | + public static final int HTTP_PARTIAL = 206; | |
| 38 | + public static final int HTTP_MULT_CHOICE = 300; | |
| 39 | + public static final int HTTP_MOVED_PERM = 301; | |
| 40 | + public static final int HTTP_MOVED_TEMP = 302; | |
| 41 | + public static final int HTTP_SEE_OTHER = 303; | |
| 42 | + public static final int HTTP_NOT_MODIFIED = 304; | |
| 43 | + public static final int HTTP_USE_PROXY = 305; | |
| 44 | + public static final int HTTP_BAD_REQUEST = 400; | |
| 45 | + public static final int HTTP_UNAUTHORIZED = 401; | |
| 46 | + public static final int HTTP_PAYMENT_REQUIRED = 402; | |
| 47 | + public static final int HTTP_FORBIDDEN = 403; | |
| 48 | + public static final int HTTP_NOT_FOUND = 404; | |
| 49 | + public static final int HTTP_BAD_METHOD = 405; | |
| 50 | + public static final int HTTP_NOT_ACCEPTABLE = 406; | |
| 51 | + public static final int HTTP_PROXY_AUTH = 407; | |
| 52 | + public static final int HTTP_CLIENT_TIMEOUT = 408; | |
| 53 | + public static final int HTTP_CONFLICT = 409; | |
| 54 | + public static final int HTTP_GONE = 410; | |
| 55 | + public static final int HTTP_LENGTH_REQUIRED = 411; | |
| 56 | + public static final int HTTP_PRECON_FAILED = 412; | |
| 57 | + public static final int HTTP_ENTITY_TOO_LARGE = 413; | |
| 58 | + public static final int HTTP_REQ_TOO_LONG = 414; | |
| 59 | + public static final int HTTP_UNSUPPORTED_TYPE = 415; | |
| 60 | + public static final int HTTP_INTERNAL_ERROR = 500; | |
| 61 | + public static final int HTTP_NOT_IMPLEMENTED = 501; | |
| 62 | + public static final int HTTP_BAD_GATEWAY = 502; | |
| 63 | + public static final int HTTP_UNAVAILABLE = 503; | |
| 64 | + public static final int HTTP_GATEWAY_TIMEOUT = 504; | |
| 65 | + public static final int HTTP_VERSION = 505; | |
| 66 | + | |
| 67 | + static String msg (int code) { | |
| 68 | + | |
| 69 | + switch (code) { | |
| 70 | + case HTTP_OK: return " OK"; | |
| 71 | + case HTTP_CONTINUE: return " Continue"; | |
| 72 | + case HTTP_CREATED: return " Created"; | |
| 73 | + case HTTP_ACCEPTED: return " Accepted"; | |
| 74 | + case HTTP_NOT_AUTHORITATIVE: return " Non-Authoritative Information"; | |
| 75 | + case HTTP_NO_CONTENT: return " No Content"; | |
| 76 | + case HTTP_RESET: return " Reset Content"; | |
| 77 | + case HTTP_PARTIAL: return " Partial Content"; | |
| 78 | + case HTTP_MULT_CHOICE: return " Multiple Choices"; | |
| 79 | + case HTTP_MOVED_PERM: return " Moved Permanently"; | |
| 80 | + case HTTP_MOVED_TEMP: return " Temporary Redirect"; | |
| 81 | + case HTTP_SEE_OTHER: return " See Other"; | |
| 82 | + case HTTP_NOT_MODIFIED: return " Not Modified"; | |
| 83 | + case HTTP_USE_PROXY: return " Use Proxy"; | |
| 84 | + case HTTP_BAD_REQUEST: return " Bad Request"; | |
| 85 | + case HTTP_UNAUTHORIZED: return " Unauthorized" ; | |
| 86 | + case HTTP_PAYMENT_REQUIRED: return " Payment Required"; | |
| 87 | + case HTTP_FORBIDDEN: return " Forbidden"; | |
| 88 | + case HTTP_NOT_FOUND: return " Not Found"; | |
| 89 | + case HTTP_BAD_METHOD: return " Method Not Allowed"; | |
| 90 | + case HTTP_NOT_ACCEPTABLE: return " Not Acceptable"; | |
| 91 | + case HTTP_PROXY_AUTH: return " Proxy Authentication Required"; | |
| 92 | + case HTTP_CLIENT_TIMEOUT: return " Request Time-Out"; | |
| 93 | + case HTTP_CONFLICT: return " Conflict"; | |
| 94 | + case HTTP_GONE: return " Gone"; | |
| 95 | + case HTTP_LENGTH_REQUIRED: return " Length Required"; | |
| 96 | + case HTTP_PRECON_FAILED: return " Precondition Failed"; | |
| 97 | + case HTTP_ENTITY_TOO_LARGE: return " Request Entity Too Large"; | |
| 98 | + case HTTP_REQ_TOO_LONG: return " Request-URI Too Large"; | |
| 99 | + case HTTP_UNSUPPORTED_TYPE: return " Unsupported Media Type"; | |
| 100 | + case HTTP_INTERNAL_ERROR: return " Internal Server Error"; | |
| 101 | + case HTTP_NOT_IMPLEMENTED: return " Not Implemented"; | |
| 102 | + case HTTP_BAD_GATEWAY: return " Bad Gateway"; | |
| 103 | + case HTTP_UNAVAILABLE: return " Service Unavailable"; | |
| 104 | + case HTTP_GATEWAY_TIMEOUT: return " Gateway Timeout"; | |
| 105 | + case HTTP_VERSION: return " HTTP Version Not Supported"; | |
| 106 | + default: return ""; | |
| 107 | + } | |
| 108 | + } | |
| 109 | +} | |
| @@ -0,0 +1,97 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import java.util.*; | |
| 29 | +import com.sun.net.httpserver.*; | |
| 30 | +import com.sun.net.httpserver.spi.*; | |
| 31 | + | |
| 32 | +class ContextList { | |
| 33 | + | |
| 34 | + final static int MAX_CONTEXTS = 50; | |
| 35 | + | |
| 36 | + LinkedList<HttpContextImpl> list = new LinkedList<HttpContextImpl>(); | |
| 37 | + | |
| 38 | + public synchronized void add (HttpContextImpl ctx) { | |
| 39 | + assert ctx.getPath() != null; | |
| 40 | + list.add (ctx); | |
| 41 | + } | |
| 42 | + | |
| 43 | + public synchronized int size () { | |
| 44 | + return list.size(); | |
| 45 | + } | |
| 46 | + | |
| 47 | + /* initially contexts are located only by protocol:path. | |
| 48 | + * Context with longest prefix matches (currently case-sensitive) | |
| 49 | + */ | |
| 50 | + synchronized HttpContextImpl findContext (String protocol, String path) { | |
| 51 | + return findContext (protocol, path, false); | |
| 52 | + } | |
| 53 | + | |
| 54 | + synchronized HttpContextImpl findContext (String protocol, String path, boolean exact) { | |
| 55 | + protocol = protocol.toLowerCase(); | |
| 56 | + String longest = ""; | |
| 57 | + HttpContextImpl lc = null; | |
| 58 | + for (HttpContextImpl ctx: list) { | |
| 59 | + if (!ctx.getProtocol().equals(protocol)) { | |
| 60 | + continue; | |
| 61 | + } | |
| 62 | + String cpath = ctx.getPath(); | |
| 63 | + if (exact && !cpath.equals (path)) { | |
| 64 | + continue; | |
| 65 | + } else if (!exact && !path.startsWith(cpath)) { | |
| 66 | + continue; | |
| 67 | + } | |
| 68 | + if (cpath.length() > longest.length()) { | |
| 69 | + longest = cpath; | |
| 70 | + lc = ctx; | |
| 71 | + } | |
| 72 | + } | |
| 73 | + return lc; | |
| 74 | + } | |
| 75 | + | |
| 76 | + public synchronized void remove (String protocol, String path) | |
| 77 | + throws IllegalArgumentException | |
| 78 | + { | |
| 79 | + HttpContextImpl ctx = findContext (protocol, path, true); | |
| 80 | + if (ctx == null) { | |
| 81 | + throw new IllegalArgumentException ("cannot remove element from list"); | |
| 82 | + } | |
| 83 | + list.remove (ctx); | |
| 84 | + } | |
| 85 | + | |
| 86 | + public synchronized void remove (HttpContextImpl context) | |
| 87 | + throws IllegalArgumentException | |
| 88 | + { | |
| 89 | + for (HttpContextImpl ctx: list) { | |
| 90 | + if (ctx.equals (context)) { | |
| 91 | + list.remove (ctx); | |
| 92 | + return; | |
| 93 | + } | |
| 94 | + } | |
| 95 | + throw new IllegalArgumentException ("no such context in list"); | |
| 96 | + } | |
| 97 | +} | |
| @@ -0,0 +1,41 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import java.net.*; | |
| 29 | +import java.io.*; | |
| 30 | +import com.sun.net.httpserver.*; | |
| 31 | +import com.sun.net.httpserver.spi.*; | |
| 32 | + | |
| 33 | +public class DefaultHttpServerProvider extends HttpServerProvider { | |
| 34 | + public HttpServer createHttpServer (InetSocketAddress addr, int backlog) throws IOException { | |
| 35 | + return new HttpServerImpl (addr, backlog); | |
| 36 | + } | |
| 37 | + | |
| 38 | + public HttpsServer createHttpsServer (InetSocketAddress addr, int backlog) throws IOException { | |
| 39 | + return new HttpsServerImpl (addr, backlog); | |
| 40 | + } | |
| 41 | +} | |
| @@ -0,0 +1,46 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import com.sun.net.httpserver.*; | |
| 29 | +import com.sun.net.httpserver.spi.*; | |
| 30 | + | |
| 31 | +class Event { | |
| 32 | + | |
| 33 | + ExchangeImpl exchange; | |
| 34 | + | |
| 35 | + protected Event (ExchangeImpl t) { | |
| 36 | + this.exchange = t; | |
| 37 | + } | |
| 38 | +} | |
| 39 | + | |
| 40 | +class WriteFinishedEvent extends Event { | |
| 41 | + WriteFinishedEvent (ExchangeImpl t) { | |
| 42 | + super (t); | |
| 43 | + assert !t.writefinished; | |
| 44 | + t.writefinished = true; | |
| 45 | + } | |
| 46 | +} | |
| @@ -0,0 +1,456 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import java.io.*; | |
| 29 | +import java.net.*; | |
| 30 | +import javax.net.ssl.*; | |
| 31 | +import java.util.*; | |
| 32 | +import java.util.logging.Logger; | |
| 33 | +import java.text.*; | |
| 34 | +import com.sun.net.httpserver.*; | |
| 35 | + | |
| 36 | +class ExchangeImpl { | |
| 37 | + | |
| 38 | + Headers reqHdrs, rspHdrs; | |
| 39 | + Request req; | |
| 40 | + String method; | |
| 41 | + boolean writefinished; | |
| 42 | + URI uri; | |
| 43 | + HttpConnection connection; | |
| 44 | + long reqContentLen; | |
| 45 | + long rspContentLen; | |
| 46 | + /* raw streams which access the socket directly */ | |
| 47 | + InputStream ris; | |
| 48 | + OutputStream ros; | |
| 49 | + Thread thread; | |
| 50 | + /* close the underlying connection when this exchange finished */ | |
| 51 | + boolean close; | |
| 52 | + boolean closed; | |
| 53 | + boolean http10 = false; | |
| 54 | + | |
| 55 | + /* for formatting the Date: header */ | |
| 56 | + private static final String pattern = "EEE, dd MMM yyyy HH:mm:ss zzz"; | |
| 57 | + private static final TimeZone gmtTZ = TimeZone.getTimeZone("GMT"); | |
| 58 | + private static final ThreadLocal<DateFormat> dateFormat = | |
| 59 | + new ThreadLocal<DateFormat>() { | |
| 60 | + @Override protected DateFormat initialValue() { | |
| 61 | + DateFormat df = new SimpleDateFormat(pattern, Locale.US); | |
| 62 | + df.setTimeZone(gmtTZ); | |
| 63 | + return df; | |
| 64 | + } | |
| 65 | + }; | |
| 66 | + | |
| 67 | + private static final String HEAD = "HEAD"; | |
| 68 | + | |
| 69 | + /* streams which take care of the HTTP protocol framing | |
| 70 | + * and are passed up to higher layers | |
| 71 | + */ | |
| 72 | + InputStream uis; | |
| 73 | + OutputStream uos; | |
| 74 | + LeftOverInputStream uis_orig; // uis may have be a user supplied wrapper | |
| 75 | + PlaceholderOutputStream uos_orig; | |
| 76 | + | |
| 77 | + boolean sentHeaders; /* true after response headers sent */ | |
| 78 | + Map<String,Object> attributes; | |
| 79 | + int rcode = -1; | |
| 80 | + HttpPrincipal principal; | |
| 81 | + ServerImpl server; | |
| 82 | + | |
| 83 | + ExchangeImpl ( | |
| 84 | + String m, URI u, Request req, long len, HttpConnection connection | |
| 85 | + ) throws IOException { | |
| 86 | + this.req = req; | |
| 87 | + this.reqHdrs = req.headers(); | |
| 88 | + this.rspHdrs = new Headers(); | |
| 89 | + this.method = m; | |
| 90 | + this.uri = u; | |
| 91 | + this.connection = connection; | |
| 92 | + this.reqContentLen = len; | |
| 93 | + /* ros only used for headers, body written directly to stream */ | |
| 94 | + this.ros = req.outputStream(); | |
| 95 | + this.ris = req.inputStream(); | |
| 96 | + server = getServerImpl(); | |
| 97 | + server.startExchange(); | |
| 98 | + } | |
| 99 | + | |
| 100 | + public Headers getRequestHeaders () { | |
| 101 | + return new UnmodifiableHeaders (reqHdrs); | |
| 102 | + } | |
| 103 | + | |
| 104 | + public Headers getResponseHeaders () { | |
| 105 | + return rspHdrs; | |
| 106 | + } | |
| 107 | + | |
| 108 | + public URI getRequestURI () { | |
| 109 | + return uri; | |
| 110 | + } | |
| 111 | + | |
| 112 | + public String getRequestMethod (){ | |
| 113 | + return method; | |
| 114 | + } | |
| 115 | + | |
| 116 | + public HttpContextImpl getHttpContext (){ | |
| 117 | + return connection.getHttpContext(); | |
| 118 | + } | |
| 119 | + | |
| 120 | + private boolean isHeadRequest() { | |
| 121 | + return HEAD.equals(getRequestMethod()); | |
| 122 | + } | |
| 123 | + | |
| 124 | + public void close () { | |
| 125 | + if (closed) { | |
| 126 | + return; | |
| 127 | + } | |
| 128 | + closed = true; | |
| 129 | + | |
| 130 | + /* close the underlying connection if, | |
| 131 | + * a) the streams not set up yet, no response can be sent, or | |
| 132 | + * b) if the wrapper output stream is not set up, or | |
| 133 | + * c) if the close of the input/outpu stream fails | |
| 134 | + */ | |
| 135 | + try { | |
| 136 | + if (uis_orig == null || uos == null) { | |
| 137 | + connection.close(); | |
| 138 | + return; | |
| 139 | + } | |
| 140 | + if (!uos_orig.isWrapped()) { | |
| 141 | + connection.close(); | |
| 142 | + return; | |
| 143 | + } | |
| 144 | + if (!uis_orig.isClosed()) { | |
| 145 | + uis_orig.close(); | |
| 146 | + } | |
| 147 | + uos.close(); | |
| 148 | + } catch (IOException e) { | |
| 149 | + connection.close(); | |
| 150 | + } | |
| 151 | + } | |
| 152 | + | |
| 153 | + public InputStream getRequestBody () { | |
| 154 | + if (uis != null) { | |
| 155 | + return uis; | |
| 156 | + } | |
| 157 | + if (reqContentLen == -1L) { | |
| 158 | + uis_orig = new ChunkedInputStream (this, ris); | |
| 159 | + uis = uis_orig; | |
| 160 | + } else { | |
| 161 | + uis_orig = new FixedLengthInputStream (this, ris, reqContentLen); | |
| 162 | + uis = uis_orig; | |
| 163 | + } | |
| 164 | + return uis; | |
| 165 | + } | |
| 166 | + | |
| 167 | + LeftOverInputStream getOriginalInputStream () { | |
| 168 | + return uis_orig; | |
| 169 | + } | |
| 170 | + | |
| 171 | + public int getResponseCode () { | |
| 172 | + return rcode; | |
| 173 | + } | |
| 174 | + | |
| 175 | + public OutputStream getResponseBody () { | |
| 176 | + /* TODO. Change spec to remove restriction below. Filters | |
| 177 | + * cannot work with this restriction | |
| 178 | + * | |
| 179 | + * if (!sentHeaders) { | |
| 180 | + * throw new IllegalStateException ("headers not sent"); | |
| 181 | + * } | |
| 182 | + */ | |
| 183 | + if (uos == null) { | |
| 184 | + uos_orig = new PlaceholderOutputStream (null); | |
| 185 | + uos = uos_orig; | |
| 186 | + } | |
| 187 | + return uos; | |
| 188 | + } | |
| 189 | + | |
| 190 | + | |
| 191 | + /* returns the place holder stream, which is the stream | |
| 192 | + * returned from the 1st call to getResponseBody() | |
| 193 | + * The "real" ouputstream is then placed inside this | |
| 194 | + */ | |
| 195 | + PlaceholderOutputStream getPlaceholderResponseBody () { | |
| 196 | + getResponseBody(); | |
| 197 | + return uos_orig; | |
| 198 | + } | |
| 199 | + | |
| 200 | + public void sendResponseHeaders (int rCode, long contentLen) | |
| 201 | + throws IOException | |
| 202 | + { | |
| 203 | + if (sentHeaders) { | |
| 204 | + throw new IOException ("headers already sent"); | |
| 205 | + } | |
| 206 | + this.rcode = rCode; | |
| 207 | + String statusLine = "HTTP/1.1 "+rCode+Code.msg(rCode)+"\r\n"; | |
| 208 | + OutputStream tmpout = new BufferedOutputStream (ros); | |
| 209 | + PlaceholderOutputStream o = getPlaceholderResponseBody(); | |
| 210 | + tmpout.write (bytes(statusLine, 0), 0, statusLine.length()); | |
| 211 | + boolean noContentToSend = false; // assume there is content | |
| 212 | + rspHdrs.set ("Date", dateFormat.get().format (new Date())); | |
| 213 | + | |
| 214 | + /* check for response type that is not allowed to send a body */ | |
| 215 | + | |
| 216 | + if ((rCode>=100 && rCode <200) /* informational */ | |
| 217 | + ||(rCode == 204) /* no content */ | |
| 218 | + ||(rCode == 304)) /* not modified */ | |
| 219 | + { | |
| 220 | + if (contentLen != -1) { | |
| 221 | + Logger logger = server.getLogger(); | |
| 222 | + String msg = "sendResponseHeaders: rCode = "+ rCode | |
| 223 | + + ": forcing contentLen = -1"; | |
| 224 | + logger.warning (msg); | |
| 225 | + } | |
| 226 | + contentLen = -1; | |
| 227 | + } | |
| 228 | + | |
| 229 | + if (isHeadRequest()) { | |
| 230 | + /* HEAD requests should not set a content length by passing it | |
| 231 | + * through this API, but should instead manually set the required | |
| 232 | + * headers.*/ | |
| 233 | + if (contentLen >= 0) { | |
| 234 | + final Logger logger = server.getLogger(); | |
| 235 | + String msg = | |
| 236 | + "sendResponseHeaders: being invoked with a content length for a HEAD request"; | |
| 237 | + logger.warning (msg); | |
| 238 | + } | |
| 239 | + noContentToSend = true; | |
| 240 | + contentLen = 0; | |
| 241 | + } else { /* not a HEAD request */ | |
| 242 | + if (contentLen == 0) { | |
| 243 | + if (http10) { | |
| 244 | + o.setWrappedStream (new UndefLengthOutputStream (this, ros)); | |
| 245 | + close = true; | |
| 246 | + } else { | |
| 247 | + rspHdrs.set ("Transfer-encoding", "chunked"); | |
| 248 | + o.setWrappedStream (new ChunkedOutputStream (this, ros)); | |
| 249 | + } | |
| 250 | + } else { | |
| 251 | + if (contentLen == -1) { | |
| 252 | + noContentToSend = true; | |
| 253 | + contentLen = 0; | |
| 254 | + } | |
| 255 | + rspHdrs.set("Content-length", Long.toString(contentLen)); | |
| 256 | + o.setWrappedStream (new FixedLengthOutputStream (this, ros, contentLen)); | |
| 257 | + } | |
| 258 | + } | |
| 259 | + write (rspHdrs, tmpout); | |
| 260 | + this.rspContentLen = contentLen; | |
| 261 | + tmpout.flush() ; | |
| 262 | + tmpout = null; | |
| 263 | + sentHeaders = true; | |
| 264 | + if (noContentToSend) { | |
| 265 | + WriteFinishedEvent e = new WriteFinishedEvent (this); | |
| 266 | + server.addEvent (e); | |
| 267 | + closed = true; | |
| 268 | + } | |
| 269 | + server.logReply (rCode, req.requestLine(), null); | |
| 270 | + } | |
| 271 | + | |
| 272 | + void write (Headers map, OutputStream os) throws IOException { | |
| 273 | + Set<Map.Entry<String,List<String>>> entries = map.entrySet(); | |
| 274 | + for (Map.Entry<String,List<String>> entry : entries) { | |
| 275 | + String key = entry.getKey(); | |
| 276 | + byte[] buf; | |
| 277 | + List<String> values = entry.getValue(); | |
| 278 | + for (String val : values) { | |
| 279 | + int i = key.length(); | |
| 280 | + buf = bytes (key, 2); | |
| 281 | + buf[i++] = ':'; | |
| 282 | + buf[i++] = ' '; | |
| 283 | + os.write (buf, 0, i); | |
| 284 | + buf = bytes (val, 2); | |
| 285 | + i = val.length(); | |
| 286 | + buf[i++] = '\r'; | |
| 287 | + buf[i++] = '\n'; | |
| 288 | + os.write (buf, 0, i); | |
| 289 | + } | |
| 290 | + } | |
| 291 | + os.write ('\r'); | |
| 292 | + os.write ('\n'); | |
| 293 | + } | |
| 294 | + | |
| 295 | + private byte[] rspbuf = new byte [128]; // used by bytes() | |
| 296 | + | |
| 297 | + /** | |
| 298 | + * convert string to byte[], using rspbuf | |
| 299 | + * Make sure that at least "extra" bytes are free at end | |
| 300 | + * of rspbuf. Reallocate rspbuf if not big enough. | |
| 301 | + * caller must check return value to see if rspbuf moved | |
| 302 | + */ | |
| 303 | + private byte[] bytes (String s, int extra) { | |
| 304 | + int slen = s.length(); | |
| 305 | + if (slen+extra > rspbuf.length) { | |
| 306 | + int diff = slen + extra - rspbuf.length; | |
| 307 | + rspbuf = new byte [2* (rspbuf.length + diff)]; | |
| 308 | + } | |
| 309 | + char c[] = s.toCharArray(); | |
| 310 | + for (int i=0; i<c.length; i++) { | |
| 311 | + rspbuf[i] = (byte)c[i]; | |
| 312 | + } | |
| 313 | + return rspbuf; | |
| 314 | + } | |
| 315 | + | |
| 316 | + public InetSocketAddress getRemoteAddress (){ | |
| 317 | + Socket s = connection.getChannel().socket(); | |
| 318 | + InetAddress ia = s.getInetAddress(); | |
| 319 | + int port = s.getPort(); | |
| 320 | + return new InetSocketAddress (ia, port); | |
| 321 | + } | |
| 322 | + | |
| 323 | + public InetSocketAddress getLocalAddress (){ | |
| 324 | + Socket s = connection.getChannel().socket(); | |
| 325 | + InetAddress ia = s.getLocalAddress(); | |
| 326 | + int port = s.getLocalPort(); | |
| 327 | + return new InetSocketAddress (ia, port); | |
| 328 | + } | |
| 329 | + | |
| 330 | + public String getProtocol (){ | |
| 331 | + String reqline = req.requestLine(); | |
| 332 | + int index = reqline.lastIndexOf (' '); | |
| 333 | + return reqline.substring (index+1); | |
| 334 | + } | |
| 335 | + | |
| 336 | + public SSLSession getSSLSession () { | |
| 337 | + SSLEngine e = connection.getSSLEngine(); | |
| 338 | + if (e == null) { | |
| 339 | + return null; | |
| 340 | + } | |
| 341 | + return e.getSession(); | |
| 342 | + } | |
| 343 | + | |
| 344 | + public Object getAttribute (String name) { | |
| 345 | + if (name == null) { | |
| 346 | + throw new NullPointerException ("null name parameter"); | |
| 347 | + } | |
| 348 | + if (attributes == null) { | |
| 349 | + attributes = getHttpContext().getAttributes(); | |
| 350 | + } | |
| 351 | + return attributes.get (name); | |
| 352 | + } | |
| 353 | + | |
| 354 | + public void setAttribute (String name, Object value) { | |
| 355 | + if (name == null) { | |
| 356 | + throw new NullPointerException ("null name parameter"); | |
| 357 | + } | |
| 358 | + if (attributes == null) { | |
| 359 | + attributes = getHttpContext().getAttributes(); | |
| 360 | + } | |
| 361 | + attributes.put (name, value); | |
| 362 | + } | |
| 363 | + | |
| 364 | + public void setStreams (InputStream i, OutputStream o) { | |
| 365 | + assert uis != null; | |
| 366 | + if (i != null) { | |
| 367 | + uis = i; | |
| 368 | + } | |
| 369 | + if (o != null) { | |
| 370 | + uos = o; | |
| 371 | + } | |
| 372 | + } | |
| 373 | + | |
| 374 | + /** | |
| 375 | + * PP | |
| 376 | + */ | |
| 377 | + HttpConnection getConnection () { | |
| 378 | + return connection; | |
| 379 | + } | |
| 380 | + | |
| 381 | + ServerImpl getServerImpl () { | |
| 382 | + return getHttpContext().getServerImpl(); | |
| 383 | + } | |
| 384 | + | |
| 385 | + public HttpPrincipal getPrincipal () { | |
| 386 | + return principal; | |
| 387 | + } | |
| 388 | + | |
| 389 | + void setPrincipal (HttpPrincipal principal) { | |
| 390 | + this.principal = principal; | |
| 391 | + } | |
| 392 | + | |
| 393 | + static ExchangeImpl get (HttpExchange t) { | |
| 394 | + if (t instanceof HttpExchangeImpl) { | |
| 395 | + return ((HttpExchangeImpl)t).getExchangeImpl(); | |
| 396 | + } else { | |
| 397 | + assert t instanceof HttpsExchangeImpl; | |
| 398 | + return ((HttpsExchangeImpl)t).getExchangeImpl(); | |
| 399 | + } | |
| 400 | + } | |
| 401 | +} | |
| 402 | + | |
| 403 | +/** | |
| 404 | + * An OutputStream which wraps another stream | |
| 405 | + * which is supplied either at creation time, or sometime later. | |
| 406 | + * If a caller/user tries to write to this stream before | |
| 407 | + * the wrapped stream has been provided, then an IOException will | |
| 408 | + * be thrown. | |
| 409 | + */ | |
| 410 | +class PlaceholderOutputStream extends java.io.OutputStream { | |
| 411 | + | |
| 412 | + OutputStream wrapped; | |
| 413 | + | |
| 414 | + PlaceholderOutputStream (OutputStream os) { | |
| 415 | + wrapped = os; | |
| 416 | + } | |
| 417 | + | |
| 418 | + void setWrappedStream (OutputStream os) { | |
| 419 | + wrapped = os; | |
| 420 | + } | |
| 421 | + | |
| 422 | + boolean isWrapped () { | |
| 423 | + return wrapped != null; | |
| 424 | + } | |
| 425 | + | |
| 426 | + private void checkWrap () throws IOException { | |
| 427 | + if (wrapped == null) { | |
| 428 | + throw new IOException ("response headers not sent yet"); | |
| 429 | + } | |
| 430 | + } | |
| 431 | + | |
| 432 | + public void write(int b) throws IOException { | |
| 433 | + checkWrap(); | |
| 434 | + wrapped.write (b); | |
| 435 | + } | |
| 436 | + | |
| 437 | + public void write(byte b[]) throws IOException { | |
| 438 | + checkWrap(); | |
| 439 | + wrapped.write (b); | |
| 440 | + } | |
| 441 | + | |
| 442 | + public void write(byte b[], int off, int len) throws IOException { | |
| 443 | + checkWrap(); | |
| 444 | + wrapped.write (b, off, len); | |
| 445 | + } | |
| 446 | + | |
| 447 | + public void flush() throws IOException { | |
| 448 | + checkWrap(); | |
| 449 | + wrapped.flush(); | |
| 450 | + } | |
| 451 | + | |
| 452 | + public void close() throws IOException { | |
| 453 | + checkWrap(); | |
| 454 | + wrapped.close(); | |
| 455 | + } | |
| 456 | +} | |
| @@ -0,0 +1,82 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import java.io.*; | |
| 29 | +import java.net.*; | |
| 30 | +import com.sun.net.httpserver.*; | |
| 31 | +import com.sun.net.httpserver.spi.*; | |
| 32 | + | |
| 33 | +/** | |
| 34 | + * a class which allows the caller to read up to a defined | |
| 35 | + * number of bytes off an underlying stream | |
| 36 | + * close() does not close the underlying stream | |
| 37 | + */ | |
| 38 | + | |
| 39 | +class FixedLengthInputStream extends LeftOverInputStream { | |
| 40 | + private long remaining; | |
| 41 | + | |
| 42 | + FixedLengthInputStream (ExchangeImpl t, InputStream src, long len) { | |
| 43 | + super (t, src); | |
| 44 | + this.remaining = len; | |
| 45 | + } | |
| 46 | + | |
| 47 | + protected int readImpl (byte[]b, int off, int len) throws IOException { | |
| 48 | + | |
| 49 | + eof = (remaining == 0L); | |
| 50 | + if (eof) { | |
| 51 | + return -1; | |
| 52 | + } | |
| 53 | + if (len > remaining) { | |
| 54 | + len = (int)remaining; | |
| 55 | + } | |
| 56 | + int n = in.read(b, off, len); | |
| 57 | + if (n > -1) { | |
| 58 | + remaining -= n; | |
| 59 | + if (remaining == 0) { | |
| 60 | + t.getServerImpl().requestCompleted (t.getConnection()); | |
| 61 | + } | |
| 62 | + } | |
| 63 | + return n; | |
| 64 | + } | |
| 65 | + | |
| 66 | + public int available () throws IOException { | |
| 67 | + if (eof) { | |
| 68 | + return 0; | |
| 69 | + } | |
| 70 | + int n = in.available(); | |
| 71 | + return n < remaining? n: (int)remaining; | |
| 72 | + } | |
| 73 | + | |
| 74 | + public boolean markSupported () {return false;} | |
| 75 | + | |
| 76 | + public void mark (int l) { | |
| 77 | + } | |
| 78 | + | |
| 79 | + public void reset () throws IOException { | |
| 80 | + throw new IOException ("mark/reset not supported"); | |
| 81 | + } | |
| 82 | +} | |
| @@ -0,0 +1,104 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import java.io.*; | |
| 29 | +import java.net.*; | |
| 30 | +import com.sun.net.httpserver.*; | |
| 31 | +import com.sun.net.httpserver.spi.*; | |
| 32 | + | |
| 33 | +/** | |
| 34 | + * a class which allows the caller to write up to a defined | |
| 35 | + * number of bytes to an underlying stream. The caller *must* | |
| 36 | + * write the pre-defined number or else an exception will be thrown | |
| 37 | + * and the whole request aborted. | |
| 38 | + * normal close() does not close the underlying stream | |
| 39 | + */ | |
| 40 | + | |
| 41 | +class FixedLengthOutputStream extends FilterOutputStream | |
| 42 | +{ | |
| 43 | + private long remaining; | |
| 44 | + private boolean eof = false; | |
| 45 | + private boolean closed = false; | |
| 46 | + ExchangeImpl t; | |
| 47 | + | |
| 48 | + FixedLengthOutputStream (ExchangeImpl t, OutputStream src, long len) { | |
| 49 | + super (src); | |
| 50 | + this.t = t; | |
| 51 | + this.remaining = len; | |
| 52 | + } | |
| 53 | + | |
| 54 | + public void write (int b) throws IOException { | |
| 55 | + if (closed) { | |
| 56 | + throw new IOException ("stream closed"); | |
| 57 | + } | |
| 58 | + eof = (remaining == 0); | |
| 59 | + if (eof) { | |
| 60 | + throw new StreamClosedException(); | |
| 61 | + } | |
| 62 | + out.write(b); | |
| 63 | + remaining --; | |
| 64 | + } | |
| 65 | + | |
| 66 | + public void write (byte[]b, int off, int len) throws IOException { | |
| 67 | + if (closed) { | |
| 68 | + throw new IOException ("stream closed"); | |
| 69 | + } | |
| 70 | + eof = (remaining == 0); | |
| 71 | + if (eof) { | |
| 72 | + throw new StreamClosedException(); | |
| 73 | + } | |
| 74 | + if (len > remaining) { | |
| 75 | + // stream is still open, caller can retry | |
| 76 | + throw new IOException ("too many bytes to write to stream"); | |
| 77 | + } | |
| 78 | + out.write(b, off, len); | |
| 79 | + remaining -= len; | |
| 80 | + } | |
| 81 | + | |
| 82 | + public void close () throws IOException { | |
| 83 | + if (closed) { | |
| 84 | + return; | |
| 85 | + } | |
| 86 | + closed = true; | |
| 87 | + if (remaining > 0) { | |
| 88 | + t.close(); | |
| 89 | + throw new IOException ("insufficient bytes written to stream"); | |
| 90 | + } | |
| 91 | + flush(); | |
| 92 | + eof = true; | |
| 93 | + LeftOverInputStream is = t.getOriginalInputStream(); | |
| 94 | + if (!is.isClosed()) { | |
| 95 | + try { | |
| 96 | + is.close(); | |
| 97 | + } catch (IOException e) {} | |
| 98 | + } | |
| 99 | + WriteFinishedEvent e = new WriteFinishedEvent (t); | |
| 100 | + t.getHttpContext().getServerImpl().addEvent (e); | |
| 101 | + } | |
| 102 | + | |
| 103 | + // flush is a pass-through | |
| 104 | +} | |
| @@ -0,0 +1,196 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import java.io.*; | |
| 29 | +import javax.net.ssl.*; | |
| 30 | +import java.nio.channels.*; | |
| 31 | +import java.util.logging.Logger; | |
| 32 | +import com.sun.net.httpserver.*; | |
| 33 | +import com.sun.net.httpserver.spi.*; | |
| 34 | + | |
| 35 | +/** | |
| 36 | + * encapsulates all the connection specific state for a HTTP/S connection | |
| 37 | + * one of these is hung from the selector attachment and is used to locate | |
| 38 | + * everything from that. | |
| 39 | + */ | |
| 40 | +class HttpConnection { | |
| 41 | + | |
| 42 | + HttpContextImpl context; | |
| 43 | + SSLEngine engine; | |
| 44 | + SSLContext sslContext; | |
| 45 | + SSLStreams sslStreams; | |
| 46 | + | |
| 47 | + /* high level streams returned to application */ | |
| 48 | + InputStream i; | |
| 49 | + | |
| 50 | + /* low level stream that sits directly over channel */ | |
| 51 | + InputStream raw; | |
| 52 | + OutputStream rawout; | |
| 53 | + | |
| 54 | + SocketChannel chan; | |
| 55 | + SelectionKey selectionKey; | |
| 56 | + String protocol; | |
| 57 | + long time; | |
| 58 | + volatile long creationTime; // time this connection was created | |
| 59 | + volatile long rspStartedTime; // time we started writing the response | |
| 60 | + int remaining; | |
| 61 | + boolean closed = false; | |
| 62 | + Logger logger; | |
| 63 | + | |
| 64 | + public enum State {IDLE, REQUEST, RESPONSE}; | |
| 65 | + volatile State state; | |
| 66 | + | |
| 67 | + public String toString() { | |
| 68 | + String s = null; | |
| 69 | + if (chan != null) { | |
| 70 | + s = chan.toString(); | |
| 71 | + } | |
| 72 | + return s; | |
| 73 | + } | |
| 74 | + | |
| 75 | + HttpConnection () { | |
| 76 | + } | |
| 77 | + | |
| 78 | + void setChannel (SocketChannel c) { | |
| 79 | + chan = c; | |
| 80 | + } | |
| 81 | + | |
| 82 | + void setContext (HttpContextImpl ctx) { | |
| 83 | + context = ctx; | |
| 84 | + } | |
| 85 | + | |
| 86 | + State getState() { | |
| 87 | + return state; | |
| 88 | + } | |
| 89 | + | |
| 90 | + void setState (State s) { | |
| 91 | + state = s; | |
| 92 | + } | |
| 93 | + | |
| 94 | + void setParameters ( | |
| 95 | + InputStream in, OutputStream rawout, SocketChannel chan, | |
| 96 | + SSLEngine engine, SSLStreams sslStreams, SSLContext sslContext, String protocol, | |
| 97 | + HttpContextImpl context, InputStream raw | |
| 98 | + ) | |
| 99 | + { | |
| 100 | + this.context = context; | |
| 101 | + this.i = in; | |
| 102 | + this.rawout = rawout; | |
| 103 | + this.raw = raw; | |
| 104 | + this.protocol = protocol; | |
| 105 | + this.engine = engine; | |
| 106 | + this.chan = chan; | |
| 107 | + this.sslContext = sslContext; | |
| 108 | + this.sslStreams = sslStreams; | |
| 109 | + this.logger = context.getLogger(); | |
| 110 | + } | |
| 111 | + | |
| 112 | + SocketChannel getChannel () { | |
| 113 | + return chan; | |
| 114 | + } | |
| 115 | + | |
| 116 | + synchronized void close () { | |
| 117 | + if (closed) { | |
| 118 | + return; | |
| 119 | + } | |
| 120 | + closed = true; | |
| 121 | + if (logger != null && chan != null) { | |
| 122 | + logger.finest ("Closing connection: " + chan.toString()); | |
| 123 | + } | |
| 124 | + | |
| 125 | + if (!chan.isOpen()) { | |
| 126 | + ServerImpl.dprint ("Channel already closed"); | |
| 127 | + return; | |
| 128 | + } | |
| 129 | + try { | |
| 130 | + /* need to ensure temporary selectors are closed */ | |
| 131 | + if (raw != null) { | |
| 132 | + raw.close(); | |
| 133 | + } | |
| 134 | + } catch (IOException e) { | |
| 135 | + ServerImpl.dprint (e); | |
| 136 | + } | |
| 137 | + try { | |
| 138 | + if (rawout != null) { | |
| 139 | + rawout.close(); | |
| 140 | + } | |
| 141 | + } catch (IOException e) { | |
| 142 | + ServerImpl.dprint (e); | |
| 143 | + } | |
| 144 | + try { | |
| 145 | + if (sslStreams != null) { | |
| 146 | + sslStreams.close(); | |
| 147 | + } | |
| 148 | + } catch (IOException e) { | |
| 149 | + ServerImpl.dprint (e); | |
| 150 | + } | |
| 151 | + try { | |
| 152 | + chan.close(); | |
| 153 | + } catch (IOException e) { | |
| 154 | + ServerImpl.dprint (e); | |
| 155 | + } | |
| 156 | + } | |
| 157 | + | |
| 158 | + /* remaining is the number of bytes left on the lowest level inputstream | |
| 159 | + * after the exchange is finished | |
| 160 | + */ | |
| 161 | + void setRemaining (int r) { | |
| 162 | + remaining = r; | |
| 163 | + } | |
| 164 | + | |
| 165 | + int getRemaining () { | |
| 166 | + return remaining; | |
| 167 | + } | |
| 168 | + | |
| 169 | + SelectionKey getSelectionKey () { | |
| 170 | + return selectionKey; | |
| 171 | + } | |
| 172 | + | |
| 173 | + InputStream getInputStream () { | |
| 174 | + return i; | |
| 175 | + } | |
| 176 | + | |
| 177 | + OutputStream getRawOutputStream () { | |
| 178 | + return rawout; | |
| 179 | + } | |
| 180 | + | |
| 181 | + String getProtocol () { | |
| 182 | + return protocol; | |
| 183 | + } | |
| 184 | + | |
| 185 | + SSLEngine getSSLEngine () { | |
| 186 | + return engine; | |
| 187 | + } | |
| 188 | + | |
| 189 | + SSLContext getSSLContext () { | |
| 190 | + return sslContext; | |
| 191 | + } | |
| 192 | + | |
| 193 | + HttpContextImpl getHttpContext () { | |
| 194 | + return context; | |
| 195 | + } | |
| 196 | +} | |
| @@ -0,0 +1,152 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | +import java.io.*; | |
| 28 | +import java.util.*; | |
| 29 | +import java.util.logging.Logger; | |
| 30 | +import com.sun.net.httpserver.*; | |
| 31 | +import com.sun.net.httpserver.spi.*; | |
| 32 | + | |
| 33 | +/** | |
| 34 | + * HttpContext represents a mapping between a protocol (http or https) together with a root URI path | |
| 35 | + * to a {@link HttpHandler} which is invoked to handle requests destined | |
| 36 | + * for the protocol/path on the associated HttpServer. | |
| 37 | + * <p> | |
| 38 | + * HttpContext instances are created by {@link HttpServer#createContext(String,String,HttpHandler,Object)} | |
| 39 | + * <p> | |
| 40 | + */ | |
| 41 | +class HttpContextImpl extends HttpContext { | |
| 42 | + | |
| 43 | + private String path; | |
| 44 | + private String protocol; | |
| 45 | + private HttpHandler handler; | |
| 46 | + private Map<String,Object> attributes = new HashMap<String,Object>(); | |
| 47 | + private ServerImpl server; | |
| 48 | + /* system filters, not visible to applications */ | |
| 49 | + private LinkedList<Filter> sfilters = new LinkedList<Filter>(); | |
| 50 | + /* user filters, set by applications */ | |
| 51 | + private LinkedList<Filter> ufilters = new LinkedList<Filter>(); | |
| 52 | + private Authenticator authenticator; | |
| 53 | + private AuthFilter authfilter; | |
| 54 | + | |
| 55 | + /** | |
| 56 | + * constructor is package private. | |
| 57 | + */ | |
| 58 | + HttpContextImpl (String protocol, String path, HttpHandler cb, ServerImpl server) { | |
| 59 | + if (path == null || protocol == null || path.length() < 1 || path.charAt(0) != '/') { | |
| 60 | + throw new IllegalArgumentException ("Illegal value for path or protocol"); | |
| 61 | + } | |
| 62 | + this.protocol = protocol.toLowerCase(); | |
| 63 | + this.path = path; | |
| 64 | + if (!this.protocol.equals ("http") && !this.protocol.equals ("https")) { | |
| 65 | + throw new IllegalArgumentException ("Illegal value for protocol"); | |
| 66 | + } | |
| 67 | + this.handler = cb; | |
| 68 | + this.server = server; | |
| 69 | + authfilter = new AuthFilter(null); | |
| 70 | + sfilters.add (authfilter); | |
| 71 | + } | |
| 72 | + | |
| 73 | + /** | |
| 74 | + * returns the handler for this context | |
| 75 | + * @return the HttpHandler for this context | |
| 76 | + */ | |
| 77 | + public HttpHandler getHandler () { | |
| 78 | + return handler; | |
| 79 | + } | |
| 80 | + | |
| 81 | + public void setHandler (HttpHandler h) { | |
| 82 | + if (h == null) { | |
| 83 | + throw new NullPointerException ("Null handler parameter"); | |
| 84 | + } | |
| 85 | + if (handler != null) { | |
| 86 | + throw new IllegalArgumentException ("handler already set"); | |
| 87 | + } | |
| 88 | + handler = h; | |
| 89 | + } | |
| 90 | + | |
| 91 | + /** | |
| 92 | + * returns the path this context was created with | |
| 93 | + * @return this context's path | |
| 94 | + */ | |
| 95 | + public String getPath() { | |
| 96 | + return path; | |
| 97 | + } | |
| 98 | + | |
| 99 | + /** | |
| 100 | + * returns the server this context was created with | |
| 101 | + * @return this context's server | |
| 102 | + */ | |
| 103 | + public HttpServer getServer () { | |
| 104 | + return server.getWrapper(); | |
| 105 | + } | |
| 106 | + | |
| 107 | + ServerImpl getServerImpl () { | |
| 108 | + return server; | |
| 109 | + } | |
| 110 | + | |
| 111 | + /** | |
| 112 | + * returns the protocol this context was created with | |
| 113 | + * @return this context's path | |
| 114 | + */ | |
| 115 | + public String getProtocol() { | |
| 116 | + return protocol; | |
| 117 | + } | |
| 118 | + | |
| 119 | + /** | |
| 120 | + * returns a mutable Map, which can be used to pass | |
| 121 | + * configuration and other data to Filter modules | |
| 122 | + * and to the context's exchange handler. | |
| 123 | + * <p> | |
| 124 | + * Every attribute stored in this Map will be visible to | |
| 125 | + * every HttpExchange processed by this context | |
| 126 | + */ | |
| 127 | + public Map<String,Object> getAttributes() { | |
| 128 | + return attributes; | |
| 129 | + } | |
| 130 | + | |
| 131 | + public List<Filter> getFilters () { | |
| 132 | + return ufilters; | |
| 133 | + } | |
| 134 | + | |
| 135 | + List<Filter> getSystemFilters () { | |
| 136 | + return sfilters; | |
| 137 | + } | |
| 138 | + | |
| 139 | + public Authenticator setAuthenticator (Authenticator auth) { | |
| 140 | + Authenticator old = authenticator; | |
| 141 | + authenticator = auth; | |
| 142 | + authfilter.setAuthenticator (auth); | |
| 143 | + return old; | |
| 144 | + } | |
| 145 | + | |
| 146 | + public Authenticator getAuthenticator () { | |
| 147 | + return authenticator; | |
| 148 | + } | |
| 149 | + Logger getLogger () { | |
| 150 | + return server.getLogger(); | |
| 151 | + } | |
| 152 | +} | |
| @@ -0,0 +1,37 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +/** | |
| 29 | + * A Http error | |
| 30 | + */ | |
| 31 | +class HttpError extends RuntimeException { | |
| 32 | + private static final long serialVersionUID = 8769596371344178179L; | |
| 33 | + | |
| 34 | + public HttpError (String msg) { | |
| 35 | + super (msg); | |
| 36 | + } | |
| 37 | +} | |
| @@ -0,0 +1,120 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import java.io.*; | |
| 29 | +import java.nio.*; | |
| 30 | +import java.nio.channels.*; | |
| 31 | +import java.net.*; | |
| 32 | +import javax.net.ssl.*; | |
| 33 | +import java.util.*; | |
| 34 | +import sun.net.www.MessageHeader; | |
| 35 | +import com.sun.net.httpserver.*; | |
| 36 | +import com.sun.net.httpserver.spi.*; | |
| 37 | + | |
| 38 | +class HttpExchangeImpl extends HttpExchange { | |
| 39 | + | |
| 40 | + ExchangeImpl impl; | |
| 41 | + | |
| 42 | + HttpExchangeImpl (ExchangeImpl impl) { | |
| 43 | + this.impl = impl; | |
| 44 | + } | |
| 45 | + | |
| 46 | + public Headers getRequestHeaders () { | |
| 47 | + return impl.getRequestHeaders(); | |
| 48 | + } | |
| 49 | + | |
| 50 | + public Headers getResponseHeaders () { | |
| 51 | + return impl.getResponseHeaders(); | |
| 52 | + } | |
| 53 | + | |
| 54 | + public URI getRequestURI () { | |
| 55 | + return impl.getRequestURI(); | |
| 56 | + } | |
| 57 | + | |
| 58 | + public String getRequestMethod (){ | |
| 59 | + return impl.getRequestMethod(); | |
| 60 | + } | |
| 61 | + | |
| 62 | + public HttpContextImpl getHttpContext (){ | |
| 63 | + return impl.getHttpContext(); | |
| 64 | + } | |
| 65 | + | |
| 66 | + public void close () { | |
| 67 | + impl.close(); | |
| 68 | + } | |
| 69 | + | |
| 70 | + public InputStream getRequestBody () { | |
| 71 | + return impl.getRequestBody(); | |
| 72 | + } | |
| 73 | + | |
| 74 | + public int getResponseCode () { | |
| 75 | + return impl.getResponseCode(); | |
| 76 | + } | |
| 77 | + | |
| 78 | + public OutputStream getResponseBody () { | |
| 79 | + return impl.getResponseBody(); | |
| 80 | + } | |
| 81 | + | |
| 82 | + | |
| 83 | + public void sendResponseHeaders (int rCode, long contentLen) | |
| 84 | + throws IOException | |
| 85 | + { | |
| 86 | + impl.sendResponseHeaders (rCode, contentLen); | |
| 87 | + } | |
| 88 | + | |
| 89 | + public InetSocketAddress getRemoteAddress (){ | |
| 90 | + return impl.getRemoteAddress(); | |
| 91 | + } | |
| 92 | + | |
| 93 | + public InetSocketAddress getLocalAddress (){ | |
| 94 | + return impl.getLocalAddress(); | |
| 95 | + } | |
| 96 | + | |
| 97 | + public String getProtocol (){ | |
| 98 | + return impl.getProtocol(); | |
| 99 | + } | |
| 100 | + | |
| 101 | + public Object getAttribute (String name) { | |
| 102 | + return impl.getAttribute (name); | |
| 103 | + } | |
| 104 | + | |
| 105 | + public void setAttribute (String name, Object value) { | |
| 106 | + impl.setAttribute (name, value); | |
| 107 | + } | |
| 108 | + | |
| 109 | + public void setStreams (InputStream i, OutputStream o) { | |
| 110 | + impl.setStreams (i, o); | |
| 111 | + } | |
| 112 | + | |
| 113 | + public HttpPrincipal getPrincipal () { | |
| 114 | + return impl.getPrincipal(); | |
| 115 | + } | |
| 116 | + | |
| 117 | + ExchangeImpl getExchangeImpl () { | |
| 118 | + return impl; | |
| 119 | + } | |
| 120 | +} | |
| @@ -0,0 +1,92 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import java.net.*; | |
| 29 | +import java.io.*; | |
| 30 | +import java.nio.*; | |
| 31 | +import java.security.*; | |
| 32 | +import java.nio.channels.*; | |
| 33 | +import java.util.*; | |
| 34 | +import java.util.concurrent.*; | |
| 35 | +import javax.net.ssl.*; | |
| 36 | +import com.sun.net.httpserver.*; | |
| 37 | +import com.sun.net.httpserver.spi.*; | |
| 38 | + | |
| 39 | +public class HttpServerImpl extends HttpServer { | |
| 40 | + | |
| 41 | + ServerImpl server; | |
| 42 | + | |
| 43 | + HttpServerImpl () throws IOException { | |
| 44 | + this (new InetSocketAddress(80), 0); | |
| 45 | + } | |
| 46 | + | |
| 47 | + HttpServerImpl ( | |
| 48 | + InetSocketAddress addr, int backlog | |
| 49 | + ) throws IOException { | |
| 50 | + server = new ServerImpl (this, "http", addr, backlog); | |
| 51 | + } | |
| 52 | + | |
| 53 | + public void bind (InetSocketAddress addr, int backlog) throws IOException { | |
| 54 | + server.bind (addr, backlog); | |
| 55 | + } | |
| 56 | + | |
| 57 | + public void start () { | |
| 58 | + server.start(); | |
| 59 | + } | |
| 60 | + | |
| 61 | + public void setExecutor (Executor executor) { | |
| 62 | + server.setExecutor(executor); | |
| 63 | + } | |
| 64 | + | |
| 65 | + public Executor getExecutor () { | |
| 66 | + return server.getExecutor(); | |
| 67 | + } | |
| 68 | + | |
| 69 | + public void stop (int delay) { | |
| 70 | + server.stop (delay); | |
| 71 | + } | |
| 72 | + | |
| 73 | + public HttpContextImpl createContext (String path, HttpHandler handler) { | |
| 74 | + return server.createContext (path, handler); | |
| 75 | + } | |
| 76 | + | |
| 77 | + public HttpContextImpl createContext (String path) { | |
| 78 | + return server.createContext (path); | |
| 79 | + } | |
| 80 | + | |
| 81 | + public void removeContext (String path) throws IllegalArgumentException { | |
| 82 | + server.removeContext (path); | |
| 83 | + } | |
| 84 | + | |
| 85 | + public void removeContext (HttpContext context) throws IllegalArgumentException { | |
| 86 | + server.removeContext (context); | |
| 87 | + } | |
| 88 | + | |
| 89 | + public InetSocketAddress getAddress() { | |
| 90 | + return server.getAddress(); | |
| 91 | + } | |
| 92 | +} | |
| @@ -0,0 +1,124 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import java.io.*; | |
| 29 | +import java.nio.*; | |
| 30 | +import java.nio.channels.*; | |
| 31 | +import java.net.*; | |
| 32 | +import javax.net.ssl.*; | |
| 33 | +import java.util.*; | |
| 34 | +import sun.net.www.MessageHeader; | |
| 35 | +import com.sun.net.httpserver.*; | |
| 36 | +import com.sun.net.httpserver.spi.*; | |
| 37 | + | |
| 38 | +class HttpsExchangeImpl extends HttpsExchange { | |
| 39 | + | |
| 40 | + ExchangeImpl impl; | |
| 41 | + | |
| 42 | + HttpsExchangeImpl (ExchangeImpl impl) throws IOException { | |
| 43 | + this.impl = impl; | |
| 44 | + } | |
| 45 | + | |
| 46 | + public Headers getRequestHeaders () { | |
| 47 | + return impl.getRequestHeaders(); | |
| 48 | + } | |
| 49 | + | |
| 50 | + public Headers getResponseHeaders () { | |
| 51 | + return impl.getResponseHeaders(); | |
| 52 | + } | |
| 53 | + | |
| 54 | + public URI getRequestURI () { | |
| 55 | + return impl.getRequestURI(); | |
| 56 | + } | |
| 57 | + | |
| 58 | + public String getRequestMethod (){ | |
| 59 | + return impl.getRequestMethod(); | |
| 60 | + } | |
| 61 | + | |
| 62 | + public HttpContextImpl getHttpContext (){ | |
| 63 | + return impl.getHttpContext(); | |
| 64 | + } | |
| 65 | + | |
| 66 | + public void close () { | |
| 67 | + impl.close(); | |
| 68 | + } | |
| 69 | + | |
| 70 | + public InputStream getRequestBody () { | |
| 71 | + return impl.getRequestBody(); | |
| 72 | + } | |
| 73 | + | |
| 74 | + public int getResponseCode () { | |
| 75 | + return impl.getResponseCode(); | |
| 76 | + } | |
| 77 | + | |
| 78 | + public OutputStream getResponseBody () { | |
| 79 | + return impl.getResponseBody(); | |
| 80 | + } | |
| 81 | + | |
| 82 | + | |
| 83 | + public void sendResponseHeaders (int rCode, long contentLen) | |
| 84 | + throws IOException | |
| 85 | + { | |
| 86 | + impl.sendResponseHeaders (rCode, contentLen); | |
| 87 | + } | |
| 88 | + | |
| 89 | + public InetSocketAddress getRemoteAddress (){ | |
| 90 | + return impl.getRemoteAddress(); | |
| 91 | + } | |
| 92 | + | |
| 93 | + public InetSocketAddress getLocalAddress (){ | |
| 94 | + return impl.getLocalAddress(); | |
| 95 | + } | |
| 96 | + | |
| 97 | + public String getProtocol (){ | |
| 98 | + return impl.getProtocol(); | |
| 99 | + } | |
| 100 | + | |
| 101 | + public SSLSession getSSLSession () { | |
| 102 | + return impl.getSSLSession (); | |
| 103 | + } | |
| 104 | + | |
| 105 | + public Object getAttribute (String name) { | |
| 106 | + return impl.getAttribute (name); | |
| 107 | + } | |
| 108 | + | |
| 109 | + public void setAttribute (String name, Object value) { | |
| 110 | + impl.setAttribute (name, value); | |
| 111 | + } | |
| 112 | + | |
| 113 | + public void setStreams (InputStream i, OutputStream o) { | |
| 114 | + impl.setStreams (i, o); | |
| 115 | + } | |
| 116 | + | |
| 117 | + public HttpPrincipal getPrincipal () { | |
| 118 | + return impl.getPrincipal(); | |
| 119 | + } | |
| 120 | + | |
| 121 | + ExchangeImpl getExchangeImpl () { | |
| 122 | + return impl; | |
| 123 | + } | |
| 124 | +} | |
| @@ -0,0 +1,100 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import java.net.*; | |
| 29 | +import java.io.*; | |
| 30 | +import java.nio.*; | |
| 31 | +import java.security.*; | |
| 32 | +import java.nio.channels.*; | |
| 33 | +import java.util.*; | |
| 34 | +import java.util.concurrent.*; | |
| 35 | +import javax.net.ssl.*; | |
| 36 | +import com.sun.net.httpserver.*; | |
| 37 | +import com.sun.net.httpserver.spi.*; | |
| 38 | + | |
| 39 | +public class HttpsServerImpl extends HttpsServer { | |
| 40 | + | |
| 41 | + ServerImpl server; | |
| 42 | + | |
| 43 | + HttpsServerImpl () throws IOException { | |
| 44 | + this (new InetSocketAddress(443), 0); | |
| 45 | + } | |
| 46 | + | |
| 47 | + HttpsServerImpl ( | |
| 48 | + InetSocketAddress addr, int backlog | |
| 49 | + ) throws IOException { | |
| 50 | + server = new ServerImpl (this, "https", addr, backlog); | |
| 51 | + } | |
| 52 | + | |
| 53 | + public void setHttpsConfigurator (HttpsConfigurator config) { | |
| 54 | + server.setHttpsConfigurator (config); | |
| 55 | + } | |
| 56 | + | |
| 57 | + public HttpsConfigurator getHttpsConfigurator () { | |
| 58 | + return server.getHttpsConfigurator(); | |
| 59 | + } | |
| 60 | + | |
| 61 | + public void bind (InetSocketAddress addr, int backlog) throws IOException { | |
| 62 | + server.bind (addr, backlog); | |
| 63 | + } | |
| 64 | + | |
| 65 | + public void start () { | |
| 66 | + server.start(); | |
| 67 | + } | |
| 68 | + | |
| 69 | + public void setExecutor (Executor executor) { | |
| 70 | + server.setExecutor(executor); | |
| 71 | + } | |
| 72 | + | |
| 73 | + public Executor getExecutor () { | |
| 74 | + return server.getExecutor(); | |
| 75 | + } | |
| 76 | + | |
| 77 | + public void stop (int delay) { | |
| 78 | + server.stop (delay); | |
| 79 | + } | |
| 80 | + | |
| 81 | + public HttpContextImpl createContext (String path, HttpHandler handler) { | |
| 82 | + return server.createContext (path, handler); | |
| 83 | + } | |
| 84 | + | |
| 85 | + public HttpContextImpl createContext (String path) { | |
| 86 | + return server.createContext (path); | |
| 87 | + } | |
| 88 | + | |
| 89 | + public void removeContext (String path) throws IllegalArgumentException { | |
| 90 | + server.removeContext (path); | |
| 91 | + } | |
| 92 | + | |
| 93 | + public void removeContext (HttpContext context) throws IllegalArgumentException { | |
| 94 | + server.removeContext (context); | |
| 95 | + } | |
| 96 | + | |
| 97 | + public InetSocketAddress getAddress() { | |
| 98 | + return server.getAddress(); | |
| 99 | + } | |
| 100 | +} | |
| @@ -0,0 +1,122 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, 2007, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import java.io.*; | |
| 29 | +import com.sun.net.httpserver.*; | |
| 30 | +import com.sun.net.httpserver.spi.*; | |
| 31 | + | |
| 32 | +/** | |
| 33 | + * a (filter) input stream which can tell us if bytes are "left over" | |
| 34 | + * on the underlying stream which can be read (without blocking) | |
| 35 | + * on another instance of this class. | |
| 36 | + * | |
| 37 | + * The class can also report if all bytes "expected" to be read | |
| 38 | + * were read, by the time close() was called. In that case, | |
| 39 | + * bytes may be drained to consume them (by calling drain() ). | |
| 40 | + * | |
| 41 | + * isEOF() returns true, when all expected bytes have been read | |
| 42 | + */ | |
| 43 | +abstract class LeftOverInputStream extends FilterInputStream { | |
| 44 | + ExchangeImpl t; | |
| 45 | + ServerImpl server; | |
| 46 | + protected boolean closed = false; | |
| 47 | + protected boolean eof = false; | |
| 48 | + byte[] one = new byte [1]; | |
| 49 | + | |
| 50 | + public LeftOverInputStream (ExchangeImpl t, InputStream src) { | |
| 51 | + super (src); | |
| 52 | + this.t = t; | |
| 53 | + this.server = t.getServerImpl(); | |
| 54 | + } | |
| 55 | + /** | |
| 56 | + * if bytes are left over buffered on *the UNDERLYING* stream | |
| 57 | + */ | |
| 58 | + public boolean isDataBuffered () throws IOException { | |
| 59 | + assert eof; | |
| 60 | + return super.available() > 0; | |
| 61 | + } | |
| 62 | + | |
| 63 | + public void close () throws IOException { | |
| 64 | + if (closed) { | |
| 65 | + return; | |
| 66 | + } | |
| 67 | + closed = true; | |
| 68 | + if (!eof) { | |
| 69 | + eof = drain (ServerConfig.getDrainAmount()); | |
| 70 | + } | |
| 71 | + } | |
| 72 | + | |
| 73 | + public boolean isClosed () { | |
| 74 | + return closed; | |
| 75 | + } | |
| 76 | + | |
| 77 | + public boolean isEOF () { | |
| 78 | + return eof; | |
| 79 | + } | |
| 80 | + | |
| 81 | + protected abstract int readImpl (byte[]b, int off, int len) throws IOException; | |
| 82 | + | |
| 83 | + public synchronized int read () throws IOException { | |
| 84 | + if (closed) { | |
| 85 | + throw new IOException ("Stream is closed"); | |
| 86 | + } | |
| 87 | + int c = readImpl (one, 0, 1); | |
| 88 | + if (c == -1 || c == 0) { | |
| 89 | + return c; | |
| 90 | + } else { | |
| 91 | + return one[0] & 0xFF; | |
| 92 | + } | |
| 93 | + } | |
| 94 | + | |
| 95 | + public synchronized int read (byte[]b, int off, int len) throws IOException { | |
| 96 | + if (closed) { | |
| 97 | + throw new IOException ("Stream is closed"); | |
| 98 | + } | |
| 99 | + return readImpl (b, off, len); | |
| 100 | + } | |
| 101 | + | |
| 102 | + /** | |
| 103 | + * read and discard up to l bytes or "eof" occurs, | |
| 104 | + * (whichever is first). Then return true if the stream | |
| 105 | + * is at eof (ie. all bytes were read) or false if not | |
| 106 | + * (still bytes to be read) | |
| 107 | + */ | |
| 108 | + public boolean drain (long l) throws IOException { | |
| 109 | + int bufSize = 2048; | |
| 110 | + byte[] db = new byte [bufSize]; | |
| 111 | + while (l > 0) { | |
| 112 | + long len = readImpl (db, 0, bufSize); | |
| 113 | + if (len == -1) { | |
| 114 | + eof = true; | |
| 115 | + return true; | |
| 116 | + } else { | |
| 117 | + l = l - len; | |
| 118 | + } | |
| 119 | + } | |
| 120 | + return false; | |
| 121 | + } | |
| 122 | +} | |
| @@ -0,0 +1,402 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import java.util.*; | |
| 29 | +import java.nio.*; | |
| 30 | +import java.net.*; | |
| 31 | +import java.io.*; | |
| 32 | +import java.nio.channels.*; | |
| 33 | +import com.sun.net.httpserver.*; | |
| 34 | +import com.sun.net.httpserver.spi.*; | |
| 35 | + | |
| 36 | +/** | |
| 37 | + */ | |
| 38 | +class Request { | |
| 39 | + | |
| 40 | + final static int BUF_LEN = 2048; | |
| 41 | + final static byte CR = 13; | |
| 42 | + final static byte LF = 10; | |
| 43 | + | |
| 44 | + private String startLine; | |
| 45 | + private SocketChannel chan; | |
| 46 | + private InputStream is; | |
| 47 | + private OutputStream os; | |
| 48 | + | |
| 49 | + Request (InputStream rawInputStream, OutputStream rawout) throws IOException { | |
| 50 | + this.chan = chan; | |
| 51 | + is = rawInputStream; | |
| 52 | + os = rawout; | |
| 53 | + do { | |
| 54 | + startLine = readLine(); | |
| 55 | + if (startLine == null) { | |
| 56 | + return; | |
| 57 | + } | |
| 58 | + /* skip blank lines */ | |
| 59 | + } while (startLine == null ? false : startLine.equals ("")); | |
| 60 | + } | |
| 61 | + | |
| 62 | + | |
| 63 | + char[] buf = new char [BUF_LEN]; | |
| 64 | + int pos; | |
| 65 | + StringBuffer lineBuf; | |
| 66 | + | |
| 67 | + public InputStream inputStream () { | |
| 68 | + return is; | |
| 69 | + } | |
| 70 | + | |
| 71 | + public OutputStream outputStream () { | |
| 72 | + return os; | |
| 73 | + } | |
| 74 | + | |
| 75 | + /** | |
| 76 | + * read a line from the stream returning as a String. | |
| 77 | + * Not used for reading headers. | |
| 78 | + */ | |
| 79 | + | |
| 80 | + public String readLine () throws IOException { | |
| 81 | + boolean gotCR = false, gotLF = false; | |
| 82 | + pos = 0; lineBuf = new StringBuffer(); | |
| 83 | + while (!gotLF) { | |
| 84 | + int c = is.read(); | |
| 85 | + if (c == -1) { | |
| 86 | + return null; | |
| 87 | + } | |
| 88 | + if (gotCR) { | |
| 89 | + if (c == LF) { | |
| 90 | + gotLF = true; | |
| 91 | + } else { | |
| 92 | + gotCR = false; | |
| 93 | + consume (CR); | |
| 94 | + consume (c); | |
| 95 | + } | |
| 96 | + } else { | |
| 97 | + if (c == CR) { | |
| 98 | + gotCR = true; | |
| 99 | + } else { | |
| 100 | + consume (c); | |
| 101 | + } | |
| 102 | + } | |
| 103 | + } | |
| 104 | + lineBuf.append (buf, 0, pos); | |
| 105 | + return new String (lineBuf); | |
| 106 | + } | |
| 107 | + | |
| 108 | + private void consume (int c) { | |
| 109 | + if (pos == BUF_LEN) { | |
| 110 | + lineBuf.append (buf); | |
| 111 | + pos = 0; | |
| 112 | + } | |
| 113 | + buf[pos++] = (char)c; | |
| 114 | + } | |
| 115 | + | |
| 116 | + /** | |
| 117 | + * returns the request line (first line of a request) | |
| 118 | + */ | |
| 119 | + public String requestLine () { | |
| 120 | + return startLine; | |
| 121 | + } | |
| 122 | + | |
| 123 | + Headers hdrs = null; | |
| 124 | + | |
| 125 | + Headers headers () throws IOException { | |
| 126 | + if (hdrs != null) { | |
| 127 | + return hdrs; | |
| 128 | + } | |
| 129 | + hdrs = new Headers(); | |
| 130 | + | |
| 131 | + char s[] = new char[10]; | |
| 132 | + int len = 0; | |
| 133 | + | |
| 134 | + int firstc = is.read(); | |
| 135 | + | |
| 136 | + // check for empty headers | |
| 137 | + if (firstc == CR || firstc == LF) { | |
| 138 | + int c = is.read(); | |
| 139 | + if (c == CR || c == LF) { | |
| 140 | + return hdrs; | |
| 141 | + } | |
| 142 | + s[0] = (char)firstc; | |
| 143 | + len = 1; | |
| 144 | + firstc = c; | |
| 145 | + } | |
| 146 | + | |
| 147 | + while (firstc != LF && firstc != CR && firstc >= 0) { | |
| 148 | + int keyend = -1; | |
| 149 | + int c; | |
| 150 | + boolean inKey = firstc > ' '; | |
| 151 | + s[len++] = (char) firstc; | |
| 152 | + parseloop:{ | |
| 153 | + while ((c = is.read()) >= 0) { | |
| 154 | + switch (c) { | |
| 155 | + case ':': | |
| 156 | + if (inKey && len > 0) | |
| 157 | + keyend = len; | |
| 158 | + inKey = false; | |
| 159 | + break; | |
| 160 | + case '\t': | |
| 161 | + c = ' '; | |
| 162 | + case ' ': | |
| 163 | + inKey = false; | |
| 164 | + break; | |
| 165 | + case CR: | |
| 166 | + case LF: | |
| 167 | + firstc = is.read(); | |
| 168 | + if (c == CR && firstc == LF) { | |
| 169 | + firstc = is.read(); | |
| 170 | + if (firstc == CR) | |
| 171 | + firstc = is.read(); | |
| 172 | + } | |
| 173 | + if (firstc == LF || firstc == CR || firstc > ' ') | |
| 174 | + break parseloop; | |
| 175 | + /* continuation */ | |
| 176 | + c = ' '; | |
| 177 | + break; | |
| 178 | + } | |
| 179 | + if (len >= s.length) { | |
| 180 | + char ns[] = new char[s.length * 2]; | |
| 181 | + System.arraycopy(s, 0, ns, 0, len); | |
| 182 | + s = ns; | |
| 183 | + } | |
| 184 | + s[len++] = (char) c; | |
| 185 | + } | |
| 186 | + firstc = -1; | |
| 187 | + } | |
| 188 | + while (len > 0 && s[len - 1] <= ' ') | |
| 189 | + len--; | |
| 190 | + String k; | |
| 191 | + if (keyend <= 0) { | |
| 192 | + k = null; | |
| 193 | + keyend = 0; | |
| 194 | + } else { | |
| 195 | + k = String.copyValueOf(s, 0, keyend); | |
| 196 | + if (keyend < len && s[keyend] == ':') | |
| 197 | + keyend++; | |
| 198 | + while (keyend < len && s[keyend] <= ' ') | |
| 199 | + keyend++; | |
| 200 | + } | |
| 201 | + String v; | |
| 202 | + if (keyend >= len) | |
| 203 | + v = new String(); | |
| 204 | + else | |
| 205 | + v = String.copyValueOf(s, keyend, len - keyend); | |
| 206 | + hdrs.add (k,v); | |
| 207 | + len = 0; | |
| 208 | + } | |
| 209 | + return hdrs; | |
| 210 | + } | |
| 211 | + | |
| 212 | + /** | |
| 213 | + * Implements blocking reading semantics on top of a non-blocking channel | |
| 214 | + */ | |
| 215 | + | |
| 216 | + static class ReadStream extends InputStream { | |
| 217 | + SocketChannel channel; | |
| 218 | + ByteBuffer chanbuf; | |
| 219 | + byte[] one; | |
| 220 | + private boolean closed = false, eof = false; | |
| 221 | + ByteBuffer markBuf; /* reads may be satisifed from this buffer */ | |
| 222 | + boolean marked; | |
| 223 | + boolean reset; | |
| 224 | + int readlimit; | |
| 225 | + static long readTimeout; | |
| 226 | + ServerImpl server; | |
| 227 | + final static int BUFSIZE = 8 * 1024; | |
| 228 | + | |
| 229 | + public ReadStream (ServerImpl server, SocketChannel chan) throws IOException { | |
| 230 | + this.channel = chan; | |
| 231 | + this.server = server; | |
| 232 | + chanbuf = ByteBuffer.allocate (BUFSIZE); | |
| 233 | + chanbuf.clear(); | |
| 234 | + one = new byte[1]; | |
| 235 | + closed = marked = reset = false; | |
| 236 | + } | |
| 237 | + | |
| 238 | + public synchronized int read (byte[] b) throws IOException { | |
| 239 | + return read (b, 0, b.length); | |
| 240 | + } | |
| 241 | + | |
| 242 | + public synchronized int read () throws IOException { | |
| 243 | + int result = read (one, 0, 1); | |
| 244 | + if (result == 1) { | |
| 245 | + return one[0] & 0xFF; | |
| 246 | + } else { | |
| 247 | + return -1; | |
| 248 | + } | |
| 249 | + } | |
| 250 | + | |
| 251 | + public synchronized int read (byte[] b, int off, int srclen) throws IOException { | |
| 252 | + | |
| 253 | + int canreturn, willreturn; | |
| 254 | + | |
| 255 | + if (closed) | |
| 256 | + throw new IOException ("Stream closed"); | |
| 257 | + | |
| 258 | + if (eof) { | |
| 259 | + return -1; | |
| 260 | + } | |
| 261 | + | |
| 262 | + assert channel.isBlocking(); | |
| 263 | + | |
| 264 | + if (off < 0 || srclen < 0|| srclen > (b.length-off)) { | |
| 265 | + throw new IndexOutOfBoundsException (); | |
| 266 | + } | |
| 267 | + | |
| 268 | + if (reset) { /* satisfy from markBuf */ | |
| 269 | + canreturn = markBuf.remaining (); | |
| 270 | + willreturn = canreturn>srclen ? srclen : canreturn; | |
| 271 | + markBuf.get(b, off, willreturn); | |
| 272 | + if (canreturn == willreturn) { | |
| 273 | + reset = false; | |
| 274 | + } | |
| 275 | + } else { /* satisfy from channel */ | |
| 276 | + chanbuf.clear (); | |
| 277 | + if (srclen < BUFSIZE) { | |
| 278 | + chanbuf.limit (srclen); | |
| 279 | + } | |
| 280 | + do { | |
| 281 | + willreturn = channel.read (chanbuf); | |
| 282 | + } while (willreturn == 0); | |
| 283 | + if (willreturn == -1) { | |
| 284 | + eof = true; | |
| 285 | + return -1; | |
| 286 | + } | |
| 287 | + chanbuf.flip (); | |
| 288 | + chanbuf.get(b, off, willreturn); | |
| 289 | + | |
| 290 | + if (marked) { /* copy into markBuf */ | |
| 291 | + try { | |
| 292 | + markBuf.put (b, off, willreturn); | |
| 293 | + } catch (BufferOverflowException e) { | |
| 294 | + marked = false; | |
| 295 | + } | |
| 296 | + } | |
| 297 | + } | |
| 298 | + return willreturn; | |
| 299 | + } | |
| 300 | + | |
| 301 | + public boolean markSupported () { | |
| 302 | + return true; | |
| 303 | + } | |
| 304 | + | |
| 305 | + /* Does not query the OS socket */ | |
| 306 | + public synchronized int available () throws IOException { | |
| 307 | + if (closed) | |
| 308 | + throw new IOException ("Stream is closed"); | |
| 309 | + | |
| 310 | + if (eof) | |
| 311 | + return -1; | |
| 312 | + | |
| 313 | + if (reset) | |
| 314 | + return markBuf.remaining(); | |
| 315 | + | |
| 316 | + return chanbuf.remaining(); | |
| 317 | + } | |
| 318 | + | |
| 319 | + public void close () throws IOException { | |
| 320 | + if (closed) { | |
| 321 | + return; | |
| 322 | + } | |
| 323 | + channel.close (); | |
| 324 | + closed = true; | |
| 325 | + } | |
| 326 | + | |
| 327 | + public synchronized void mark (int readlimit) { | |
| 328 | + if (closed) | |
| 329 | + return; | |
| 330 | + this.readlimit = readlimit; | |
| 331 | + markBuf = ByteBuffer.allocate (readlimit); | |
| 332 | + marked = true; | |
| 333 | + reset = false; | |
| 334 | + } | |
| 335 | + | |
| 336 | + public synchronized void reset () throws IOException { | |
| 337 | + if (closed ) | |
| 338 | + return; | |
| 339 | + if (!marked) | |
| 340 | + throw new IOException ("Stream not marked"); | |
| 341 | + marked = false; | |
| 342 | + reset = true; | |
| 343 | + markBuf.flip (); | |
| 344 | + } | |
| 345 | + } | |
| 346 | + | |
| 347 | + static class WriteStream extends java.io.OutputStream { | |
| 348 | + SocketChannel channel; | |
| 349 | + ByteBuffer buf; | |
| 350 | + SelectionKey key; | |
| 351 | + boolean closed; | |
| 352 | + byte[] one; | |
| 353 | + ServerImpl server; | |
| 354 | + | |
| 355 | + public WriteStream (ServerImpl server, SocketChannel channel) throws IOException { | |
| 356 | + this.channel = channel; | |
| 357 | + this.server = server; | |
| 358 | + assert channel.isBlocking(); | |
| 359 | + closed = false; | |
| 360 | + one = new byte [1]; | |
| 361 | + buf = ByteBuffer.allocate (4096); | |
| 362 | + } | |
| 363 | + | |
| 364 | + public synchronized void write (int b) throws IOException { | |
| 365 | + one[0] = (byte)b; | |
| 366 | + write (one, 0, 1); | |
| 367 | + } | |
| 368 | + | |
| 369 | + public synchronized void write (byte[] b) throws IOException { | |
| 370 | + write (b, 0, b.length); | |
| 371 | + } | |
| 372 | + | |
| 373 | + public synchronized void write (byte[] b, int off, int len) throws IOException { | |
| 374 | + int l = len; | |
| 375 | + if (closed) | |
| 376 | + throw new IOException ("stream is closed"); | |
| 377 | + | |
| 378 | + int cap = buf.capacity(); | |
| 379 | + if (cap < len) { | |
| 380 | + int diff = len - cap; | |
| 381 | + buf = ByteBuffer.allocate (2*(cap+diff)); | |
| 382 | + } | |
| 383 | + buf.clear(); | |
| 384 | + buf.put (b, off, len); | |
| 385 | + buf.flip (); | |
| 386 | + int n; | |
| 387 | + while ((n = channel.write (buf)) < l) { | |
| 388 | + l -= n; | |
| 389 | + if (l == 0) | |
| 390 | + return; | |
| 391 | + } | |
| 392 | + } | |
| 393 | + | |
| 394 | + public void close () throws IOException { | |
| 395 | + if (closed) | |
| 396 | + return; | |
| 397 | + //server.logStackTrace ("Request.OS.close: isOpen="+channel.isOpen()); | |
| 398 | + channel.close (); | |
| 399 | + closed = true; | |
| 400 | + } | |
| 401 | + } | |
| 402 | +} | |
| @@ -0,0 +1,662 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import java.net.*; | |
| 29 | +import java.nio.*; | |
| 30 | +import java.io.*; | |
| 31 | +import java.nio.channels.*; | |
| 32 | +import java.util.*; | |
| 33 | +import java.util.concurrent.*; | |
| 34 | +import java.util.concurrent.locks.*; | |
| 35 | +import javax.net.ssl.*; | |
| 36 | +import javax.net.ssl.SSLEngineResult.*; | |
| 37 | +import com.sun.net.httpserver.*; | |
| 38 | +import com.sun.net.httpserver.spi.*; | |
| 39 | + | |
| 40 | +/** | |
| 41 | + * given a non-blocking SocketChannel, it produces | |
| 42 | + * (blocking) streams which encrypt/decrypt the SSL content | |
| 43 | + * and handle the SSL handshaking automatically. | |
| 44 | + */ | |
| 45 | + | |
| 46 | +class SSLStreams { | |
| 47 | + | |
| 48 | + SSLContext sslctx; | |
| 49 | + SocketChannel chan; | |
| 50 | + TimeSource time; | |
| 51 | + ServerImpl server; | |
| 52 | + SSLEngine engine; | |
| 53 | + EngineWrapper wrapper; | |
| 54 | + OutputStream os; | |
| 55 | + InputStream is; | |
| 56 | + | |
| 57 | + /* held by thread doing the hand-shake on this connection */ | |
| 58 | + Lock handshaking = new ReentrantLock(); | |
| 59 | + | |
| 60 | + SSLStreams (ServerImpl server, SSLContext sslctx, SocketChannel chan) throws IOException { | |
| 61 | + this.server = server; | |
| 62 | + this.time= (TimeSource)server; | |
| 63 | + this.sslctx= sslctx; | |
| 64 | + this.chan= chan; | |
| 65 | + InetSocketAddress addr = | |
| 66 | + (InetSocketAddress)chan.socket().getRemoteSocketAddress(); | |
| 67 | + engine = sslctx.createSSLEngine (addr.getHostName(), addr.getPort()); | |
| 68 | + engine.setUseClientMode (false); | |
| 69 | + HttpsConfigurator cfg = server.getHttpsConfigurator(); | |
| 70 | + configureEngine (cfg, addr); | |
| 71 | + wrapper = new EngineWrapper (chan, engine); | |
| 72 | + } | |
| 73 | + | |
| 74 | + private void configureEngine(HttpsConfigurator cfg, InetSocketAddress addr){ | |
| 75 | + if (cfg != null) { | |
| 76 | + Parameters params = new Parameters (cfg, addr); | |
| 77 | +//BEGIN_TIGER_EXCLUDE | |
| 78 | + cfg.configure (params); | |
| 79 | + SSLParameters sslParams = params.getSSLParameters(); | |
| 80 | + if (sslParams != null) { | |
| 81 | + engine.setSSLParameters (sslParams); | |
| 82 | + } else | |
| 83 | +//END_TIGER_EXCLUDE | |
| 84 | + { | |
| 85 | + /* tiger compatibility */ | |
| 86 | + if (params.getCipherSuites() != null) { | |
| 87 | + try { | |
| 88 | + engine.setEnabledCipherSuites ( | |
| 89 | + params.getCipherSuites() | |
| 90 | + ); | |
| 91 | + } catch (IllegalArgumentException e) { /* LOG */} | |
| 92 | + } | |
| 93 | + engine.setNeedClientAuth (params.getNeedClientAuth()); | |
| 94 | + engine.setWantClientAuth (params.getWantClientAuth()); | |
| 95 | + if (params.getProtocols() != null) { | |
| 96 | + try { | |
| 97 | + engine.setEnabledProtocols ( | |
| 98 | + params.getProtocols() | |
| 99 | + ); | |
| 100 | + } catch (IllegalArgumentException e) { /* LOG */} | |
| 101 | + } | |
| 102 | + } | |
| 103 | + } | |
| 104 | + } | |
| 105 | + | |
| 106 | + class Parameters extends HttpsParameters { | |
| 107 | + InetSocketAddress addr; | |
| 108 | + HttpsConfigurator cfg; | |
| 109 | + | |
| 110 | + Parameters (HttpsConfigurator cfg, InetSocketAddress addr) { | |
| 111 | + this.addr = addr; | |
| 112 | + this.cfg = cfg; | |
| 113 | + } | |
| 114 | + public InetSocketAddress getClientAddress () { | |
| 115 | + return addr; | |
| 116 | + } | |
| 117 | + public HttpsConfigurator getHttpsConfigurator() { | |
| 118 | + return cfg; | |
| 119 | + } | |
| 120 | +//BEGIN_TIGER_EXCLUDE | |
| 121 | + SSLParameters params; | |
| 122 | + public void setSSLParameters (SSLParameters p) { | |
| 123 | + params = p; | |
| 124 | + } | |
| 125 | + SSLParameters getSSLParameters () { | |
| 126 | + return params; | |
| 127 | + } | |
| 128 | +//END_TIGER_EXCLUDE | |
| 129 | + } | |
| 130 | + | |
| 131 | + /** | |
| 132 | + * cleanup resources allocated inside this object | |
| 133 | + */ | |
| 134 | + void close () throws IOException { | |
| 135 | + wrapper.close(); | |
| 136 | + } | |
| 137 | + | |
| 138 | + /** | |
| 139 | + * return the SSL InputStream | |
| 140 | + */ | |
| 141 | + InputStream getInputStream () throws IOException { | |
| 142 | + if (is == null) { | |
| 143 | + is = new InputStream(); | |
| 144 | + } | |
| 145 | + return is; | |
| 146 | + } | |
| 147 | + | |
| 148 | + /** | |
| 149 | + * return the SSL OutputStream | |
| 150 | + */ | |
| 151 | + OutputStream getOutputStream () throws IOException { | |
| 152 | + if (os == null) { | |
| 153 | + os = new OutputStream(); | |
| 154 | + } | |
| 155 | + return os; | |
| 156 | + } | |
| 157 | + | |
| 158 | + SSLEngine getSSLEngine () { | |
| 159 | + return engine; | |
| 160 | + } | |
| 161 | + | |
| 162 | + /** | |
| 163 | + * request the engine to repeat the handshake on this session | |
| 164 | + * the handshake must be driven by reads/writes on the streams | |
| 165 | + * Normally, not necessary to call this. | |
| 166 | + */ | |
| 167 | + void beginHandshake() throws SSLException { | |
| 168 | + engine.beginHandshake(); | |
| 169 | + } | |
| 170 | + | |
| 171 | + class WrapperResult { | |
| 172 | + SSLEngineResult result; | |
| 173 | + | |
| 174 | + /* if passed in buffer was not big enough then the | |
| 175 | + * a reallocated buffer is returned here | |
| 176 | + */ | |
| 177 | + ByteBuffer buf; | |
| 178 | + } | |
| 179 | + | |
| 180 | + int app_buf_size; | |
| 181 | + int packet_buf_size; | |
| 182 | + | |
| 183 | + enum BufType { | |
| 184 | + PACKET, APPLICATION | |
| 185 | + }; | |
| 186 | + | |
| 187 | + private ByteBuffer allocate (BufType type) { | |
| 188 | + return allocate (type, -1); | |
| 189 | + } | |
| 190 | + | |
| 191 | + private ByteBuffer allocate (BufType type, int len) { | |
| 192 | + assert engine != null; | |
| 193 | + synchronized (this) { | |
| 194 | + int size; | |
| 195 | + if (type == BufType.PACKET) { | |
| 196 | + if (packet_buf_size == 0) { | |
| 197 | + SSLSession sess = engine.getSession(); | |
| 198 | + packet_buf_size = sess.getPacketBufferSize(); | |
| 199 | + } | |
| 200 | + if (len > packet_buf_size) { | |
| 201 | + packet_buf_size = len; | |
| 202 | + } | |
| 203 | + size = packet_buf_size; | |
| 204 | + } else { | |
| 205 | + if (app_buf_size == 0) { | |
| 206 | + SSLSession sess = engine.getSession(); | |
| 207 | + app_buf_size = sess.getApplicationBufferSize(); | |
| 208 | + } | |
| 209 | + if (len > app_buf_size) { | |
| 210 | + app_buf_size = len; | |
| 211 | + } | |
| 212 | + size = app_buf_size; | |
| 213 | + } | |
| 214 | + return ByteBuffer.allocate (size); | |
| 215 | + } | |
| 216 | + } | |
| 217 | + | |
| 218 | + /* reallocates the buffer by :- | |
| 219 | + * 1. creating a new buffer double the size of the old one | |
| 220 | + * 2. putting the contents of the old buffer into the new one | |
| 221 | + * 3. set xx_buf_size to the new size if it was smaller than new size | |
| 222 | + * | |
| 223 | + * flip is set to true if the old buffer needs to be flipped | |
| 224 | + * before it is copied. | |
| 225 | + */ | |
| 226 | + private ByteBuffer realloc (ByteBuffer b, boolean flip, BufType type) { | |
| 227 | + synchronized (this) { | |
| 228 | + int nsize = 2 * b.capacity(); | |
| 229 | + ByteBuffer n = allocate (type, nsize); | |
| 230 | + if (flip) { | |
| 231 | + b.flip(); | |
| 232 | + } | |
| 233 | + n.put(b); | |
| 234 | + b = n; | |
| 235 | + } | |
| 236 | + return b; | |
| 237 | + } | |
| 238 | + /** | |
| 239 | + * This is a thin wrapper over SSLEngine and the SocketChannel, | |
| 240 | + * which guarantees the ordering of wraps/unwraps with respect to the underlying | |
| 241 | + * channel read/writes. It handles the UNDER/OVERFLOW status codes | |
| 242 | + * It does not handle the handshaking status codes, or the CLOSED status code | |
| 243 | + * though once the engine is closed, any attempt to read/write to it | |
| 244 | + * will get an exception. The overall result is returned. | |
| 245 | + * It functions synchronously/blocking | |
| 246 | + */ | |
| 247 | + class EngineWrapper { | |
| 248 | + | |
| 249 | + SocketChannel chan; | |
| 250 | + SSLEngine engine; | |
| 251 | + Object wrapLock, unwrapLock; | |
| 252 | + ByteBuffer unwrap_src, wrap_dst; | |
| 253 | + boolean closed = false; | |
| 254 | + int u_remaining; // the number of bytes left in unwrap_src after an unwrap() | |
| 255 | + | |
| 256 | + EngineWrapper (SocketChannel chan, SSLEngine engine) throws IOException { | |
| 257 | + this.chan = chan; | |
| 258 | + this.engine = engine; | |
| 259 | + wrapLock = new Object(); | |
| 260 | + unwrapLock = new Object(); | |
| 261 | + unwrap_src = allocate(BufType.PACKET); | |
| 262 | + wrap_dst = allocate(BufType.PACKET); | |
| 263 | + } | |
| 264 | + | |
| 265 | + void close () throws IOException { | |
| 266 | + } | |
| 267 | + | |
| 268 | + /* try to wrap and send the data in src. Handles OVERFLOW. | |
| 269 | + * Might block if there is an outbound blockage or if another | |
| 270 | + * thread is calling wrap(). Also, might not send any data | |
| 271 | + * if an unwrap is needed. | |
| 272 | + */ | |
| 273 | + WrapperResult wrapAndSend(ByteBuffer src) throws IOException { | |
| 274 | + return wrapAndSendX(src, false); | |
| 275 | + } | |
| 276 | + | |
| 277 | + WrapperResult wrapAndSendX(ByteBuffer src, boolean ignoreClose) throws IOException { | |
| 278 | + if (closed && !ignoreClose) { | |
| 279 | + throw new IOException ("Engine is closed"); | |
| 280 | + } | |
| 281 | + Status status; | |
| 282 | + WrapperResult r = new WrapperResult(); | |
| 283 | + synchronized (wrapLock) { | |
| 284 | + wrap_dst.clear(); | |
| 285 | + do { | |
| 286 | + r.result = engine.wrap (src, wrap_dst); | |
| 287 | + status = r.result.getStatus(); | |
| 288 | + if (status == Status.BUFFER_OVERFLOW) { | |
| 289 | + wrap_dst = realloc (wrap_dst, true, BufType.PACKET); | |
| 290 | + } | |
| 291 | + } while (status == Status.BUFFER_OVERFLOW); | |
| 292 | + if (status == Status.CLOSED && !ignoreClose) { | |
| 293 | + closed = true; | |
| 294 | + return r; | |
| 295 | + } | |
| 296 | + if (r.result.bytesProduced() > 0) { | |
| 297 | + wrap_dst.flip(); | |
| 298 | + int l = wrap_dst.remaining(); | |
| 299 | + assert l == r.result.bytesProduced(); | |
| 300 | + while (l>0) { | |
| 301 | + l -= chan.write (wrap_dst); | |
| 302 | + } | |
| 303 | + } | |
| 304 | + } | |
| 305 | + return r; | |
| 306 | + } | |
| 307 | + | |
| 308 | + /* block until a complete message is available and return it | |
| 309 | + * in dst, together with the Result. dst may have been re-allocated | |
| 310 | + * so caller should check the returned value in Result | |
| 311 | + * If handshaking is in progress then, possibly no data is returned | |
| 312 | + */ | |
| 313 | + WrapperResult recvAndUnwrap(ByteBuffer dst) throws IOException { | |
| 314 | + Status status = Status.OK; | |
| 315 | + WrapperResult r = new WrapperResult(); | |
| 316 | + r.buf = dst; | |
| 317 | + if (closed) { | |
| 318 | + throw new IOException ("Engine is closed"); | |
| 319 | + } | |
| 320 | + boolean needData; | |
| 321 | + if (u_remaining > 0) { | |
| 322 | + unwrap_src.compact(); | |
| 323 | + unwrap_src.flip(); | |
| 324 | + needData = false; | |
| 325 | + } else { | |
| 326 | + unwrap_src.clear(); | |
| 327 | + needData = true; | |
| 328 | + } | |
| 329 | + synchronized (unwrapLock) { | |
| 330 | + int x; | |
| 331 | + do { | |
| 332 | + if (needData) { | |
| 333 | + do { | |
| 334 | + x = chan.read (unwrap_src); | |
| 335 | + } while (x == 0); | |
| 336 | + if (x == -1) { | |
| 337 | + throw new IOException ("connection closed for reading"); | |
| 338 | + } | |
| 339 | + unwrap_src.flip(); | |
| 340 | + } | |
| 341 | + r.result = engine.unwrap (unwrap_src, r.buf); | |
| 342 | + status = r.result.getStatus(); | |
| 343 | + if (status == Status.BUFFER_UNDERFLOW) { | |
| 344 | + if (unwrap_src.limit() == unwrap_src.capacity()) { | |
| 345 | + /* buffer not big enough */ | |
| 346 | + unwrap_src = realloc ( | |
| 347 | + unwrap_src, false, BufType.PACKET | |
| 348 | + ); | |
| 349 | + } else { | |
| 350 | + /* Buffer not full, just need to read more | |
| 351 | + * data off the channel. Reset pointers | |
| 352 | + * for reading off SocketChannel | |
| 353 | + */ | |
| 354 | + unwrap_src.position (unwrap_src.limit()); | |
| 355 | + unwrap_src.limit (unwrap_src.capacity()); | |
| 356 | + } | |
| 357 | + needData = true; | |
| 358 | + } else if (status == Status.BUFFER_OVERFLOW) { | |
| 359 | + r.buf = realloc (r.buf, true, BufType.APPLICATION); | |
| 360 | + needData = false; | |
| 361 | + } else if (status == Status.CLOSED) { | |
| 362 | + closed = true; | |
| 363 | + r.buf.flip(); | |
| 364 | + return r; | |
| 365 | + } | |
| 366 | + } while (status != Status.OK); | |
| 367 | + } | |
| 368 | + u_remaining = unwrap_src.remaining(); | |
| 369 | + return r; | |
| 370 | + } | |
| 371 | + } | |
| 372 | + | |
| 373 | + /** | |
| 374 | + * send the data in the given ByteBuffer. If a handshake is needed | |
| 375 | + * then this is handled within this method. When this call returns, | |
| 376 | + * all of the given user data has been sent and any handshake has been | |
| 377 | + * completed. Caller should check if engine has been closed. | |
| 378 | + */ | |
| 379 | + public WrapperResult sendData (ByteBuffer src) throws IOException { | |
| 380 | + WrapperResult r=null; | |
| 381 | + while (src.remaining() > 0) { | |
| 382 | + r = wrapper.wrapAndSend(src); | |
| 383 | + Status status = r.result.getStatus(); | |
| 384 | + if (status == Status.CLOSED) { | |
| 385 | + doClosure (); | |
| 386 | + return r; | |
| 387 | + } | |
| 388 | + HandshakeStatus hs_status = r.result.getHandshakeStatus(); | |
| 389 | + if (hs_status != HandshakeStatus.FINISHED && | |
| 390 | + hs_status != HandshakeStatus.NOT_HANDSHAKING) | |
| 391 | + { | |
| 392 | + doHandshake(hs_status); | |
| 393 | + } | |
| 394 | + } | |
| 395 | + return r; | |
| 396 | + } | |
| 397 | + | |
| 398 | + /** | |
| 399 | + * read data thru the engine into the given ByteBuffer. If the | |
| 400 | + * given buffer was not large enough, a new one is allocated | |
| 401 | + * and returned. This call handles handshaking automatically. | |
| 402 | + * Caller should check if engine has been closed. | |
| 403 | + */ | |
| 404 | + public WrapperResult recvData (ByteBuffer dst) throws IOException { | |
| 405 | + /* we wait until some user data arrives */ | |
| 406 | + WrapperResult r = null; | |
| 407 | + assert dst.position() == 0; | |
| 408 | + while (dst.position() == 0) { | |
| 409 | + r = wrapper.recvAndUnwrap (dst); | |
| 410 | + dst = (r.buf != dst) ? r.buf: dst; | |
| 411 | + Status status = r.result.getStatus(); | |
| 412 | + if (status == Status.CLOSED) { | |
| 413 | + doClosure (); | |
| 414 | + return r; | |
| 415 | + } | |
| 416 | + | |
| 417 | + HandshakeStatus hs_status = r.result.getHandshakeStatus(); | |
| 418 | + if (hs_status != HandshakeStatus.FINISHED && | |
| 419 | + hs_status != HandshakeStatus.NOT_HANDSHAKING) | |
| 420 | + { | |
| 421 | + doHandshake (hs_status); | |
| 422 | + } | |
| 423 | + } | |
| 424 | + dst.flip(); | |
| 425 | + return r; | |
| 426 | + } | |
| 427 | + | |
| 428 | + /* we've received a close notify. Need to call wrap to send | |
| 429 | + * the response | |
| 430 | + */ | |
| 431 | + void doClosure () throws IOException { | |
| 432 | + try { | |
| 433 | + handshaking.lock(); | |
| 434 | + ByteBuffer tmp = allocate(BufType.APPLICATION); | |
| 435 | + WrapperResult r; | |
| 436 | + do { | |
| 437 | + tmp.clear(); | |
| 438 | + tmp.flip (); | |
| 439 | + r = wrapper.wrapAndSendX (tmp, true); | |
| 440 | + } while (r.result.getStatus() != Status.CLOSED); | |
| 441 | + } finally { | |
| 442 | + handshaking.unlock(); | |
| 443 | + } | |
| 444 | + } | |
| 445 | + | |
| 446 | + /* do the (complete) handshake after acquiring the handshake lock. | |
| 447 | + * If two threads call this at the same time, then we depend | |
| 448 | + * on the wrapper methods being idempotent. eg. if wrapAndSend() | |
| 449 | + * is called with no data to send then there must be no problem | |
| 450 | + */ | |
| 451 | + void doHandshake (HandshakeStatus hs_status) throws IOException { | |
| 452 | + try { | |
| 453 | + handshaking.lock(); | |
| 454 | + ByteBuffer tmp = allocate(BufType.APPLICATION); | |
| 455 | + while (hs_status != HandshakeStatus.FINISHED && | |
| 456 | + hs_status != HandshakeStatus.NOT_HANDSHAKING) | |
| 457 | + { | |
| 458 | + WrapperResult r = null; | |
| 459 | + switch (hs_status) { | |
| 460 | + case NEED_TASK: | |
| 461 | + Runnable task; | |
| 462 | + while ((task = engine.getDelegatedTask()) != null) { | |
| 463 | + /* run in current thread, because we are already | |
| 464 | + * running an external Executor | |
| 465 | + */ | |
| 466 | + task.run(); | |
| 467 | + } | |
| 468 | + /* fall thru - call wrap again */ | |
| 469 | + case NEED_WRAP: | |
| 470 | + tmp.clear(); | |
| 471 | + tmp.flip(); | |
| 472 | + r = wrapper.wrapAndSend(tmp); | |
| 473 | + break; | |
| 474 | + | |
| 475 | + case NEED_UNWRAP: | |
| 476 | + tmp.clear(); | |
| 477 | + r = wrapper.recvAndUnwrap (tmp); | |
| 478 | + if (r.buf != tmp) { | |
| 479 | + tmp = r.buf; | |
| 480 | + } | |
| 481 | + assert tmp.position() == 0; | |
| 482 | + break; | |
| 483 | + } | |
| 484 | + hs_status = r.result.getHandshakeStatus(); | |
| 485 | + } | |
| 486 | + } finally { | |
| 487 | + handshaking.unlock(); | |
| 488 | + } | |
| 489 | + } | |
| 490 | + | |
| 491 | + /** | |
| 492 | + * represents an SSL input stream. Multiple https requests can | |
| 493 | + * be sent over one stream. closing this stream causes an SSL close | |
| 494 | + * input. | |
| 495 | + */ | |
| 496 | + class InputStream extends java.io.InputStream { | |
| 497 | + | |
| 498 | + ByteBuffer bbuf; | |
| 499 | + boolean closed = false; | |
| 500 | + | |
| 501 | + /* this stream eof */ | |
| 502 | + boolean eof = false; | |
| 503 | + | |
| 504 | + boolean needData = true; | |
| 505 | + | |
| 506 | + InputStream () { | |
| 507 | + bbuf = allocate (BufType.APPLICATION); | |
| 508 | + } | |
| 509 | + | |
| 510 | + public int read (byte[] buf, int off, int len) throws IOException { | |
| 511 | + if (closed) { | |
| 512 | + throw new IOException ("SSL stream is closed"); | |
| 513 | + } | |
| 514 | + if (eof) { | |
| 515 | + return 0; | |
| 516 | + } | |
| 517 | + int available=0; | |
| 518 | + if (!needData) { | |
| 519 | + available = bbuf.remaining(); | |
| 520 | + needData = (available==0); | |
| 521 | + } | |
| 522 | + if (needData) { | |
| 523 | + bbuf.clear(); | |
| 524 | + WrapperResult r = recvData (bbuf); | |
| 525 | + bbuf = r.buf== bbuf? bbuf: r.buf; | |
| 526 | + if ((available=bbuf.remaining()) == 0) { | |
| 527 | + eof = true; | |
| 528 | + return 0; | |
| 529 | + } else { | |
| 530 | + needData = false; | |
| 531 | + } | |
| 532 | + } | |
| 533 | + /* copy as much as possible from buf into users buf */ | |
| 534 | + if (len > available) { | |
| 535 | + len = available; | |
| 536 | + } | |
| 537 | + bbuf.get (buf, off, len); | |
| 538 | + return len; | |
| 539 | + } | |
| 540 | + | |
| 541 | + public int available () throws IOException { | |
| 542 | + return bbuf.remaining(); | |
| 543 | + } | |
| 544 | + | |
| 545 | + public boolean markSupported () { | |
| 546 | + return false; /* not possible with SSLEngine */ | |
| 547 | + } | |
| 548 | + | |
| 549 | + public void reset () throws IOException { | |
| 550 | + throw new IOException ("mark/reset not supported"); | |
| 551 | + } | |
| 552 | + | |
| 553 | + public long skip (long s) throws IOException { | |
| 554 | + int n = (int)s; | |
| 555 | + if (closed) { | |
| 556 | + throw new IOException ("SSL stream is closed"); | |
| 557 | + } | |
| 558 | + if (eof) { | |
| 559 | + return 0; | |
| 560 | + } | |
| 561 | + int ret = n; | |
| 562 | + while (n > 0) { | |
| 563 | + if (bbuf.remaining() >= n) { | |
| 564 | + bbuf.position (bbuf.position()+n); | |
| 565 | + return ret; | |
| 566 | + } else { | |
| 567 | + n -= bbuf.remaining(); | |
| 568 | + bbuf.clear(); | |
| 569 | + WrapperResult r = recvData (bbuf); | |
| 570 | + bbuf = r.buf==bbuf? bbuf: r.buf; | |
| 571 | + } | |
| 572 | + } | |
| 573 | + return ret; /* not reached */ | |
| 574 | + } | |
| 575 | + | |
| 576 | + /** | |
| 577 | + * close the SSL connection. All data must have been consumed | |
| 578 | + * before this is called. Otherwise an exception will be thrown. | |
| 579 | + * [Note. May need to revisit this. not quite the normal close() symantics | |
| 580 | + */ | |
| 581 | + public void close () throws IOException { | |
| 582 | + eof = true; | |
| 583 | + engine.closeInbound (); | |
| 584 | + } | |
| 585 | + | |
| 586 | + public int read (byte[] buf) throws IOException { | |
| 587 | + return read (buf, 0, buf.length); | |
| 588 | + } | |
| 589 | + | |
| 590 | + byte single[] = new byte [1]; | |
| 591 | + | |
| 592 | + public int read () throws IOException { | |
| 593 | + int n = read (single, 0, 1); | |
| 594 | + if (n == 0) { | |
| 595 | + return -1; | |
| 596 | + } else { | |
| 597 | + return single[0] & 0xFF; | |
| 598 | + } | |
| 599 | + } | |
| 600 | + } | |
| 601 | + | |
| 602 | + /** | |
| 603 | + * represents an SSL output stream. plain text data written to this stream | |
| 604 | + * is encrypted by the stream. Multiple HTTPS responses can be sent on | |
| 605 | + * one stream. closing this stream initiates an SSL closure | |
| 606 | + */ | |
| 607 | + class OutputStream extends java.io.OutputStream { | |
| 608 | + ByteBuffer buf; | |
| 609 | + boolean closed = false; | |
| 610 | + byte single[] = new byte[1]; | |
| 611 | + | |
| 612 | + OutputStream() { | |
| 613 | + buf = allocate(BufType.APPLICATION); | |
| 614 | + } | |
| 615 | + | |
| 616 | + public void write(int b) throws IOException { | |
| 617 | + single[0] = (byte)b; | |
| 618 | + write (single, 0, 1); | |
| 619 | + } | |
| 620 | + | |
| 621 | + public void write(byte b[]) throws IOException { | |
| 622 | + write (b, 0, b.length); | |
| 623 | + } | |
| 624 | + public void write(byte b[], int off, int len) throws IOException { | |
| 625 | + if (closed) { | |
| 626 | + throw new IOException ("output stream is closed"); | |
| 627 | + } | |
| 628 | + while (len > 0) { | |
| 629 | + int l = len > buf.capacity() ? buf.capacity() : len; | |
| 630 | + buf.clear(); | |
| 631 | + buf.put (b, off, l); | |
| 632 | + len -= l; | |
| 633 | + off += l; | |
| 634 | + buf.flip(); | |
| 635 | + WrapperResult r = sendData (buf); | |
| 636 | + if (r.result.getStatus() == Status.CLOSED) { | |
| 637 | + closed = true; | |
| 638 | + if (len > 0) { | |
| 639 | + throw new IOException ("output stream is closed"); | |
| 640 | + } | |
| 641 | + } | |
| 642 | + } | |
| 643 | + } | |
| 644 | + | |
| 645 | + public void flush() throws IOException { | |
| 646 | + /* no-op */ | |
| 647 | + } | |
| 648 | + | |
| 649 | + public void close() throws IOException { | |
| 650 | + WrapperResult r=null; | |
| 651 | + engine.closeOutbound(); | |
| 652 | + closed = true; | |
| 653 | + HandshakeStatus stat = HandshakeStatus.NEED_WRAP; | |
| 654 | + buf.clear(); | |
| 655 | + while (stat == HandshakeStatus.NEED_WRAP) { | |
| 656 | + r = wrapper.wrapAndSend (buf); | |
| 657 | + stat = r.result.getHandshakeStatus(); | |
| 658 | + } | |
| 659 | + assert r.result.getStatus() == Status.CLOSED; | |
| 660 | + } | |
| 661 | + } | |
| 662 | +} | |
| @@ -0,0 +1,175 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import com.sun.net.httpserver.*; | |
| 29 | +import com.sun.net.httpserver.spi.*; | |
| 30 | +import java.util.logging.Logger; | |
| 31 | +import java.security.PrivilegedAction; | |
| 32 | + | |
| 33 | +/** | |
| 34 | + * Parameters that users will not likely need to set | |
| 35 | + * but are useful for debugging | |
| 36 | + */ | |
| 37 | + | |
| 38 | +class ServerConfig { | |
| 39 | + | |
| 40 | + static int clockTick; | |
| 41 | + | |
| 42 | + static final int DEFAULT_CLOCK_TICK = 10000 ; // 10 sec. | |
| 43 | + | |
| 44 | + /* These values must be a reasonable multiple of clockTick */ | |
| 45 | + static final long DEFAULT_IDLE_INTERVAL = 30 ; // 5 min | |
| 46 | + static final int DEFAULT_MAX_IDLE_CONNECTIONS = 200 ; | |
| 47 | + | |
| 48 | + static final long DEFAULT_MAX_REQ_TIME = -1; // default: forever | |
| 49 | + static final long DEFAULT_MAX_RSP_TIME = -1; // default: forever | |
| 50 | + static final long DEFAULT_TIMER_MILLIS = 1000; | |
| 51 | + | |
| 52 | + static final long DEFAULT_DRAIN_AMOUNT = 64 * 1024; | |
| 53 | + | |
| 54 | + static long idleInterval; | |
| 55 | + static long drainAmount; // max # of bytes to drain from an inputstream | |
| 56 | + static int maxIdleConnections; | |
| 57 | + | |
| 58 | + // max time a request or response is allowed to take | |
| 59 | + static long maxReqTime; | |
| 60 | + static long maxRspTime; | |
| 61 | + static long timerMillis; | |
| 62 | + static boolean debug = false; | |
| 63 | + | |
| 64 | + static { | |
| 65 | + | |
| 66 | + idleInterval = ((Long)java.security.AccessController.doPrivileged( | |
| 67 | + new sun.security.action.GetLongAction( | |
| 68 | + "sun.net.httpserver.idleInterval", | |
| 69 | + DEFAULT_IDLE_INTERVAL))).longValue() * 1000; | |
| 70 | + | |
| 71 | + clockTick = ((Integer)java.security.AccessController.doPrivileged( | |
| 72 | + new sun.security.action.GetIntegerAction( | |
| 73 | + "sun.net.httpserver.clockTick", | |
| 74 | + DEFAULT_CLOCK_TICK))).intValue(); | |
| 75 | + | |
| 76 | + maxIdleConnections = ((Integer)java.security.AccessController.doPrivileged( | |
| 77 | + new sun.security.action.GetIntegerAction( | |
| 78 | + "sun.net.httpserver.maxIdleConnections", | |
| 79 | + DEFAULT_MAX_IDLE_CONNECTIONS))).intValue(); | |
| 80 | + | |
| 81 | + drainAmount = ((Long)java.security.AccessController.doPrivileged( | |
| 82 | + new sun.security.action.GetLongAction( | |
| 83 | + "sun.net.httpserver.drainAmount", | |
| 84 | + DEFAULT_DRAIN_AMOUNT))).longValue(); | |
| 85 | + | |
| 86 | + maxReqTime = ((Long)java.security.AccessController.doPrivileged( | |
| 87 | + new sun.security.action.GetLongAction( | |
| 88 | + "sun.net.httpserver.maxReqTime", | |
| 89 | + DEFAULT_MAX_REQ_TIME))).longValue(); | |
| 90 | + | |
| 91 | + maxRspTime = ((Long)java.security.AccessController.doPrivileged( | |
| 92 | + new sun.security.action.GetLongAction( | |
| 93 | + "sun.net.httpserver.maxRspTime", | |
| 94 | + DEFAULT_MAX_RSP_TIME))).longValue(); | |
| 95 | + | |
| 96 | + timerMillis = ((Long)java.security.AccessController.doPrivileged( | |
| 97 | + new sun.security.action.GetLongAction( | |
| 98 | + "sun.net.httpserver.timerMillis", | |
| 99 | + DEFAULT_TIMER_MILLIS))).longValue(); | |
| 100 | + | |
| 101 | + debug = ((Boolean)java.security.AccessController.doPrivileged( | |
| 102 | + new sun.security.action.GetBooleanAction( | |
| 103 | + "sun.net.httpserver.debug"))).booleanValue(); | |
| 104 | + } | |
| 105 | + | |
| 106 | + | |
| 107 | + static void checkLegacyProperties (final Logger logger) { | |
| 108 | + | |
| 109 | + // legacy properties that are no longer used | |
| 110 | + // print a warning to logger if they are set. | |
| 111 | + | |
| 112 | + java.security.AccessController.doPrivileged( | |
| 113 | + new PrivilegedAction<Void>() { | |
| 114 | + public Void run () { | |
| 115 | + if (System.getProperty("sun.net.httpserver.readTimeout") | |
| 116 | + !=null) | |
| 117 | + { | |
| 118 | + logger.warning ("sun.net.httpserver.readTimeout "+ | |
| 119 | + "property is no longer used. "+ | |
| 120 | + "Use sun.net.httpserver.maxReqTime instead." | |
| 121 | + ); | |
| 122 | + } | |
| 123 | + if (System.getProperty("sun.net.httpserver.writeTimeout") | |
| 124 | + !=null) | |
| 125 | + { | |
| 126 | + logger.warning ("sun.net.httpserver.writeTimeout "+ | |
| 127 | + "property is no longer used. Use "+ | |
| 128 | + "sun.net.httpserver.maxRspTime instead." | |
| 129 | + ); | |
| 130 | + } | |
| 131 | + if (System.getProperty("sun.net.httpserver.selCacheTimeout") | |
| 132 | + !=null) | |
| 133 | + { | |
| 134 | + logger.warning ("sun.net.httpserver.selCacheTimeout "+ | |
| 135 | + "property is no longer used." | |
| 136 | + ); | |
| 137 | + } | |
| 138 | + return null; | |
| 139 | + } | |
| 140 | + } | |
| 141 | + ); | |
| 142 | + } | |
| 143 | + | |
| 144 | + static boolean debugEnabled () { | |
| 145 | + return debug; | |
| 146 | + } | |
| 147 | + | |
| 148 | + static long getIdleInterval () { | |
| 149 | + return idleInterval; | |
| 150 | + } | |
| 151 | + | |
| 152 | + static int getClockTick () { | |
| 153 | + return clockTick; | |
| 154 | + } | |
| 155 | + | |
| 156 | + static int getMaxIdleConnections () { | |
| 157 | + return maxIdleConnections; | |
| 158 | + } | |
| 159 | + | |
| 160 | + static long getDrainAmount () { | |
| 161 | + return drainAmount; | |
| 162 | + } | |
| 163 | + | |
| 164 | + static long getMaxReqTime () { | |
| 165 | + return maxReqTime; | |
| 166 | + } | |
| 167 | + | |
| 168 | + static long getMaxRspTime () { | |
| 169 | + return maxRspTime; | |
| 170 | + } | |
| 171 | + | |
| 172 | + static long getTimerMillis () { | |
| 173 | + return timerMillis; | |
| 174 | + } | |
| 175 | +} | |
| @@ -0,0 +1,870 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import java.net.*; | |
| 29 | +import java.io.*; | |
| 30 | +import java.nio.*; | |
| 31 | +import java.security.*; | |
| 32 | +import java.nio.channels.*; | |
| 33 | +import java.util.*; | |
| 34 | +import java.util.concurrent.*; | |
| 35 | +import java.util.logging.Logger; | |
| 36 | +import java.util.logging.Level; | |
| 37 | +import javax.net.ssl.*; | |
| 38 | +import com.sun.net.httpserver.*; | |
| 39 | +import com.sun.net.httpserver.spi.*; | |
| 40 | +import sun.net.httpserver.HttpConnection.State; | |
| 41 | + | |
| 42 | +/** | |
| 43 | + * Provides implementation for both HTTP and HTTPS | |
| 44 | + */ | |
| 45 | +class ServerImpl implements TimeSource { | |
| 46 | + | |
| 47 | + private String protocol; | |
| 48 | + private boolean https; | |
| 49 | + private Executor executor; | |
| 50 | + private HttpsConfigurator httpsConfig; | |
| 51 | + private SSLContext sslContext; | |
| 52 | + private ContextList contexts; | |
| 53 | + private InetSocketAddress address; | |
| 54 | + private ServerSocketChannel schan; | |
| 55 | + private Selector selector; | |
| 56 | + private SelectionKey listenerKey; | |
| 57 | + private Set<HttpConnection> idleConnections; | |
| 58 | + private Set<HttpConnection> allConnections; | |
| 59 | + /* following two are used to keep track of the times | |
| 60 | + * when a connection/request is first received | |
| 61 | + * and when we start to send the response | |
| 62 | + */ | |
| 63 | + private Set<HttpConnection> reqConnections; | |
| 64 | + private Set<HttpConnection> rspConnections; | |
| 65 | + private List<Event> events; | |
| 66 | + private Object lolock = new Object(); | |
| 67 | + private volatile boolean finished = false; | |
| 68 | + private volatile boolean terminating = false; | |
| 69 | + private boolean bound = false; | |
| 70 | + private boolean started = false; | |
| 71 | + private volatile long time; /* current time */ | |
| 72 | + private volatile long subticks = 0; | |
| 73 | + private volatile long ticks; /* number of clock ticks since server started */ | |
| 74 | + private HttpServer wrapper; | |
| 75 | + | |
| 76 | + final static int CLOCK_TICK = ServerConfig.getClockTick(); | |
| 77 | + final static long IDLE_INTERVAL = ServerConfig.getIdleInterval(); | |
| 78 | + final static int MAX_IDLE_CONNECTIONS = ServerConfig.getMaxIdleConnections(); | |
| 79 | + final static long TIMER_MILLIS = ServerConfig.getTimerMillis (); | |
| 80 | + final static long MAX_REQ_TIME=getTimeMillis(ServerConfig.getMaxReqTime()); | |
| 81 | + final static long MAX_RSP_TIME=getTimeMillis(ServerConfig.getMaxRspTime()); | |
| 82 | + final static boolean timer1Enabled = MAX_REQ_TIME != -1 || MAX_RSP_TIME != -1; | |
| 83 | + | |
| 84 | + private Timer timer, timer1; | |
| 85 | + private Logger logger; | |
| 86 | + | |
| 87 | + ServerImpl ( | |
| 88 | + HttpServer wrapper, String protocol, InetSocketAddress addr, int backlog | |
| 89 | + ) throws IOException { | |
| 90 | + | |
| 91 | + this.protocol = protocol; | |
| 92 | + this.wrapper = wrapper; | |
| 93 | + this.logger = Logger.getLogger ("com.sun.net.httpserver"); | |
| 94 | + ServerConfig.checkLegacyProperties (logger); | |
| 95 | + https = protocol.equalsIgnoreCase ("https"); | |
| 96 | + this.address = addr; | |
| 97 | + contexts = new ContextList(); | |
| 98 | + schan = ServerSocketChannel.open(); | |
| 99 | + if (addr != null) { | |
| 100 | + ServerSocket socket = schan.socket(); | |
| 101 | + socket.bind (addr, backlog); | |
| 102 | + bound = true; | |
| 103 | + } | |
| 104 | + selector = Selector.open (); | |
| 105 | + schan.configureBlocking (false); | |
| 106 | + listenerKey = schan.register (selector, SelectionKey.OP_ACCEPT); | |
| 107 | + dispatcher = new Dispatcher(); | |
| 108 | + idleConnections = Collections.synchronizedSet (new HashSet<HttpConnection>()); | |
| 109 | + allConnections = Collections.synchronizedSet (new HashSet<HttpConnection>()); | |
| 110 | + reqConnections = Collections.synchronizedSet (new HashSet<HttpConnection>()); | |
| 111 | + rspConnections = Collections.synchronizedSet (new HashSet<HttpConnection>()); | |
| 112 | + time = System.currentTimeMillis(); | |
| 113 | + timer = new Timer ("server-timer", true); | |
| 114 | + timer.schedule (new ServerTimerTask(), CLOCK_TICK, CLOCK_TICK); | |
| 115 | + if (timer1Enabled) { | |
| 116 | + timer1 = new Timer ("server-timer1", true); | |
| 117 | + timer1.schedule (new ServerTimerTask1(),TIMER_MILLIS,TIMER_MILLIS); | |
| 118 | + logger.config ("HttpServer timer1 enabled period in ms: "+TIMER_MILLIS); | |
| 119 | + logger.config ("MAX_REQ_TIME: "+MAX_REQ_TIME); | |
| 120 | + logger.config ("MAX_RSP_TIME: "+MAX_RSP_TIME); | |
| 121 | + } | |
| 122 | + events = new LinkedList<Event>(); | |
| 123 | + logger.config ("HttpServer created "+protocol+" "+ addr); | |
| 124 | + } | |
| 125 | + | |
| 126 | + public void bind (InetSocketAddress addr, int backlog) throws IOException { | |
| 127 | + if (bound) { | |
| 128 | + throw new BindException ("HttpServer already bound"); | |
| 129 | + } | |
| 130 | + if (addr == null) { | |
| 131 | + throw new NullPointerException ("null address"); | |
| 132 | + } | |
| 133 | + ServerSocket socket = schan.socket(); | |
| 134 | + socket.bind (addr, backlog); | |
| 135 | + bound = true; | |
| 136 | + } | |
| 137 | + | |
| 138 | + public void start () { | |
| 139 | + if (!bound || started || finished) { | |
| 140 | + throw new IllegalStateException ("server in wrong state"); | |
| 141 | + } | |
| 142 | + if (executor == null) { | |
| 143 | + executor = new DefaultExecutor(); | |
| 144 | + } | |
| 145 | + Thread t = new Thread (dispatcher); | |
| 146 | + started = true; | |
| 147 | + t.start(); | |
| 148 | + } | |
| 149 | + | |
| 150 | + public void setExecutor (Executor executor) { | |
| 151 | + if (started) { | |
| 152 | + throw new IllegalStateException ("server already started"); | |
| 153 | + } | |
| 154 | + this.executor = executor; | |
| 155 | + } | |
| 156 | + | |
| 157 | + private static class DefaultExecutor implements Executor { | |
| 158 | + public void execute (Runnable task) { | |
| 159 | + task.run(); | |
| 160 | + } | |
| 161 | + } | |
| 162 | + | |
| 163 | + public Executor getExecutor () { | |
| 164 | + return executor; | |
| 165 | + } | |
| 166 | + | |
| 167 | + public void setHttpsConfigurator (HttpsConfigurator config) { | |
| 168 | + if (config == null) { | |
| 169 | + throw new NullPointerException ("null HttpsConfigurator"); | |
| 170 | + } | |
| 171 | + if (started) { | |
| 172 | + throw new IllegalStateException ("server already started"); | |
| 173 | + } | |
| 174 | + this.httpsConfig = config; | |
| 175 | + sslContext = config.getSSLContext(); | |
| 176 | + } | |
| 177 | + | |
| 178 | + public HttpsConfigurator getHttpsConfigurator () { | |
| 179 | + return httpsConfig; | |
| 180 | + } | |
| 181 | + | |
| 182 | + public void stop (int delay) { | |
| 183 | + if (delay < 0) { | |
| 184 | + throw new IllegalArgumentException ("negative delay parameter"); | |
| 185 | + } | |
| 186 | + terminating = true; | |
| 187 | + try { schan.close(); } catch (IOException e) {} | |
| 188 | + selector.wakeup(); | |
| 189 | + long latest = System.currentTimeMillis() + delay * 1000; | |
| 190 | + while (System.currentTimeMillis() < latest) { | |
| 191 | + delay(); | |
| 192 | + if (finished) { | |
| 193 | + break; | |
| 194 | + } | |
| 195 | + } | |
| 196 | + finished = true; | |
| 197 | + selector.wakeup(); | |
| 198 | + synchronized (allConnections) { | |
| 199 | + for (HttpConnection c : allConnections) { | |
| 200 | + c.close(); | |
| 201 | + } | |
| 202 | + } | |
| 203 | + allConnections.clear(); | |
| 204 | + idleConnections.clear(); | |
| 205 | + timer.cancel(); | |
| 206 | + if (timer1Enabled) { | |
| 207 | + timer1.cancel(); | |
| 208 | + } | |
| 209 | + } | |
| 210 | + | |
| 211 | + Dispatcher dispatcher; | |
| 212 | + | |
| 213 | + public synchronized HttpContextImpl createContext (String path, HttpHandler handler) { | |
| 214 | + if (handler == null || path == null) { | |
| 215 | + throw new NullPointerException ("null handler, or path parameter"); | |
| 216 | + } | |
| 217 | + HttpContextImpl context = new HttpContextImpl (protocol, path, handler, this); | |
| 218 | + contexts.add (context); | |
| 219 | + logger.config ("context created: " + path); | |
| 220 | + return context; | |
| 221 | + } | |
| 222 | + | |
| 223 | + public synchronized HttpContextImpl createContext (String path) { | |
| 224 | + if (path == null) { | |
| 225 | + throw new NullPointerException ("null path parameter"); | |
| 226 | + } | |
| 227 | + HttpContextImpl context = new HttpContextImpl (protocol, path, null, this); | |
| 228 | + contexts.add (context); | |
| 229 | + logger.config ("context created: " + path); | |
| 230 | + return context; | |
| 231 | + } | |
| 232 | + | |
| 233 | + public synchronized void removeContext (String path) throws IllegalArgumentException { | |
| 234 | + if (path == null) { | |
| 235 | + throw new NullPointerException ("null path parameter"); | |
| 236 | + } | |
| 237 | + contexts.remove (protocol, path); | |
| 238 | + logger.config ("context removed: " + path); | |
| 239 | + } | |
| 240 | + | |
| 241 | + public synchronized void removeContext (HttpContext context) throws IllegalArgumentException { | |
| 242 | + if (!(context instanceof HttpContextImpl)) { | |
| 243 | + throw new IllegalArgumentException ("wrong HttpContext type"); | |
| 244 | + } | |
| 245 | + contexts.remove ((HttpContextImpl)context); | |
| 246 | + logger.config ("context removed: " + context.getPath()); | |
| 247 | + } | |
| 248 | + | |
| 249 | + public InetSocketAddress getAddress() { | |
| 250 | + return (InetSocketAddress)schan.socket().getLocalSocketAddress(); | |
| 251 | + } | |
| 252 | + | |
| 253 | + Selector getSelector () { | |
| 254 | + return selector; | |
| 255 | + } | |
| 256 | + | |
| 257 | + void addEvent (Event r) { | |
| 258 | + synchronized (lolock) { | |
| 259 | + events.add (r); | |
| 260 | + selector.wakeup(); | |
| 261 | + } | |
| 262 | + } | |
| 263 | + | |
| 264 | + /* main server listener task */ | |
| 265 | + | |
| 266 | + class Dispatcher implements Runnable { | |
| 267 | + | |
| 268 | + private void handleEvent (Event r) { | |
| 269 | + ExchangeImpl t = r.exchange; | |
| 270 | + HttpConnection c = t.getConnection(); | |
| 271 | + try { | |
| 272 | + if (r instanceof WriteFinishedEvent) { | |
| 273 | + | |
| 274 | + int exchanges = endExchange(); | |
| 275 | + if (terminating && exchanges == 0) { | |
| 276 | + finished = true; | |
| 277 | + } | |
| 278 | + responseCompleted (c); | |
| 279 | + LeftOverInputStream is = t.getOriginalInputStream(); | |
| 280 | + if (!is.isEOF()) { | |
| 281 | + t.close = true; | |
| 282 | + } | |
| 283 | + if (t.close || idleConnections.size() >= MAX_IDLE_CONNECTIONS) { | |
| 284 | + c.close(); | |
| 285 | + allConnections.remove (c); | |
| 286 | + } else { | |
| 287 | + if (is.isDataBuffered()) { | |
| 288 | + /* don't re-enable the interestops, just handle it */ | |
| 289 | + requestStarted (c); | |
| 290 | + handle (c.getChannel(), c); | |
| 291 | + } else { | |
| 292 | + connsToRegister.add (c); | |
| 293 | + } | |
| 294 | + } | |
| 295 | + } | |
| 296 | + } catch (IOException e) { | |
| 297 | + logger.log ( | |
| 298 | + Level.FINER, "Dispatcher (1)", e | |
| 299 | + ); | |
| 300 | + c.close(); | |
| 301 | + } | |
| 302 | + } | |
| 303 | + | |
| 304 | + final LinkedList<HttpConnection> connsToRegister = | |
| 305 | + new LinkedList<HttpConnection>(); | |
| 306 | + | |
| 307 | + void reRegister (HttpConnection c) { | |
| 308 | + /* re-register with selector */ | |
| 309 | + try { | |
| 310 | + SocketChannel chan = c.getChannel(); | |
| 311 | + chan.configureBlocking (false); | |
| 312 | + SelectionKey key = chan.register (selector, SelectionKey.OP_READ); | |
| 313 | + key.attach (c); | |
| 314 | + c.selectionKey = key; | |
| 315 | + c.time = getTime() + IDLE_INTERVAL; | |
| 316 | + idleConnections.add (c); | |
| 317 | + } catch (IOException e) { | |
| 318 | + dprint(e); | |
| 319 | + logger.log(Level.FINER, "Dispatcher(8)", e); | |
| 320 | + c.close(); | |
| 321 | + } | |
| 322 | + } | |
| 323 | + | |
| 324 | + public void run() { | |
| 325 | + while (!finished) { | |
| 326 | + try { | |
| 327 | + ListIterator<HttpConnection> li = | |
| 328 | + connsToRegister.listIterator(); | |
| 329 | + for (HttpConnection c : connsToRegister) { | |
| 330 | + reRegister(c); | |
| 331 | + } | |
| 332 | + connsToRegister.clear(); | |
| 333 | + | |
| 334 | + List<Event> list = null; | |
| 335 | + selector.select(1000); | |
| 336 | + synchronized (lolock) { | |
| 337 | + if (events.size() > 0) { | |
| 338 | + list = events; | |
| 339 | + events = new LinkedList<Event>(); | |
| 340 | + } | |
| 341 | + } | |
| 342 | + | |
| 343 | + if (list != null) { | |
| 344 | + for (Event r: list) { | |
| 345 | + handleEvent (r); | |
| 346 | + } | |
| 347 | + } | |
| 348 | + | |
| 349 | + /* process the selected list now */ | |
| 350 | + | |
| 351 | + Set<SelectionKey> selected = selector.selectedKeys(); | |
| 352 | + Iterator<SelectionKey> iter = selected.iterator(); | |
| 353 | + while (iter.hasNext()) { | |
| 354 | + SelectionKey key = iter.next(); | |
| 355 | + iter.remove (); | |
| 356 | + if (key.equals (listenerKey)) { | |
| 357 | + if (terminating) { | |
| 358 | + continue; | |
| 359 | + } | |
| 360 | + SocketChannel chan = schan.accept(); | |
| 361 | + if (chan == null) { | |
| 362 | + continue; /* cancel something ? */ | |
| 363 | + } | |
| 364 | + chan.configureBlocking (false); | |
| 365 | + SelectionKey newkey = chan.register (selector, SelectionKey.OP_READ); | |
| 366 | + HttpConnection c = new HttpConnection (); | |
| 367 | + c.selectionKey = newkey; | |
| 368 | + c.setChannel (chan); | |
| 369 | + newkey.attach (c); | |
| 370 | + requestStarted (c); | |
| 371 | + allConnections.add (c); | |
| 372 | + } else { | |
| 373 | + try { | |
| 374 | + if (key.isReadable()) { | |
| 375 | + boolean closed; | |
| 376 | + SocketChannel chan = (SocketChannel)key.channel(); | |
| 377 | + HttpConnection conn = (HttpConnection)key.attachment(); | |
| 378 | + | |
| 379 | + key.cancel(); | |
| 380 | + chan.configureBlocking (true); | |
| 381 | + if (idleConnections.remove(conn)) { | |
| 382 | + // was an idle connection so add it | |
| 383 | + // to reqConnections set. | |
| 384 | + requestStarted (conn); | |
| 385 | + } | |
| 386 | + handle (chan, conn); | |
| 387 | + } else { | |
| 388 | + assert false; | |
| 389 | + } | |
| 390 | + } catch (CancelledKeyException e) { | |
| 391 | + handleException(key, null); | |
| 392 | + } catch (IOException e) { | |
| 393 | + handleException(key, e); | |
| 394 | + } | |
| 395 | + } | |
| 396 | + } | |
| 397 | + // call the selector just to process the cancelled keys | |
| 398 | + selector.selectNow(); | |
| 399 | + } catch (IOException e) { | |
| 400 | + logger.log (Level.FINER, "Dispatcher (4)", e); | |
| 401 | + } catch (Exception e) { | |
| 402 | + e.printStackTrace(); | |
| 403 | + logger.log (Level.FINER, "Dispatcher (7)", e); | |
| 404 | + } | |
| 405 | + } | |
| 406 | + } | |
| 407 | + | |
| 408 | + private void handleException (SelectionKey key, Exception e) { | |
| 409 | + HttpConnection conn = (HttpConnection)key.attachment(); | |
| 410 | + if (e != null) { | |
| 411 | + logger.log (Level.FINER, "Dispatcher (2)", e); | |
| 412 | + } | |
| 413 | + closeConnection(conn); | |
| 414 | + } | |
| 415 | + | |
| 416 | + public void handle (SocketChannel chan, HttpConnection conn) | |
| 417 | + throws IOException | |
| 418 | + { | |
| 419 | + try { | |
| 420 | + Exchange t = new Exchange (chan, protocol, conn); | |
| 421 | + executor.execute (t); | |
| 422 | + } catch (HttpError e1) { | |
| 423 | + logger.log (Level.FINER, "Dispatcher (4)", e1); | |
| 424 | + closeConnection(conn); | |
| 425 | + } catch (IOException e) { | |
| 426 | + logger.log (Level.FINER, "Dispatcher (5)", e); | |
| 427 | + closeConnection(conn); | |
| 428 | + } | |
| 429 | + } | |
| 430 | + } | |
| 431 | + | |
| 432 | + static boolean debug = ServerConfig.debugEnabled (); | |
| 433 | + | |
| 434 | + static synchronized void dprint (String s) { | |
| 435 | + if (debug) { | |
| 436 | + System.out.println (s); | |
| 437 | + } | |
| 438 | + } | |
| 439 | + | |
| 440 | + static synchronized void dprint (Exception e) { | |
| 441 | + if (debug) { | |
| 442 | + System.out.println (e); | |
| 443 | + e.printStackTrace(); | |
| 444 | + } | |
| 445 | + } | |
| 446 | + | |
| 447 | + Logger getLogger () { | |
| 448 | + return logger; | |
| 449 | + } | |
| 450 | + | |
| 451 | + private void closeConnection(HttpConnection conn) { | |
| 452 | + conn.close(); | |
| 453 | + allConnections.remove(conn); | |
| 454 | + switch (conn.getState()) { | |
| 455 | + case REQUEST: | |
| 456 | + reqConnections.remove(conn); | |
| 457 | + break; | |
| 458 | + case RESPONSE: | |
| 459 | + rspConnections.remove(conn); | |
| 460 | + break; | |
| 461 | + case IDLE: | |
| 462 | + idleConnections.remove(conn); | |
| 463 | + break; | |
| 464 | + } | |
| 465 | + assert !reqConnections.remove(conn); | |
| 466 | + assert !rspConnections.remove(conn); | |
| 467 | + assert !idleConnections.remove(conn); | |
| 468 | + } | |
| 469 | + | |
| 470 | + /* per exchange task */ | |
| 471 | + | |
| 472 | + class Exchange implements Runnable { | |
| 473 | + SocketChannel chan; | |
| 474 | + HttpConnection connection; | |
| 475 | + HttpContextImpl context; | |
| 476 | + InputStream rawin; | |
| 477 | + OutputStream rawout; | |
| 478 | + String protocol; | |
| 479 | + ExchangeImpl tx; | |
| 480 | + HttpContextImpl ctx; | |
| 481 | + boolean rejected = false; | |
| 482 | + | |
| 483 | + Exchange (SocketChannel chan, String protocol, HttpConnection conn) throws IOException { | |
| 484 | + this.chan = chan; | |
| 485 | + this.connection = conn; | |
| 486 | + this.protocol = protocol; | |
| 487 | + } | |
| 488 | + | |
| 489 | + public void run () { | |
| 490 | + /* context will be null for new connections */ | |
| 491 | + context = connection.getHttpContext(); | |
| 492 | + boolean newconnection; | |
| 493 | + SSLEngine engine = null; | |
| 494 | + String requestLine = null; | |
| 495 | + SSLStreams sslStreams = null; | |
| 496 | + try { | |
| 497 | + if (context != null ) { | |
| 498 | + this.rawin = connection.getInputStream(); | |
| 499 | + this.rawout = connection.getRawOutputStream(); | |
| 500 | + newconnection = false; | |
| 501 | + } else { | |
| 502 | + /* figure out what kind of connection this is */ | |
| 503 | + newconnection = true; | |
| 504 | + if (https) { | |
| 505 | + if (sslContext == null) { | |
| 506 | + logger.warning ("SSL connection received. No https contxt created"); | |
| 507 | + throw new HttpError ("No SSL context established"); | |
| 508 | + } | |
| 509 | + sslStreams = new SSLStreams (ServerImpl.this, sslContext, chan); | |
| 510 | + rawin = sslStreams.getInputStream(); | |
| 511 | + rawout = sslStreams.getOutputStream(); | |
| 512 | + engine = sslStreams.getSSLEngine(); | |
| 513 | + connection.sslStreams = sslStreams; | |
| 514 | + } else { | |
| 515 | + rawin = new BufferedInputStream( | |
| 516 | + new Request.ReadStream ( | |
| 517 | + ServerImpl.this, chan | |
| 518 | + )); | |
| 519 | + rawout = new Request.WriteStream ( | |
| 520 | + ServerImpl.this, chan | |
| 521 | + ); | |
| 522 | + } | |
| 523 | + connection.raw = rawin; | |
| 524 | + connection.rawout = rawout; | |
| 525 | + } | |
| 526 | + Request req = new Request (rawin, rawout); | |
| 527 | + requestLine = req.requestLine(); | |
| 528 | + if (requestLine == null) { | |
| 529 | + /* connection closed */ | |
| 530 | + closeConnection(connection); | |
| 531 | + return; | |
| 532 | + } | |
| 533 | + int space = requestLine.indexOf (' '); | |
| 534 | + if (space == -1) { | |
| 535 | + reject (Code.HTTP_BAD_REQUEST, | |
| 536 | + requestLine, "Bad request line"); | |
| 537 | + return; | |
| 538 | + } | |
| 539 | + String method = requestLine.substring (0, space); | |
| 540 | + int start = space+1; | |
| 541 | + space = requestLine.indexOf(' ', start); | |
| 542 | + if (space == -1) { | |
| 543 | + reject (Code.HTTP_BAD_REQUEST, | |
| 544 | + requestLine, "Bad request line"); | |
| 545 | + return; | |
| 546 | + } | |
| 547 | + String uriStr = requestLine.substring (start, space); | |
| 548 | + URI uri = new URI (uriStr); | |
| 549 | + start = space+1; | |
| 550 | + String version = requestLine.substring (start); | |
| 551 | + Headers headers = req.headers(); | |
| 552 | + String s = headers.getFirst ("Transfer-encoding"); | |
| 553 | + long clen = 0L; | |
| 554 | + if (s !=null && s.equalsIgnoreCase ("chunked")) { | |
| 555 | + clen = -1L; | |
| 556 | + } else { | |
| 557 | + s = headers.getFirst ("Content-Length"); | |
| 558 | + if (s != null) { | |
| 559 | + clen = Long.parseLong(s); | |
| 560 | + } | |
| 561 | + if (clen == 0) { | |
| 562 | + requestCompleted (connection); | |
| 563 | + } | |
| 564 | + } | |
| 565 | + ctx = contexts.findContext (protocol, uri.getPath()); | |
| 566 | + if (ctx == null) { | |
| 567 | + reject (Code.HTTP_NOT_FOUND, | |
| 568 | + requestLine, "No context found for request"); | |
| 569 | + return; | |
| 570 | + } | |
| 571 | + connection.setContext (ctx); | |
| 572 | + if (ctx.getHandler() == null) { | |
| 573 | + reject (Code.HTTP_INTERNAL_ERROR, | |
| 574 | + requestLine, "No handler for context"); | |
| 575 | + return; | |
| 576 | + } | |
| 577 | + tx = new ExchangeImpl ( | |
| 578 | + method, uri, req, clen, connection | |
| 579 | + ); | |
| 580 | + String chdr = headers.getFirst("Connection"); | |
| 581 | + Headers rheaders = tx.getResponseHeaders(); | |
| 582 | + | |
| 583 | + if (chdr != null && chdr.equalsIgnoreCase ("close")) { | |
| 584 | + tx.close = true; | |
| 585 | + } | |
| 586 | + if (version.equalsIgnoreCase ("http/1.0")) { | |
| 587 | + tx.http10 = true; | |
| 588 | + if (chdr == null) { | |
| 589 | + tx.close = true; | |
| 590 | + rheaders.set ("Connection", "close"); | |
| 591 | + } else if (chdr.equalsIgnoreCase ("keep-alive")) { | |
| 592 | + rheaders.set ("Connection", "keep-alive"); | |
| 593 | + int idle=(int)ServerConfig.getIdleInterval()/1000; | |
| 594 | + int max=(int)ServerConfig.getMaxIdleConnections(); | |
| 595 | + String val = "timeout="+idle+", max="+max; | |
| 596 | + rheaders.set ("Keep-Alive", val); | |
| 597 | + } | |
| 598 | + } | |
| 599 | + | |
| 600 | + if (newconnection) { | |
| 601 | + connection.setParameters ( | |
| 602 | + rawin, rawout, chan, engine, sslStreams, | |
| 603 | + sslContext, protocol, ctx, rawin | |
| 604 | + ); | |
| 605 | + } | |
| 606 | + /* check if client sent an Expect 100 Continue. | |
| 607 | + * In that case, need to send an interim response. | |
| 608 | + * In future API may be modified to allow app to | |
| 609 | + * be involved in this process. | |
| 610 | + */ | |
| 611 | + String exp = headers.getFirst("Expect"); | |
| 612 | + if (exp != null && exp.equalsIgnoreCase ("100-continue")) { | |
| 613 | + logReply (100, requestLine, null); | |
| 614 | + sendReply ( | |
| 615 | + Code.HTTP_CONTINUE, false, null | |
| 616 | + ); | |
| 617 | + } | |
| 618 | + /* uf is the list of filters seen/set by the user. | |
| 619 | + * sf is the list of filters established internally | |
| 620 | + * and which are not visible to the user. uc and sc | |
| 621 | + * are the corresponding Filter.Chains. | |
| 622 | + * They are linked together by a LinkHandler | |
| 623 | + * so that they can both be invoked in one call. | |
| 624 | + */ | |
| 625 | + List<Filter> sf = ctx.getSystemFilters(); | |
| 626 | + List<Filter> uf = ctx.getFilters(); | |
| 627 | + | |
| 628 | + Filter.Chain sc = new Filter.Chain(sf, ctx.getHandler()); | |
| 629 | + Filter.Chain uc = new Filter.Chain(uf, new LinkHandler (sc)); | |
| 630 | + | |
| 631 | + /* set up the two stream references */ | |
| 632 | + tx.getRequestBody(); | |
| 633 | + tx.getResponseBody(); | |
| 634 | + if (https) { | |
| 635 | + uc.doFilter (new HttpsExchangeImpl (tx)); | |
| 636 | + } else { | |
| 637 | + uc.doFilter (new HttpExchangeImpl (tx)); | |
| 638 | + } | |
| 639 | + | |
| 640 | + } catch (IOException e1) { | |
| 641 | + logger.log (Level.FINER, "ServerImpl.Exchange (1)", e1); | |
| 642 | + closeConnection(connection); | |
| 643 | + } catch (NumberFormatException e3) { | |
| 644 | + reject (Code.HTTP_BAD_REQUEST, | |
| 645 | + requestLine, "NumberFormatException thrown"); | |
| 646 | + } catch (URISyntaxException e) { | |
| 647 | + reject (Code.HTTP_BAD_REQUEST, | |
| 648 | + requestLine, "URISyntaxException thrown"); | |
| 649 | + } catch (Exception e4) { | |
| 650 | + logger.log (Level.FINER, "ServerImpl.Exchange (2)", e4); | |
| 651 | + closeConnection(connection); | |
| 652 | + } | |
| 653 | + } | |
| 654 | + | |
| 655 | + /* used to link to 2 or more Filter.Chains together */ | |
| 656 | + | |
| 657 | + class LinkHandler implements HttpHandler { | |
| 658 | + Filter.Chain nextChain; | |
| 659 | + | |
| 660 | + LinkHandler (Filter.Chain nextChain) { | |
| 661 | + this.nextChain = nextChain; | |
| 662 | + } | |
| 663 | + | |
| 664 | + public void handle (HttpExchange exchange) throws IOException { | |
| 665 | + nextChain.doFilter (exchange); | |
| 666 | + } | |
| 667 | + } | |
| 668 | + | |
| 669 | + void reject (int code, String requestStr, String message) { | |
| 670 | + rejected = true; | |
| 671 | + logReply (code, requestStr, message); | |
| 672 | + sendReply ( | |
| 673 | + code, false, "<h1>"+code+Code.msg(code)+"</h1>"+message | |
| 674 | + ); | |
| 675 | + closeConnection(connection); | |
| 676 | + } | |
| 677 | + | |
| 678 | + void sendReply ( | |
| 679 | + int code, boolean closeNow, String text) | |
| 680 | + { | |
| 681 | + try { | |
| 682 | + StringBuilder builder = new StringBuilder (512); | |
| 683 | + builder.append ("HTTP/1.1 ") | |
| 684 | + .append (code).append (Code.msg(code)).append ("\r\n"); | |
| 685 | + | |
| 686 | + if (text != null && text.length() != 0) { | |
| 687 | + builder.append ("Content-Length: ") | |
| 688 | + .append (text.length()).append ("\r\n") | |
| 689 | + .append ("Content-Type: text/html\r\n"); | |
| 690 | + } else { | |
| 691 | + builder.append ("Content-Length: 0\r\n"); | |
| 692 | + text = ""; | |
| 693 | + } | |
| 694 | + if (closeNow) { | |
| 695 | + builder.append ("Connection: close\r\n"); | |
| 696 | + } | |
| 697 | + builder.append ("\r\n").append (text); | |
| 698 | + String s = builder.toString(); | |
| 699 | + byte[] b = s.getBytes("ISO8859_1"); | |
| 700 | + rawout.write (b); | |
| 701 | + rawout.flush(); | |
| 702 | + if (closeNow) { | |
| 703 | + closeConnection(connection); | |
| 704 | + } | |
| 705 | + } catch (IOException e) { | |
| 706 | + logger.log (Level.FINER, "ServerImpl.sendReply", e); | |
| 707 | + closeConnection(connection); | |
| 708 | + } | |
| 709 | + } | |
| 710 | + | |
| 711 | + } | |
| 712 | + | |
| 713 | + void logReply (int code, String requestStr, String text) { | |
| 714 | + if (!logger.isLoggable(Level.FINE)) { | |
| 715 | + return; | |
| 716 | + } | |
| 717 | + if (text == null) { | |
| 718 | + text = ""; | |
| 719 | + } | |
| 720 | + String r; | |
| 721 | + if (requestStr.length() > 80) { | |
| 722 | + r = requestStr.substring (0, 80) + "<TRUNCATED>"; | |
| 723 | + } else { | |
| 724 | + r = requestStr; | |
| 725 | + } | |
| 726 | + String message = r + " [" + code + " " + | |
| 727 | + Code.msg(code) + "] ("+text+")"; | |
| 728 | + logger.fine (message); | |
| 729 | + } | |
| 730 | + | |
| 731 | + long getTicks() { | |
| 732 | + return ticks; | |
| 733 | + } | |
| 734 | + | |
| 735 | + public long getTime() { | |
| 736 | + return time; | |
| 737 | + } | |
| 738 | + | |
| 739 | + void delay () { | |
| 740 | + Thread.yield(); | |
| 741 | + try { | |
| 742 | + Thread.sleep (200); | |
| 743 | + } catch (InterruptedException e) {} | |
| 744 | + } | |
| 745 | + | |
| 746 | + private int exchangeCount = 0; | |
| 747 | + | |
| 748 | + synchronized void startExchange () { | |
| 749 | + exchangeCount ++; | |
| 750 | + } | |
| 751 | + | |
| 752 | + synchronized int endExchange () { | |
| 753 | + exchangeCount --; | |
| 754 | + assert exchangeCount >= 0; | |
| 755 | + return exchangeCount; | |
| 756 | + } | |
| 757 | + | |
| 758 | + HttpServer getWrapper () { | |
| 759 | + return wrapper; | |
| 760 | + } | |
| 761 | + | |
| 762 | + void requestStarted (HttpConnection c) { | |
| 763 | + c.creationTime = getTime(); | |
| 764 | + c.setState (State.REQUEST); | |
| 765 | + reqConnections.add (c); | |
| 766 | + } | |
| 767 | + | |
| 768 | + // called after a request has been completely read | |
| 769 | + // by the server. This stops the timer which would | |
| 770 | + // close the connection if the request doesn't arrive | |
| 771 | + // quickly enough. It then starts the timer | |
| 772 | + // that ensures the client reads the response in a timely | |
| 773 | + // fashion. | |
| 774 | + | |
| 775 | + void requestCompleted (HttpConnection c) { | |
| 776 | + assert c.getState() == State.REQUEST; | |
| 777 | + reqConnections.remove (c); | |
| 778 | + c.rspStartedTime = getTime(); | |
| 779 | + rspConnections.add (c); | |
| 780 | + c.setState (State.RESPONSE); | |
| 781 | + } | |
| 782 | + | |
| 783 | + // called after response has been sent | |
| 784 | + void responseCompleted (HttpConnection c) { | |
| 785 | + assert c.getState() == State.RESPONSE; | |
| 786 | + rspConnections.remove (c); | |
| 787 | + c.setState (State.IDLE); | |
| 788 | + } | |
| 789 | + | |
| 790 | + /** | |
| 791 | + * TimerTask run every CLOCK_TICK ms | |
| 792 | + */ | |
| 793 | + class ServerTimerTask extends TimerTask { | |
| 794 | + public void run () { | |
| 795 | + LinkedList<HttpConnection> toClose = new LinkedList<HttpConnection>(); | |
| 796 | + time = System.currentTimeMillis(); | |
| 797 | + ticks ++; | |
| 798 | + synchronized (idleConnections) { | |
| 799 | + for (HttpConnection c : idleConnections) { | |
| 800 | + if (c.time <= time) { | |
| 801 | + toClose.add (c); | |
| 802 | + } | |
| 803 | + } | |
| 804 | + for (HttpConnection c : toClose) { | |
| 805 | + idleConnections.remove (c); | |
| 806 | + allConnections.remove (c); | |
| 807 | + c.close(); | |
| 808 | + } | |
| 809 | + } | |
| 810 | + } | |
| 811 | + } | |
| 812 | + | |
| 813 | + class ServerTimerTask1 extends TimerTask { | |
| 814 | + | |
| 815 | + // runs every TIMER_MILLIS | |
| 816 | + public void run () { | |
| 817 | + LinkedList<HttpConnection> toClose = new LinkedList<HttpConnection>(); | |
| 818 | + time = System.currentTimeMillis(); | |
| 819 | + synchronized (reqConnections) { | |
| 820 | + if (MAX_REQ_TIME != -1) { | |
| 821 | + for (HttpConnection c : reqConnections) { | |
| 822 | + if (c.creationTime + TIMER_MILLIS + MAX_REQ_TIME <= time) { | |
| 823 | + toClose.add (c); | |
| 824 | + } | |
| 825 | + } | |
| 826 | + for (HttpConnection c : toClose) { | |
| 827 | + logger.log (Level.FINE, "closing: no request: " + c); | |
| 828 | + reqConnections.remove (c); | |
| 829 | + allConnections.remove (c); | |
| 830 | + c.close(); | |
| 831 | + } | |
| 832 | + } | |
| 833 | + } | |
| 834 | + toClose = new LinkedList<HttpConnection>(); | |
| 835 | + synchronized (rspConnections) { | |
| 836 | + if (MAX_RSP_TIME != -1) { | |
| 837 | + for (HttpConnection c : rspConnections) { | |
| 838 | + if (c.rspStartedTime + TIMER_MILLIS +MAX_RSP_TIME <= time) { | |
| 839 | + toClose.add (c); | |
| 840 | + } | |
| 841 | + } | |
| 842 | + for (HttpConnection c : toClose) { | |
| 843 | + logger.log (Level.FINE, "closing: no response: " + c); | |
| 844 | + rspConnections.remove (c); | |
| 845 | + allConnections.remove (c); | |
| 846 | + c.close(); | |
| 847 | + } | |
| 848 | + } | |
| 849 | + } | |
| 850 | + } | |
| 851 | + } | |
| 852 | + | |
| 853 | + void logStackTrace (String s) { | |
| 854 | + logger.finest (s); | |
| 855 | + StringBuilder b = new StringBuilder (); | |
| 856 | + StackTraceElement[] e = Thread.currentThread().getStackTrace(); | |
| 857 | + for (int i=0; i<e.length; i++) { | |
| 858 | + b.append (e[i].toString()).append("\n"); | |
| 859 | + } | |
| 860 | + logger.finest (b.toString()); | |
| 861 | + } | |
| 862 | + | |
| 863 | + static long getTimeMillis(long secs) { | |
| 864 | + if (secs == -1) { | |
| 865 | + return -1; | |
| 866 | + } else { | |
| 867 | + return secs * 1000; | |
| 868 | + } | |
| 869 | + } | |
| 870 | +} | |
| @@ -0,0 +1,32 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import java.io.*; | |
| 29 | + | |
| 30 | +class StreamClosedException extends IOException { | |
| 31 | + private static final long serialVersionUID = -4485921499356327937L; | |
| 32 | +} | |
| @@ -0,0 +1,30 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +interface TimeSource { | |
| 29 | + public long getTime(); | |
| 30 | +} | |
| @@ -0,0 +1,81 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import java.io.*; | |
| 29 | +import java.net.*; | |
| 30 | +import com.sun.net.httpserver.*; | |
| 31 | +import com.sun.net.httpserver.spi.*; | |
| 32 | + | |
| 33 | +/** | |
| 34 | + * a class which allows the caller to write an indefinite | |
| 35 | + * number of bytes to an underlying stream , but without using | |
| 36 | + * chunked encoding. Used for http/1.0 clients only | |
| 37 | + * The underlying connection needs to be closed afterwards. | |
| 38 | + */ | |
| 39 | + | |
| 40 | +class UndefLengthOutputStream extends FilterOutputStream | |
| 41 | +{ | |
| 42 | + private boolean closed = false; | |
| 43 | + ExchangeImpl t; | |
| 44 | + | |
| 45 | + UndefLengthOutputStream (ExchangeImpl t, OutputStream src) { | |
| 46 | + super (src); | |
| 47 | + this.t = t; | |
| 48 | + } | |
| 49 | + | |
| 50 | + public void write (int b) throws IOException { | |
| 51 | + if (closed) { | |
| 52 | + throw new IOException ("stream closed"); | |
| 53 | + } | |
| 54 | + out.write(b); | |
| 55 | + } | |
| 56 | + | |
| 57 | + public void write (byte[]b, int off, int len) throws IOException { | |
| 58 | + if (closed) { | |
| 59 | + throw new IOException ("stream closed"); | |
| 60 | + } | |
| 61 | + out.write(b, off, len); | |
| 62 | + } | |
| 63 | + | |
| 64 | + public void close () throws IOException { | |
| 65 | + if (closed) { | |
| 66 | + return; | |
| 67 | + } | |
| 68 | + closed = true; | |
| 69 | + flush(); | |
| 70 | + LeftOverInputStream is = t.getOriginalInputStream(); | |
| 71 | + if (!is.isClosed()) { | |
| 72 | + try { | |
| 73 | + is.close(); | |
| 74 | + } catch (IOException e) {} | |
| 75 | + } | |
| 76 | + WriteFinishedEvent e = new WriteFinishedEvent (t); | |
| 77 | + t.getHttpContext().getServerImpl().addEvent (e); | |
| 78 | + } | |
| 79 | + | |
| 80 | + // flush is a pass-through | |
| 81 | +} | |
| @@ -0,0 +1,101 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. | |
| 3 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| 4 | + * | |
| 5 | + * This code is free software; you can redistribute it and/or modify it | |
| 6 | + * under the terms of the GNU General Public License version 2 only, as | |
| 7 | + * published by the Free Software Foundation. Oracle designates this | |
| 8 | + * particular file as subject to the "Classpath" exception as provided | |
| 9 | + * by Oracle in the LICENSE file that accompanied this code. | |
| 10 | + * | |
| 11 | + * This code is distributed in the hope that it will be useful, but WITHOUT | |
| 12 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 13 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 14 | + * version 2 for more details (a copy is included in the LICENSE file that | |
| 15 | + * accompanied this code). | |
| 16 | + * | |
| 17 | + * You should have received a copy of the GNU General Public License version | |
| 18 | + * 2 along with this work; if not, write to the Free Software Foundation, | |
| 19 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 20 | + * | |
| 21 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| 22 | + * or visit www.oracle.com if you need additional information or have any | |
| 23 | + * questions. | |
| 24 | + */ | |
| 25 | + | |
| 26 | +package sun.net.httpserver; | |
| 27 | + | |
| 28 | +import java.util.*; | |
| 29 | +import com.sun.net.httpserver.*; | |
| 30 | + | |
| 31 | +class UnmodifiableHeaders extends Headers { | |
| 32 | + | |
| 33 | + Headers map; | |
| 34 | + | |
| 35 | + UnmodifiableHeaders(Headers map) { | |
| 36 | + this.map = map; | |
| 37 | + } | |
| 38 | + | |
| 39 | + public int size() {return map.size();} | |
| 40 | + | |
| 41 | + public boolean isEmpty() {return map.isEmpty();} | |
| 42 | + | |
| 43 | + public boolean containsKey(Object key) { | |
| 44 | + return map.containsKey (key); | |
| 45 | + } | |
| 46 | + | |
| 47 | + public boolean containsValue(Object value) { | |
| 48 | + return map.containsValue(value); | |
| 49 | + } | |
| 50 | + | |
| 51 | + public List<String> get(Object key) { | |
| 52 | + return map.get(key); | |
| 53 | + } | |
| 54 | + | |
| 55 | + public String getFirst (String key) { | |
| 56 | + return map.getFirst(key); | |
| 57 | + } | |
| 58 | + | |
| 59 | + | |
| 60 | + public List<String> put(String key, List<String> value) { | |
| 61 | + return map.put (key, value); | |
| 62 | + } | |
| 63 | + | |
| 64 | + public void add (String key, String value) { | |
| 65 | + throw new UnsupportedOperationException ("unsupported operation"); | |
| 66 | + } | |
| 67 | + | |
| 68 | + public void set (String key, String value) { | |
| 69 | + throw new UnsupportedOperationException ("unsupported operation"); | |
| 70 | + } | |
| 71 | + | |
| 72 | + public List<String> remove(Object key) { | |
| 73 | + throw new UnsupportedOperationException ("unsupported operation"); | |
| 74 | + } | |
| 75 | + | |
| 76 | + public void putAll(Map<? extends String,? extends List<String>> t) { | |
| 77 | + throw new UnsupportedOperationException ("unsupported operation"); | |
| 78 | + } | |
| 79 | + | |
| 80 | + public void clear() { | |
| 81 | + throw new UnsupportedOperationException ("unsupported operation"); | |
| 82 | + } | |
| 83 | + | |
| 84 | + public Set<String> keySet() { | |
| 85 | + return Collections.unmodifiableSet (map.keySet()); | |
| 86 | + } | |
| 87 | + | |
| 88 | + public Collection<List<String>> values() { | |
| 89 | + return Collections.unmodifiableCollection(map.values()); | |
| 90 | + } | |
| 91 | + | |
| 92 | + /* TODO check that contents of set are not modifable : security */ | |
| 93 | + | |
| 94 | + public Set<Map.Entry<String, List<String>>> entrySet() { | |
| 95 | + return Collections.unmodifiableSet (map.entrySet()); | |
| 96 | + } | |
| 97 | + | |
| 98 | + public boolean equals(Object o) {return map.equals(o);} | |
| 99 | + | |
| 100 | + public int hashCode() {return map.hashCode();} | |
| 101 | + } | |
| @@ -0,0 +1,33 @@ | ||
| 1 | +<../../../../../mkconfig | |
| 2 | + | |
| 3 | +TARG=\ | |
| 4 | + AuthFilter.dis\ | |
| 5 | + ChunkedInputStream.dis\ | |
| 6 | + ChunkedOutputStream.dis\ | |
| 7 | + Code.dis\ | |
| 8 | + ContextList.dis\ | |
| 9 | + DefaultHttpServerProvider.dis\ | |
| 10 | + Event.dis\ | |
| 11 | + ExchangeImpl.dis\ | |
| 12 | + FixedLengthInputStream.dis\ | |
| 13 | + FixedLengthOutputStream.dis\ | |
| 14 | + HttpConnection.dis\ | |
| 15 | + HttpContextImpl.dis\ | |
| 16 | + HttpError.dis\ | |
| 17 | + HttpExchangeImpl.dis\ | |
| 18 | + HttpServerImpl.dis\ | |
| 19 | + HttpsExchangeImpl.dis\ | |
| 20 | + HttpsServerImpl.dis\ | |
| 21 | + LeftOverInputStream.dis\ | |
| 22 | + Request.dis\ | |
| 23 | + ServerConfig.dis\ | |
| 24 | + ServerImpl.dis\ | |
| 25 | + SSLStreams.dis\ | |
| 26 | + StreamClosedException.dis\ | |
| 27 | + TimeSource.dis\ | |
| 28 | + UndefLengthOutputStream.dis\ | |
| 29 | + UnmodifiableHeaders.dis\ | |
| 30 | + | |
| 31 | +DISBIN=$ROOT/java/dis/java/java/sun/net/httpserver | |
| 32 | + | |
| 33 | +<$ROOT/mkfiles/mkjava | |