8187443: Forest Consolidation: Move files to unified layout

Reviewed-by: darcy, ihse
This commit is contained in:
Erik Joelsson 2017-09-12 19:03:39 +02:00
parent 270fe13182
commit 3789983e89
56923 changed files with 3 additions and 15727 deletions

View file

@ -0,0 +1,125 @@
/*
* Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.net.httpserver;
import java.net.*;
import java.io.*;
import java.util.*;
/**
* Authenticator represents an implementation of an HTTP authentication
* mechanism. Sub-classes provide implementations of specific mechanisms
* such as Digest or Basic auth. Instances are invoked to provide verification
* of the authentication information provided in all incoming requests.
* Note. This implies that any caching of credentials or other authentication
* information must be done outside of this class.
*/
public abstract class Authenticator {
/**
* Base class for return type from authenticate() method
*/
public abstract static class Result {}
/**
* Indicates an authentication failure. The authentication
* attempt has completed.
*/
public static class Failure extends Result {
private int responseCode;
public Failure (int responseCode) {
this.responseCode = responseCode;
}
/**
* returns the response code to send to the client
*/
public int getResponseCode() {
return responseCode;
}
}
/**
* Indicates an authentication has succeeded and the
* authenticated user principal can be acquired by calling
* getPrincipal().
*/
public static class Success extends Result {
private HttpPrincipal principal;
public Success (HttpPrincipal p) {
principal = p;
}
/**
* returns the authenticated user Principal
*/
public HttpPrincipal getPrincipal() {
return principal;
}
}
/**
* Indicates an authentication must be retried. The
* response code to be sent back is as returned from
* getResponseCode(). The Authenticator must also have
* set any necessary response headers in the given HttpExchange
* before returning this Retry object.
*/
public static class Retry extends Result {
private int responseCode;
public Retry (int responseCode) {
this.responseCode = responseCode;
}
/**
* returns the response code to send to the client
*/
public int getResponseCode() {
return responseCode;
}
}
/**
* called to authenticate each incoming request. The implementation
* must return a Failure, Success or Retry object as appropriate :-
* <p>
* Failure means the authentication has completed, but has failed
* due to invalid credentials.
* <p>
* Sucess means that the authentication
* has succeeded, and a Principal object representing the user
* can be retrieved by calling Sucess.getPrincipal() .
* <p>
* Retry means that another HTTP exchange is required. Any response
* headers needing to be sent back to the client are set in the
* given HttpExchange. The response code to be returned must be provided
* in the Retry object. Retry may occur multiple times.
*/
public abstract Result authenticate (HttpExchange exch);
}

View file

@ -0,0 +1,106 @@
/*
* Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.net.httpserver;
import java.util.Base64;
/**
* BasicAuthenticator provides an implementation of HTTP Basic
* authentication. It is an abstract class and must be extended
* to provide an implementation of {@link #checkCredentials(String,String)}
* which is called to verify each incoming request.
*/
public abstract class BasicAuthenticator extends Authenticator {
protected String realm;
/**
* Creates a BasicAuthenticator for the given HTTP realm
* @param realm The HTTP Basic authentication realm
* @throws NullPointerException if the realm is an empty string
*/
public BasicAuthenticator (String realm) {
this.realm = realm;
}
/**
* returns the realm this BasicAuthenticator was created with
* @return the authenticator's realm string.
*/
public String getRealm () {
return realm;
}
public Result authenticate (HttpExchange t)
{
Headers rmap = t.getRequestHeaders();
/*
* look for auth token
*/
String auth = rmap.getFirst ("Authorization");
if (auth == null) {
Headers map = t.getResponseHeaders();
map.set ("WWW-Authenticate", "Basic realm=" + "\""+realm+"\"");
return new Authenticator.Retry (401);
}
int sp = auth.indexOf (' ');
if (sp == -1 || !auth.substring(0, sp).equals ("Basic")) {
return new Authenticator.Failure (401);
}
byte[] b = Base64.getDecoder().decode(auth.substring(sp+1));
String userpass = new String (b);
int colon = userpass.indexOf (':');
String uname = userpass.substring (0, colon);
String pass = userpass.substring (colon+1);
if (checkCredentials (uname, pass)) {
return new Authenticator.Success (
new HttpPrincipal (
uname, realm
)
);
} else {
/* reject the request again with 401 */
Headers map = t.getResponseHeaders();
map.set ("WWW-Authenticate", "Basic realm=" + "\""+realm+"\"");
return new Authenticator.Failure(401);
}
}
/**
* called for each incoming request to verify the
* given name and password in the context of this
* Authenticator's realm. Any caching of credentials
* must be done by the implementation of this method
* @param username the username from the request
* @param password the password from the request
* @return <code>true</code> if the credentials are valid,
* <code>false</code> otherwise.
*/
public abstract boolean checkCredentials (String username, String password);
}

View file

@ -0,0 +1,120 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.net.httpserver;
import java.io.IOException;
import java.util.*;
/**
* A filter used to pre- and post-process incoming requests. Pre-processing occurs
* before the application's exchange handler is invoked, and post-processing
* occurs after the exchange handler returns. Filters
* are organised in chains, and are associated with HttpContext instances.
* <p>
* Each Filter in the chain, invokes the next filter within its own
* doFilter() implementation. The final Filter in the chain invokes the applications
* exchange handler.
* @since 1.6
*/
public abstract class Filter {
protected Filter () {}
/**
* a chain of filters associated with a HttpServer.
* Each filter in the chain is given one of these
* so it can invoke the next filter in the chain
*/
public static class Chain {
/* the last element in the chain must invoke the users
* handler
*/
private ListIterator<Filter> iter;
private HttpHandler handler;
public Chain (List<Filter> filters, HttpHandler handler) {
iter = filters.listIterator();
this.handler = handler;
}
/**
* calls the next filter in the chain, or else
* the users exchange handler, if this is the
* final filter in the chain. The Filter may decide
* to terminate the chain, by not calling this method.
* In this case, the filter <b>must</b> send the
* response to the request, because the application's
* exchange handler will not be invoked.
* @param exchange the HttpExchange
* @throws IOException let exceptions pass up the stack
* @throws NullPointerException if exchange is {@code null}
*/
public void doFilter (HttpExchange exchange) throws IOException {
if (!iter.hasNext()) {
handler.handle (exchange);
} else {
Filter f = iter.next();
f.doFilter (exchange, this);
}
}
}
/**
* Asks this filter to pre/post-process the given exchange. The filter
* can:
* <ul><li>examine or modify the request headers</li>
* <li>filter the request body or the response body, by creating suitable
* filter streams and calling
* {@link HttpExchange#setStreams(InputStream,OutputStream)}</li>
* <li>set attribute Objects in the exchange, which other filters or the
* exchange handler can access.</li>
* <li>decide to either<ol>
* <li>invoke the next filter in the chain, by calling
* {@link Filter.Chain#doFilter(HttpExchange)}</li>
* <li>terminate the chain of invocation, by <b>not</b> calling
* {@link Filter.Chain#doFilter(HttpExchange)}</li></ol>
* <li>if option 1. above taken, then when doFilter() returns all subsequent
* filters in the Chain have been called, and the response headers can be
* examined or modified.</li>
* <li>if option 2. above taken, then this Filter must use the HttpExchange
* to send back an appropriate response</li></ul>
*
* @param exchange the {@code HttpExchange} to be filtered.
* @param chain the Chain which allows the next filter to be invoked.
* @throws IOException may be thrown by any filter module, and if
* caught, must be rethrown again.
* @throws NullPointerException if either exchange or chain are {@code null}
*/
public abstract void doFilter (HttpExchange exchange, Chain chain)
throws IOException;
/**
* returns a short description of this Filter
* @return a string describing the Filter
*/
public abstract String description ();
}

View file

@ -0,0 +1,186 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.net.httpserver;
import java.util.*;
/**
* HTTP request and response headers are represented by this class which implements
* the interface
* {@link java.util.Map}{@literal <}{@link java.lang.String}, {@link java.util.List}
* {@literal <}{@link java.lang.String}{@literal >>}.
* The keys are case-insensitive Strings representing the header names and
* the value associated with each key is
* a {@link List}{@literal <}{@link String}{@literal >} with one
* element for each occurrence of the header name in the request or response.
* <p>
* For example, if a response header instance contains
* one key "HeaderName" with two values "value1 and value2"
* then this object is output as two header lines:
* <blockquote><pre>
* HeaderName: value1
* HeaderName: value2
* </pre></blockquote>
* <p>
* All the normal {@link java.util.Map} methods are provided, but the following
* additional convenience methods are most likely to be used:
* <ul>
* <li>{@link #getFirst(String)} returns a single valued header or the first value of
* a multi-valued header.</li>
* <li>{@link #add(String,String)} adds the given header value to the list for the given key</li>
* <li>{@link #set(String,String)} sets the given header field to the single value given
* overwriting any existing values in the value list.
* </ul><p>
* All methods in this class accept <code>null</code> values for keys and values. However, null
* keys will never will be present in HTTP request headers, and will not be output/sent in response headers.
* Null values can be represented as either a null entry for the key (i.e. the list is null) or
* where the key has a list, but one (or more) of the list's values is null. Null values are output
* as a header line containing the key but no associated value.
* @since 1.6
*/
public class Headers implements Map<String,List<String>> {
HashMap<String,List<String>> map;
public Headers () {map = new HashMap<String,List<String>>(32);}
/* Normalize the key by converting to following form.
* First char upper case, rest lower case.
* key is presumed to be ASCII
*/
private String normalize (String key) {
if (key == null) {
return null;
}
int len = key.length();
if (len == 0) {
return key;
}
char[] b = key.toCharArray();
if (b[0] >= 'a' && b[0] <= 'z') {
b[0] = (char)(b[0] - ('a' - 'A'));
}
for (int i=1; i<len; i++) {
if (b[i] >= 'A' && b[i] <= 'Z') {
b[i] = (char) (b[i] + ('a' - 'A'));
}
}
return new String(b);
}
public int size() {return map.size();}
public boolean isEmpty() {return map.isEmpty();}
public boolean containsKey(Object key) {
if (key == null) {
return false;
}
if (!(key instanceof String)) {
return false;
}
return map.containsKey (normalize((String)key));
}
public boolean containsValue(Object value) {
return map.containsValue(value);
}
public List<String> get(Object key) {
return map.get(normalize((String)key));
}
/**
* returns the first value from the List of String values
* for the given key (if at least one exists).
* @param key the key to search for
* @return the first string value associated with the key
*/
public String getFirst (String key) {
List<String> l = map.get(normalize(key));
if (l == null) {
return null;
}
return l.get(0);
}
public List<String> put(String key, List<String> value) {
return map.put (normalize(key), value);
}
/**
* adds the given value to the list of headers
* for the given key. If the mapping does not
* already exist, then it is created
* @param key the header name
* @param value the header value to add to the header
*/
public void add (String key, String value) {
String k = normalize(key);
List<String> l = map.get(k);
if (l == null) {
l = new LinkedList<String>();
map.put(k,l);
}
l.add (value);
}
/**
* sets the given value as the sole header value
* for the given key. If the mapping does not
* already exist, then it is created
* @param key the header name
* @param value the header value to set.
*/
public void set (String key, String value) {
LinkedList<String> l = new LinkedList<String>();
l.add (value);
put (key, l);
}
public List<String> remove(Object key) {
return map.remove(normalize((String)key));
}
public void putAll(Map<? extends String,? extends List<String>> t) {
map.putAll (t);
}
public void clear() {map.clear();}
public Set<String> keySet() {return map.keySet();}
public Collection<List<String>> values() {return map.values();}
public Set<Map.Entry<String, List<String>>> entrySet() {
return map.entrySet();
}
public boolean equals(Object o) {return map.equals(o);}
public int hashCode() {return map.hashCode();}
}

View file

@ -0,0 +1,112 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.net.httpserver;
import java.net.*;
import java.io.*;
import java.util.*;
/**
* HttpContext represents a mapping between the root URI path of an application
* to a {@link HttpHandler} which is invoked to handle requests destined
* for that path on the associated HttpServer or HttpsServer.
* <p>
* HttpContext instances are created by the create methods in HttpServer
* and HttpsServer
* <p>
* A chain of {@link Filter} objects can be added to a HttpContext. All exchanges processed by the
* context can be pre- and post-processed by each Filter in the chain.
* @since 1.6
*/
public abstract class HttpContext {
protected HttpContext () {
}
/**
* returns the handler for this context
* @return the HttpHandler for this context
*/
public abstract HttpHandler getHandler () ;
/**
* Sets the handler for this context, if not already set.
* @param h the handler to set for this context
* @throws IllegalArgumentException if this context's handler is already set.
* @throws NullPointerException if handler is <code>null</code>
*/
public abstract void setHandler (HttpHandler h) ;
/**
* returns the path this context was created with
* @return this context's path
*/
public abstract String getPath() ;
/**
* returns the server this context was created with
* @return this context's server
*/
public abstract HttpServer getServer () ;
/**
* returns a mutable Map, which can be used to pass
* configuration and other data to Filter modules
* and to the context's exchange handler.
* <p>
* Every attribute stored in this Map will be visible to
* every HttpExchange processed by this context
*/
public abstract Map<String,Object> getAttributes() ;
/**
* returns this context's list of Filters. This is the
* actual list used by the server when dispatching requests
* so modifications to this list immediately affect the
* the handling of exchanges.
*/
public abstract List<Filter> getFilters();
/**
* Sets the Authenticator for this HttpContext. Once an authenticator
* is establised on a context, all client requests must be
* authenticated, and the given object will be invoked to validate each
* request. Each call to this method replaces any previous value set.
* @param auth the authenticator to set. If <code>null</code> then any
* previously set authenticator is removed,
* and client authentication will no longer be required.
* @return the previous Authenticator, if any set, or <code>null</code>
* otherwise.
*/
public abstract Authenticator setAuthenticator (Authenticator auth);
/**
* Returns the currently set Authenticator for this context
* if one exists.
* @return this HttpContext's Authenticator, or <code>null</code>
* if none is set.
*/
public abstract Authenticator getAuthenticator ();
}

View file

@ -0,0 +1,264 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.net.httpserver;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import javax.net.ssl.*;
import java.util.*;
/**
* This class encapsulates a HTTP request received and a
* response to be generated in one exchange. It provides methods
* for examining the request from the client, and for building and
* sending the response.
* <p>
* The typical life-cycle of a HttpExchange is shown in the sequence
* below.
* <ol><li>{@link #getRequestMethod()} to determine the command
* <li>{@link #getRequestHeaders()} to examine the request headers (if needed)
* <li>{@link #getRequestBody()} returns a {@link java.io.InputStream} for reading the request body.
* After reading the request body, the stream is close.
* <li>{@link #getResponseHeaders()} to set any response headers, except content-length
* <li>{@link #sendResponseHeaders(int,long)} to send the response headers. Must be called before
* next step.
* <li>{@link #getResponseBody()} to get a {@link java.io.OutputStream} to send the response body.
* When the response body has been written, the stream must be closed to terminate the exchange.
* </ol>
* <b>Terminating exchanges</b>
* <br>
* Exchanges are terminated when both the request InputStream and response OutputStream are closed.
* Closing the OutputStream, implicitly closes the InputStream (if it is not already closed).
* However, it is recommended
* to consume all the data from the InputStream before closing it.
* The convenience method {@link #close()} does all of these tasks.
* Closing an exchange without consuming all of the request body is not an error
* but may make the underlying TCP connection unusable for following exchanges.
* The effect of failing to terminate an exchange is undefined, but will typically
* result in resources failing to be freed/reused.
* @since 1.6
*/
public abstract class HttpExchange {
protected HttpExchange () {
}
/**
* Returns an immutable Map containing the HTTP headers that were
* included with this request. The keys in this Map will be the header
* names, while the values will be a List of Strings containing each value
* that was included (either for a header that was listed several times,
* or one that accepts a comma-delimited list of values on a single line).
* In either of these cases, the values for the header name will be
* presented in the order that they were included in the request.
* <p>
* The keys in Map are case-insensitive.
* @return a read-only Map which can be used to access request headers
*/
public abstract Headers getRequestHeaders () ;
/**
* Returns a mutable Map into which the HTTP response headers can be stored
* and which will be transmitted as part of this response. The keys in the
* Map will be the header names, while the values must be a List of Strings
* containing each value that should be included multiple times
* (in the order that they should be included).
* <p>
* The keys in Map are case-insensitive.
* @return a writable Map which can be used to set response headers.
*/
public abstract Headers getResponseHeaders () ;
/**
* Get the request URI
*
* @return the request URI
*/
public abstract URI getRequestURI () ;
/**
* Get the request method
* @return the request method
*/
public abstract String getRequestMethod ();
/**
* Get the HttpContext for this exchange
* @return the HttpContext
*/
public abstract HttpContext getHttpContext ();
/**
* Ends this exchange by doing the following in sequence:<ol>
* <li>close the request InputStream, if not already closed;</li>
* <li>close the response OutputStream, if not already closed.</li>
* </ol>
*/
public abstract void close () ;
/**
* returns a stream from which the request body can be read.
* Multiple calls to this method will return the same stream.
* It is recommended that applications should consume (read) all of the
* data from this stream before closing it. If a stream is closed
* before all data has been read, then the close() call will
* read and discard remaining data (up to an implementation specific
* number of bytes).
* @return the stream from which the request body can be read.
*/
public abstract InputStream getRequestBody () ;
/**
* returns a stream to which the response body must be
* written. {@link #sendResponseHeaders(int,long)}) must be called prior to calling
* this method. Multiple calls to this method (for the same exchange)
* will return the same stream. In order to correctly terminate
* each exchange, the output stream must be closed, even if no
* response body is being sent.
* <p>
* Closing this stream implicitly
* closes the InputStream returned from {@link #getRequestBody()}
* (if it is not already closed).
* <P>
* If the call to sendResponseHeaders() specified a fixed response
* body length, then the exact number of bytes specified in that
* call must be written to this stream. If too many bytes are written,
* then write() will throw an IOException. If too few bytes are written
* then the stream close() will throw an IOException. In both cases,
* the exchange is aborted and the underlying TCP connection closed.
* @return the stream to which the response body is written
*/
public abstract OutputStream getResponseBody () ;
/**
* Starts sending the response back to the client using the current set of response headers
* and the numeric response code as specified in this method. The response body length is also specified
* as follows. If the response length parameter is greater than zero, this specifies an exact
* number of bytes to send and the application must send that exact amount of data.
* If the response length parameter is {@code zero}, then chunked transfer encoding is
* used and an arbitrary amount of data may be sent. The application terminates the
* response body by closing the OutputStream. If response length has the value {@code -1}
* then no response body is being sent.
* <p>
* If the content-length response header has not already been set then
* this is set to the appropriate value depending on the response length parameter.
* <p>
* This method must be called prior to calling {@link #getResponseBody()}.
* @param rCode the response code to send
* @param responseLength if {@literal > 0}, specifies a fixed response
* body length and that exact number of bytes must be written
* to the stream acquired from getResponseBody(), or else
* if equal to 0, then chunked encoding is used,
* and an arbitrary number of bytes may be written.
* if {@literal <= -1}, then no response body length is specified and
* no response body may be written.
* @see HttpExchange#getResponseBody()
*/
public abstract void sendResponseHeaders (int rCode, long responseLength) throws IOException ;
/**
* Returns the address of the remote entity invoking this request
* @return the InetSocketAddress of the caller
*/
public abstract InetSocketAddress getRemoteAddress ();
/**
* Returns the response code, if it has already been set
* @return the response code, if available. {@code -1} if not available yet.
*/
public abstract int getResponseCode ();
/**
* Returns the local address on which the request was received
* @return the InetSocketAddress of the local interface
*/
public abstract InetSocketAddress getLocalAddress ();
/**
* Returns the protocol string from the request in the form
* <i>protocol/majorVersion.minorVersion</i>. For example,
* "HTTP/1.1"
* @return the protocol string from the request
*/
public abstract String getProtocol ();
/**
* Filter modules may store arbitrary objects with HttpExchange
* instances as an out-of-band communication mechanism. Other Filters
* or the exchange handler may then access these objects.
* <p>
* Each Filter class will document the attributes which they make
* available.
* @param name the name of the attribute to retrieve
* @return the attribute object, or null if it does not exist
* @throws NullPointerException if name is {@code null}
*/
public abstract Object getAttribute (String name) ;
/**
* Filter modules may store arbitrary objects with HttpExchange
* instances as an out-of-band communication mechanism. Other Filters
* or the exchange handler may then access these objects.
* <p>
* Each Filter class will document the attributes which they make
* available.
* @param name the name to associate with the attribute value
* @param value the object to store as the attribute value. {@code null}
* value is permitted.
* @throws NullPointerException if name is {@code null}
*/
public abstract void setAttribute (String name, Object value) ;
/**
* Used by Filters to wrap either (or both) of this exchange's InputStream
* and OutputStream, with the given filtered streams so
* that subsequent calls to {@link #getRequestBody()} will
* return the given {@link java.io.InputStream}, and calls to
* {@link #getResponseBody()} will return the given
* {@link java.io.OutputStream}. The streams provided to this
* call must wrap the original streams, and may be (but are not
* required to be) sub-classes of {@link java.io.FilterInputStream}
* and {@link java.io.FilterOutputStream}.
* @param i the filtered input stream to set as this object's inputstream,
* or {@code null} if no change.
* @param o the filtered output stream to set as this object's outputstream,
* or {@code null} if no change.
*/
public abstract void setStreams (InputStream i, OutputStream o);
/**
* If an authenticator is set on the HttpContext that owns this exchange,
* then this method will return the {@link HttpPrincipal} that represents
* the authenticated user for this HttpExchange.
* @return the HttpPrincipal, or {@code null} if no authenticator is set.
*/
public abstract HttpPrincipal getPrincipal ();
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.net.httpserver;
import java.io.IOException;
/**
* A handler which is invoked to process HTTP exchanges. Each
* HTTP exchange is handled by one of these handlers.
* @since 1.6
*/
public interface HttpHandler {
/**
* Handle the given request and generate an appropriate response.
* See {@link HttpExchange} for a description of the steps
* involved in handling an exchange.
* @param exchange the exchange containing the request from the
* client and used to send the response
* @throws NullPointerException if exchange is <code>null</code>
*/
public abstract void handle (HttpExchange exchange) throws IOException;
}

View file

@ -0,0 +1,104 @@
/*
* Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.net.httpserver;
import java.net.*;
import java.io.*;
import java.util.*;
import java.security.Principal;
/**
* Represents a user authenticated by HTTP Basic or Digest
* authentication.
*/
public class HttpPrincipal implements Principal {
private String username, realm;
/**
* creates a HttpPrincipal from the given username and realm
* @param username The name of the user within the realm
* @param realm The realm.
* @throws NullPointerException if either username or realm are null
*/
public HttpPrincipal (String username, String realm) {
if (username == null || realm == null) {
throw new NullPointerException();
}
this.username = username;
this.realm = realm;
}
/**
* Compares two HttpPrincipal. Returns <code>true</code>
* if <i>another</i> is an instance of HttpPrincipal, and its
* username and realm are equal to this object's username
* and realm. Returns <code>false</code> otherwise.
*/
public boolean equals (Object another) {
if (!(another instanceof HttpPrincipal)) {
return false;
}
HttpPrincipal theother = (HttpPrincipal)another;
return (username.equals(theother.username) &&
realm.equals(theother.realm));
}
/**
* returns the contents of this principal in the form
* <i>realm:username</i>
*/
public String getName() {
return username;
}
/**
* returns the username this object was created with.
*/
public String getUsername() {
return username;
}
/**
* returns the realm this object was created with.
*/
public String getRealm() {
return realm;
}
/**
* returns a hashcode for this HttpPrincipal. This is calculated
* as <code>(getUsername()+getRealm().hashCode()</code>
*/
public int hashCode() {
return (username+realm).hashCode();
}
/**
* returns the same string as getName()
*/
public String toString() {
return getName();
}
}

View file

@ -0,0 +1,262 @@
/*
* Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.net.httpserver;
import java.net.*;
import java.io.*;
import java.nio.*;
import java.security.*;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import javax.net.ssl.*;
import com.sun.net.httpserver.spi.HttpServerProvider;
/**
* This class implements a simple HTTP server. A HttpServer is bound to an IP address
* and port number and listens for incoming TCP connections from clients on this address.
* The sub-class {@link HttpsServer} implements a server which handles HTTPS requests.
* <p>
* One or more {@link HttpHandler} objects must be associated with a server
* in order to process requests. Each such HttpHandler is registered
* with a root URI path which represents the
* location of the application or service on this server. The mapping of a handler
* to a HttpServer is encapsulated by a {@link HttpContext} object. HttpContexts
* are created by calling {@link #createContext(String,HttpHandler)}.
* Any request for which no handler can be found is rejected with a 404 response.
* Management of threads can be done external to this object by providing a
* {@link java.util.concurrent.Executor} object. If none is provided a default
* implementation is used.
* <p>
* <a id="mapping_description"></a>
* <b>Mapping request URIs to HttpContext paths</b><p>
* When a HTTP request is received,
* the appropriate HttpContext (and handler) is located by finding the context
* whose path is the longest matching prefix of the request URI's path.
* Paths are matched literally, which means that the strings are compared
* case sensitively, and with no conversion to or from any encoded forms.
* For example. Given a HttpServer with the following HttpContexts configured.
* <table class="striped"><caption style="display:none">description</caption>
* <thead>
* <tr><th scope="col"><i>Context</i></th><th scope="col"><i>Context path</i></th></tr>
* </thead>
* <tbody>
* <tr><th scope="row">ctx1</th><td>"/"</td></tr>
* <tr><th scope="row">ctx2</th><td>"/apps/"</td></tr>
* <tr><th scope="row">ctx3</th><td>"/apps/foo/"</td></tr>
* </tbody>
* </table>
* <p>
* the following table shows some request URIs and which, if any context they would
* match with.
* <table class="striped"><caption style="display:none">description</caption>
* <thead>
* <tr><th scope="col"><i>Request URI</i></th><th scope="col"><i>Matches context</i></th></tr>
* </thead>
* <tbody>
* <tr><th scope="row">"http://foo.com/apps/foo/bar"</th><td>ctx3</td></tr>
* <tr><th scope="row">"http://foo.com/apps/Foo/bar"</th><td>no match, wrong case</td></tr>
* <tr><th scope="row">"http://foo.com/apps/app1"</th><td>ctx2</td></tr>
* <tr><th scope="row">"http://foo.com/foo"</th><td>ctx1</td></tr>
* </tbody>
* </table>
* <p>
* <b>Note about socket backlogs</b><p>
* When binding to an address and port number, the application can also specify an integer
* <i>backlog</i> parameter. This represents the maximum number of incoming TCP connections
* which the system will queue internally. Connections are queued while they are waiting to
* be accepted by the HttpServer. When the limit is reached, further connections may be
* rejected (or possibly ignored) by the underlying TCP implementation. Setting the right
* backlog value is a compromise between efficient resource usage in the TCP layer (not setting
* it too high) and allowing adequate throughput of incoming requests (not setting it too low).
* @since 1.6
*/
public abstract class HttpServer {
/**
*/
protected HttpServer () {
}
/**
* creates a HttpServer instance which is initially not bound to any local address/port.
* The HttpServer is acquired from the currently installed {@link HttpServerProvider}
* The server must be bound using {@link #bind(InetSocketAddress,int)} before it can be used.
* @throws IOException
*/
public static HttpServer create () throws IOException {
return create (null, 0);
}
/**
* Create a <code>HttpServer</code> instance which will bind to the
* specified {@link java.net.InetSocketAddress} (IP address and port number)
*
* A maximum backlog can also be specified. This is the maximum number of
* queued incoming connections to allow on the listening socket.
* Queued TCP connections exceeding this limit may be rejected by the TCP implementation.
* The HttpServer is acquired from the currently installed {@link HttpServerProvider}
*
* @param addr the address to listen on, if <code>null</code> then bind() must be called
* to set the address
* @param backlog the socket backlog. If this value is less than or equal to zero,
* then a system default value is used.
* @throws BindException if the server cannot bind to the requested address,
* or if the server is already bound.
* @throws IOException
*/
public static HttpServer create (
InetSocketAddress addr, int backlog
) throws IOException {
HttpServerProvider provider = HttpServerProvider.provider();
return provider.createHttpServer (addr, backlog);
}
/**
* Binds a currently unbound HttpServer to the given address and port number.
* A maximum backlog can also be specified. This is the maximum number of
* queued incoming connections to allow on the listening socket.
* Queued TCP connections exceeding this limit may be rejected by the TCP implementation.
* @param addr the address to listen on
* @param backlog the socket backlog. If this value is less than or equal to zero,
* then a system default value is used.
* @throws BindException if the server cannot bind to the requested address or if the server
* is already bound.
* @throws NullPointerException if addr is <code>null</code>
*/
public abstract void bind (InetSocketAddress addr, int backlog) throws IOException;
/**
* Starts this server in a new background thread. The background thread
* inherits the priority, thread group and context class loader
* of the caller.
*/
public abstract void start () ;
/**
* sets this server's {@link java.util.concurrent.Executor} object. An
* Executor must be established before {@link #start()} is called.
* All HTTP requests are handled in tasks given to the executor.
* If this method is not called (before start()) or if it is
* called with a <code>null</code> Executor, then
* a default implementation is used, which uses the thread
* which was created by the {@link #start()} method.
* @param executor the Executor to set, or <code>null</code> for default
* implementation
* @throws IllegalStateException if the server is already started
*/
public abstract void setExecutor (Executor executor);
/**
* returns this server's Executor object if one was specified with
* {@link #setExecutor(Executor)}, or <code>null</code> if none was
* specified.
* @return the Executor established for this server or <code>null</code> if not set.
*/
public abstract Executor getExecutor () ;
/**
* stops this server by closing the listening socket and disallowing
* any new exchanges from being processed. The method will then block
* until all current exchange handlers have completed or else when
* approximately <i>delay</i> seconds have elapsed (whichever happens
* sooner). Then, all open TCP connections are closed, the background
* thread created by start() exits, and the method returns.
* Once stopped, a HttpServer cannot be re-used.
*
* @param delay the maximum time in seconds to wait until exchanges have finished.
* @throws IllegalArgumentException if delay is less than zero.
*/
public abstract void stop (int delay);
/**
* Creates a HttpContext. A HttpContext represents a mapping from a
* URI path to a exchange handler on this HttpServer. Once created, all requests
* received by the server for the path will be handled by calling
* the given handler object. The context is identified by the path, and
* can later be removed from the server using this with the {@link #removeContext(String)} method.
* <p>
* The path specifies the root URI path for this context. The first character of path must be
* '/'. <p>
* The class overview describes how incoming request URIs are <a href="#mapping_description">mapped</a>
* to HttpContext instances.
* @param path the root URI path to associate the context with
* @param handler the handler to invoke for incoming requests.
* @throws IllegalArgumentException if path is invalid, or if a context
* already exists for this path
* @throws NullPointerException if either path, or handler are <code>null</code>
*/
public abstract HttpContext createContext (String path, HttpHandler handler) ;
/**
* Creates a HttpContext without initially specifying a handler. The handler must later be specified using
* {@link HttpContext#setHandler(HttpHandler)}. A HttpContext represents a mapping from a
* URI path to an exchange handler on this HttpServer. Once created, and when
* the handler has been set, all requests
* received by the server for the path will be handled by calling
* the handler object. The context is identified by the path, and
* can later be removed from the server using this with the {@link #removeContext(String)} method.
* <p>
* The path specifies the root URI path for this context. The first character of path must be
* '/'. <p>
* The class overview describes how incoming request URIs are <a href="#mapping_description">mapped</a>
* to HttpContext instances.
* @param path the root URI path to associate the context with
* @throws IllegalArgumentException if path is invalid, or if a context
* already exists for this path
* @throws NullPointerException if path is <code>null</code>
*/
public abstract HttpContext createContext (String path) ;
/**
* Removes the context identified by the given path from the server.
* Removing a context does not affect exchanges currently being processed
* but prevents new ones from being accepted.
* @param path the path of the handler to remove
* @throws IllegalArgumentException if no handler corresponding to this
* path exists.
* @throws NullPointerException if path is <code>null</code>
*/
public abstract void removeContext (String path) throws IllegalArgumentException ;
/**
* Removes the given context from the server.
* Removing a context does not affect exchanges currently being processed
* but prevents new ones from being accepted.
* @param context the context to remove
* @throws NullPointerException if context is <code>null</code>
*/
public abstract void removeContext (HttpContext context) ;
/**
* returns the address this server is listening on
* @return the address/port number the server is listening on
*/
public abstract InetSocketAddress getAddress() ;
}

View file

@ -0,0 +1,116 @@
/*
* Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.net.httpserver;
import java.net.*;
import java.io.*;
import java.nio.*;
import java.security.*;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import javax.net.ssl.*;
/**
* This class is used to configure the https parameters for each incoming
* https connection on a HttpsServer. Applications need to override
* the {@link #configure(HttpsParameters)} method in order to change
* the default configuration.
* <p>
* The following <a id="example">example</a> shows how this may be done:
*
* <blockquote><pre>
* SSLContext sslContext = SSLContext.getInstance (....);
* HttpsServer server = HttpsServer.create();
*
* server.setHttpsConfigurator (new HttpsConfigurator(sslContext) {
* public void configure (HttpsParameters params) {
*
* // get the remote address if needed
* InetSocketAddress remote = params.getClientAddress();
*
* SSLContext c = getSSLContext();
*
* // get the default parameters
* SSLParameters sslparams = c.getDefaultSSLParameters();
* if (remote.equals (...) ) {
* // modify the default set for client x
* }
*
* params.setSSLParameters(sslparams);
* }
* });
* </pre></blockquote>
* @since 1.6
*/
public class HttpsConfigurator {
private SSLContext context;
/**
* Creates an Https configuration, with the given SSLContext.
* @param context the SSLContext to use for this configurator
* @throws NullPointerException if no SSLContext supplied
*/
public HttpsConfigurator (SSLContext context) {
if (context == null) {
throw new NullPointerException ("null SSLContext");
}
this.context = context;
}
/**
* Returns the SSLContext for this HttpsConfigurator.
* @return the SSLContext
*/
public SSLContext getSSLContext() {
return context;
}
//BEGIN_TIGER_EXCLUDE
/**
* Called by the HttpsServer to configure the parameters
* for a https connection currently being established.
* The implementation of configure() must call
* {@link HttpsParameters#setSSLParameters(SSLParameters)}
* in order to set the SSL parameters for the connection.
* <p>
* The default implementation of this method uses the
* SSLParameters returned from <p>
* {@code getSSLContext().getDefaultSSLParameters()}
* <p>
* configure() may be overridden in order to modify this behavior.
* See, the example <a href="#example">above</a>.
* @param params the HttpsParameters to be configured.
*
* @since 1.6
*/
public void configure (HttpsParameters params) {
params.setSSLParameters (getSSLContext().getDefaultSSLParameters());
}
//END_TIGER_EXCLUDE
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.net.httpserver;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import javax.net.ssl.*;
import java.util.*;
/**
* This class encapsulates a HTTPS request received and a
* response to be generated in one exchange and defines
* the extensions to HttpExchange that are specific to the HTTPS protocol.
* @since 1.6
*/
public abstract class HttpsExchange extends HttpExchange {
protected HttpsExchange () {
}
/**
* Get the SSLSession for this exchange.
* @return the SSLSession
*/
public abstract SSLSession getSSLSession ();
}

View file

@ -0,0 +1,164 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.net.httpserver;
import java.net.InetSocketAddress;
//BEGIN_TIGER_EXCLUDE
import javax.net.ssl.SSLParameters;
//END_TIGER_EXCLUDE
/**
* Represents the set of parameters for each https
* connection negotiated with clients. One of these
* is created and passed to
* {@link HttpsConfigurator#configure(HttpsParameters)}
* for every incoming https connection,
* in order to determine the parameters to use.
* <p>
* The underlying SSL parameters may be established either
* via the set/get methods of this class, or else via
* a {@link javax.net.ssl.SSLParameters} object. SSLParameters
* is the preferred method, because in the future,
* additional configuration capabilities may be added to that class, and
* it is easier to determine the set of supported parameters and their
* default values with SSLParameters. Also, if an SSLParameters object is
* provided via
* {@link #setSSLParameters(SSLParameters)} then those parameter settings
* are used, and any settings made in this object are ignored.
* @since 1.6
*/
public abstract class HttpsParameters {
private String[] cipherSuites;
private String[] protocols;
private boolean wantClientAuth;
private boolean needClientAuth;
protected HttpsParameters() {}
/**
* Returns the HttpsConfigurator for this HttpsParameters.
*/
public abstract HttpsConfigurator getHttpsConfigurator();
/**
* Returns the address of the remote client initiating the
* connection.
*/
public abstract InetSocketAddress getClientAddress();
//BEGIN_TIGER_EXCLUDE
/**
* Sets the SSLParameters to use for this HttpsParameters.
* The parameters must be supported by the SSLContext contained
* by the HttpsConfigurator associated with this HttpsParameters.
* If no parameters are set, then the default behavior is to use
* the default parameters from the associated SSLContext.
* @param params the SSLParameters to set. If <code>null</code>
* then the existing parameters (if any) remain unchanged.
* @throws IllegalArgumentException if any of the parameters are
* invalid or unsupported.
*/
public abstract void setSSLParameters (SSLParameters params);
//END_TIGER_EXCLUDE
/**
* Returns a copy of the array of ciphersuites or null if none
* have been set.
*
* @return a copy of the array of ciphersuites or null if none
* have been set.
*/
public String[] getCipherSuites() {
return cipherSuites != null ? cipherSuites.clone() : null;
}
/**
* Sets the array of ciphersuites.
*
* @param cipherSuites the array of ciphersuites (or null)
*/
public void setCipherSuites(String[] cipherSuites) {
this.cipherSuites = cipherSuites != null ? cipherSuites.clone() : null;
}
/**
* Returns a copy of the array of protocols or null if none
* have been set.
*
* @return a copy of the array of protocols or null if none
* have been set.
*/
public String[] getProtocols() {
return protocols != null ? protocols.clone() : null;
}
/**
* Sets the array of protocols.
*
* @param protocols the array of protocols (or null)
*/
public void setProtocols(String[] protocols) {
this.protocols = protocols != null ? protocols.clone() : null;
}
/**
* Returns whether client authentication should be requested.
*
* @return whether client authentication should be requested.
*/
public boolean getWantClientAuth() {
return wantClientAuth;
}
/**
* Sets whether client authentication should be requested. Calling
* this method clears the <code>needClientAuth</code> flag.
*
* @param wantClientAuth whether client authentication should be requested
*/
public void setWantClientAuth(boolean wantClientAuth) {
this.wantClientAuth = wantClientAuth;
}
/**
* Returns whether client authentication should be required.
*
* @return whether client authentication should be required.
*/
public boolean getNeedClientAuth() {
return needClientAuth;
}
/**
* Sets whether client authentication should be required. Calling
* this method clears the <code>wantClientAuth</code> flag.
*
* @param needClientAuth whether client authentication should be required
*/
public void setNeedClientAuth(boolean needClientAuth) {
this.needClientAuth = needClientAuth;
}
}

View file

@ -0,0 +1,104 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.net.httpserver;
import java.net.*;
import java.io.*;
import java.nio.*;
import java.security.*;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import javax.net.ssl.*;
import com.sun.net.httpserver.spi.*;
/**
* This class is an extension of {@link HttpServer} which provides
* support for HTTPS. <p>
* A HttpsServer must have an associated {@link HttpsConfigurator} object
* which is used to establish the SSL configuration for the SSL connections.
* <p>
* All other configuration is the same as for HttpServer.
* @since 1.6
*/
public abstract class HttpsServer extends HttpServer {
/**
*/
protected HttpsServer () {
}
/**
* creates a HttpsServer instance which is initially not bound to any local address/port.
* The HttpsServer is acquired from the currently installed {@link HttpServerProvider}
* The server must be bound using {@link #bind(InetSocketAddress,int)} before it can be used.
* The server must also have a HttpsConfigurator established with {@link #setHttpsConfigurator(HttpsConfigurator)}
* @throws IOException
*/
public static HttpsServer create () throws IOException {
return create (null, 0);
}
/**
* Create a <code>HttpsServer</code> instance which will bind to the
* specified {@link java.net.InetSocketAddress} (IP address and port number)
*
* A maximum backlog can also be specified. This is the maximum number of
* queued incoming connections to allow on the listening socket.
* Queued TCP connections exceeding this limit may be rejected by the TCP implementation.
* The HttpsServer is acquired from the currently installed {@link HttpServerProvider}
* The server must have a HttpsConfigurator established with {@link #setHttpsConfigurator(HttpsConfigurator)}
*
* @param addr the address to listen on, if <code>null</code> then bind() must be called
* to set the address
* @param backlog the socket backlog. If this value is less than or equal to zero,
* then a system default value is used.
* @throws BindException if the server cannot bind to the requested address,
* or if the server is already bound.
* @throws IOException
*/
public static HttpsServer create (
InetSocketAddress addr, int backlog
) throws IOException {
HttpServerProvider provider = HttpServerProvider.provider();
return provider.createHttpsServer (addr, backlog);
}
/**
* Sets this server's {@link HttpsConfigurator} object.
* @param config the HttpsConfigurator to set
* @throws NullPointerException if config is null.
*/
public abstract void setHttpsConfigurator (HttpsConfigurator config) ;
/**
* Gets this server's {@link HttpsConfigurator} object, if it has been set.
* @return the HttpsConfigurator for this server, or <code>null</code> if not set.
*/
public abstract HttpsConfigurator getHttpsConfigurator ();
}

View file

@ -0,0 +1,126 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
Provides a simple high-level Http server API, which can be used to build
embedded HTTP servers. Both "http" and "https" are supported. The API provides
a partial implementation of RFC <a href="http://www.ietf.org/rfc/rfc2616.txt">2616</a> (HTTP 1.1)
and RFC <a href="http://www.ietf.org/rfc/rfc2818.txt">2818</a> (HTTP over TLS).
Any HTTP functionality not provided by this API can be implemented by application code
using the API.
<p>
Programmers must implement the {@link com.sun.net.httpserver.HttpHandler} interface. This interface
provides a callback which is invoked to handle incoming requests from clients.
A HTTP request and its response is known as an exchange. HTTP exchanges are
represented by the {@link com.sun.net.httpserver.HttpExchange} class.
The {@link com.sun.net.httpserver.HttpServer} class is used to listen for incoming TCP connections
and it dispatches requests on these connections to handlers which have been
registered with the server.
<p>
A minimal Http server example is shown below:
<blockquote><pre>
class MyHandler implements HttpHandler {
public void handle(HttpExchange t) throws IOException {
InputStream is = t.getRequestBody();
read(is); // .. read the request body
String response = "This is the response";
t.sendResponseHeaders(200, response.length());
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
}
...
HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
server.createContext("/applications/myapp", new MyHandler());
server.setExecutor(null); // creates a default executor
server.start();
</pre></blockquote>
<p>The example above creates a simple HttpServer which uses the calling
application thread to invoke the handle() method for incoming http
requests directed to port 8000, and to the path /applications/myapp/.
<p>
The {@link com.sun.net.httpserver.HttpExchange} class encapsulates everything an application needs to
process incoming requests and to generate appropriate responses.
<p>
Registering a handler with a HttpServer creates a {@link com.sun.net.httpserver.HttpContext} object and
{@link com.sun.net.httpserver.Filter}
objects can be added to the returned context. Filters are used to perform automatic pre- and
post-processing of exchanges before they are passed to the exchange handler.
<p>
For sensitive information, a {@link com.sun.net.httpserver.HttpsServer} can
be used to process "https" requests secured by the SSL or TLS protocols.
A HttpsServer must be provided with a
{@link com.sun.net.httpserver.HttpsConfigurator} object, which contains an
initialized {@link javax.net.ssl.SSLContext}.
HttpsConfigurator can be used to configure the
cipher suites and other SSL operating parameters.
A simple example SSLContext could be created as follows:
<blockquote><pre>
char[] passphrase = "passphrase".toCharArray();
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("testkeys"), passphrase);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, passphrase);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);
SSLContext ssl = SSLContext.getInstance("TLS");
ssl.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
</pre></blockquote>
<p>
In the example above, a keystore file called "testkeys", created with the keytool utility
is used as a certificate store for client and server certificates.
The following code shows how the SSLContext is then used in a HttpsConfigurator
and how the SSLContext and HttpsConfigurator are linked to the HttpsServer.
<blockquote><pre>
server.setHttpsConfigurator (new HttpsConfigurator(sslContext) {
public void configure (HttpsParameters params) {
// get the remote address if needed
InetSocketAddress remote = params.getClientAddress();
SSLContext c = getSSLContext();
// get the default parameters
SSLParameters sslparams = c.getDefaultSSLParameters();
if (remote.equals (...) ) {
// modify the default set for client x
}
params.setSSLParameters(sslparams);
// statement above could throw IAE if any params invalid.
// eg. if app has a UI and parameters supplied by a user.
}
});
</pre></blockquote>
@since 1.6
*/
package com.sun.net.httpserver;

View file

@ -0,0 +1,179 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.net.httpserver.spi;
import java.io.IOException;
import java.net.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Iterator;
import java.util.ServiceLoader;
import java.util.ServiceConfigurationError;
import com.sun.net.httpserver.*;
/**
* Service provider class for HttpServer.
* Sub-classes of HttpServerProvider provide an implementation of
* {@link HttpServer} and associated classes. Applications do not normally use
* this class. See {@link #provider()} for how providers are found and loaded.
*/
public abstract class HttpServerProvider {
/**
* creates a HttpServer from this provider
*
* @param addr
* the address to bind to. May be {@code null}
*
* @param backlog
* the socket backlog. A value of {@code zero} means the systems default
*/
public abstract HttpServer createHttpServer(InetSocketAddress addr,
int backlog)
throws IOException;
/**
* creates a HttpsServer from this provider
*
* @param addr
* the address to bind to. May be {@code null}
*
* @param backlog
* the socket backlog. A value of {@code zero} means the systems default
*/
public abstract HttpsServer createHttpsServer(InetSocketAddress addr,
int backlog)
throws IOException;
private static final Object lock = new Object();
private static HttpServerProvider provider = null;
/**
* Initializes a new instance of this class.
*
* @throws SecurityException
* If a security manager has been installed and it denies
* {@link RuntimePermission}{@code ("httpServerProvider")}
*/
protected HttpServerProvider() {
SecurityManager sm = System.getSecurityManager();
if (sm != null)
sm.checkPermission(new RuntimePermission("httpServerProvider"));
}
private static boolean loadProviderFromProperty() {
String cn = System.getProperty("com.sun.net.httpserver.HttpServerProvider");
if (cn == null)
return false;
try {
@SuppressWarnings("deprecation")
Object o = Class.forName(cn, true,
ClassLoader.getSystemClassLoader()).newInstance();
provider = (HttpServerProvider)o;
return true;
} catch (ClassNotFoundException |
IllegalAccessException |
InstantiationException |
SecurityException x) {
throw new ServiceConfigurationError(null, x);
}
}
private static boolean loadProviderAsService() {
Iterator<HttpServerProvider> i =
ServiceLoader.load(HttpServerProvider.class,
ClassLoader.getSystemClassLoader())
.iterator();
for (;;) {
try {
if (!i.hasNext())
return false;
provider = i.next();
return true;
} catch (ServiceConfigurationError sce) {
if (sce.getCause() instanceof SecurityException) {
// Ignore the security exception, try the next provider
continue;
}
throw sce;
}
}
}
/**
* Returns the system wide default HttpServerProvider for this invocation of
* the Java virtual machine.
*
* <p> The first invocation of this method locates the default provider
* object as follows: </p>
*
* <ol>
*
* <li><p> If the system property
* {@code com.sun.net.httpserver.HttpServerProvider} is defined then it
* is taken to be the fully-qualified name of a concrete provider class.
* The class is loaded and instantiated; if this process fails then an
* unspecified unchecked error or exception is thrown. </p></li>
*
* <li><p> If a provider class has been installed in a jar file that is
* visible to the system class loader, and that jar file contains a
* provider-configuration file named
* {@code com.sun.net.httpserver.HttpServerProvider} in the resource
* directory {@code META-INF/services}, then the first class name
* specified in that file is taken. The class is loaded and
* instantiated; if this process fails then an unspecified unchecked error
* or exception is thrown. </p></li>
*
* <li><p> Finally, if no provider has been specified by any of the above
* means then the system-default provider class is instantiated and the
* result is returned. </p></li>
*
* </ol>
*
* <p> Subsequent invocations of this method return the provider that was
* returned by the first invocation. </p>
*
* @return The system-wide default HttpServerProvider
*/
public static HttpServerProvider provider () {
synchronized (lock) {
if (provider != null)
return provider;
return (HttpServerProvider)AccessController
.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
if (loadProviderFromProperty())
return provider;
if (loadProviderAsService())
return provider;
provider = new sun.net.httpserver.DefaultHttpServerProvider();
return provider;
}
});
}
}
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* Provides a pluggable service provider interface, which allows the HTTP server
* implementation to be replaced with other implementations.
*/
package com.sun.net.httpserver.spi;

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* Defines the JDK-specific HTTP server API.
*
* @uses com.sun.net.httpserver.spi.HttpServerProvider
*
* @moduleGraph
* @since 9
*/
module jdk.httpserver {
exports com.sun.net.httpserver;
exports com.sun.net.httpserver.spi;
uses com.sun.net.httpserver.spi.HttpServerProvider;
}

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import com.sun.net.httpserver.*;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.*;
import javax.security.auth.*;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;
public class AuthFilter extends Filter {
private Authenticator authenticator;
public AuthFilter (Authenticator authenticator) {
this.authenticator = authenticator;
}
public String description () {
return "Authentication filter";
}
public void setAuthenticator (Authenticator a) {
authenticator = a;
}
public void consumeInput (HttpExchange t) throws IOException {
InputStream i = t.getRequestBody();
byte[] b = new byte [4096];
while (i.read (b) != -1);
i.close ();
}
/**
* The filter's implementation, which is invoked by the server
*/
public void doFilter (HttpExchange t, Filter.Chain chain) throws IOException
{
if (authenticator != null) {
Authenticator.Result r = authenticator.authenticate (t);
if (r instanceof Authenticator.Success) {
Authenticator.Success s = (Authenticator.Success)r;
ExchangeImpl e = ExchangeImpl.get (t);
e.setPrincipal (s.getPrincipal());
chain.doFilter (t);
} else if (r instanceof Authenticator.Retry) {
Authenticator.Retry ry = (Authenticator.Retry)r;
consumeInput (t);
t.sendResponseHeaders (ry.getResponseCode(), -1);
} else if (r instanceof Authenticator.Failure) {
Authenticator.Failure f = (Authenticator.Failure)r;
consumeInput (t);
t.sendResponseHeaders (f.getResponseCode(), -1);
}
} else {
chain.doFilter (t);
}
}
}

View file

@ -0,0 +1,184 @@
/*
* Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.io.*;
import java.net.*;
import com.sun.net.httpserver.*;
import com.sun.net.httpserver.spi.*;
class ChunkedInputStream extends LeftOverInputStream {
ChunkedInputStream (ExchangeImpl t, InputStream src) {
super (t, src);
}
private int remaining;
/* true when a chunk header needs to be read */
private boolean needToReadHeader = true;
final static char CR = '\r';
final static char LF = '\n';
/*
* Maximum chunk header size of 2KB + 2 bytes for CRLF
*/
private final static int MAX_CHUNK_HEADER_SIZE = 2050;
private int numeric (char[] arr, int nchars) throws IOException {
assert arr.length >= nchars;
int len = 0;
for (int i=0; i<nchars; i++) {
char c = arr[i];
int val=0;
if (c>='0' && c <='9') {
val = c - '0';
} else if (c>='a' && c<= 'f') {
val = c - 'a' + 10;
} else if (c>='A' && c<= 'F') {
val = c - 'A' + 10;
} else {
throw new IOException ("invalid chunk length");
}
len = len * 16 + val;
}
return len;
}
/* read the chunk header line and return the chunk length
* any chunk extensions are ignored
*/
private int readChunkHeader () throws IOException {
boolean gotCR = false;
int c;
char[] len_arr = new char [16];
int len_size = 0;
boolean end_of_len = false;
int read = 0;
while ((c=in.read())!= -1) {
char ch = (char) c;
read++;
if ((len_size == len_arr.length -1) ||
(read > MAX_CHUNK_HEADER_SIZE))
{
throw new IOException ("invalid chunk header");
}
if (gotCR) {
if (ch == LF) {
int l = numeric (len_arr, len_size);
return l;
} else {
gotCR = false;
}
if (!end_of_len) {
len_arr[len_size++] = ch;
}
} else {
if (ch == CR) {
gotCR = true;
} else if (ch == ';') {
end_of_len = true;
} else if (!end_of_len) {
len_arr[len_size++] = ch;
}
}
}
throw new IOException ("end of stream reading chunk header");
}
protected int readImpl (byte[]b, int off, int len) throws IOException {
if (eof) {
return -1;
}
if (needToReadHeader) {
remaining = readChunkHeader();
if (remaining == 0) {
eof = true;
consumeCRLF();
t.getServerImpl().requestCompleted (t.getConnection());
return -1;
}
needToReadHeader = false;
}
if (len > remaining) {
len = remaining;
}
int n = in.read(b, off, len);
if (n > -1) {
remaining -= n;
}
if (remaining == 0) {
needToReadHeader = true;
consumeCRLF();
}
return n;
}
private void consumeCRLF () throws IOException {
char c;
c = (char)in.read(); /* CR */
if (c != CR) {
throw new IOException ("invalid chunk end");
}
c = (char)in.read(); /* LF */
if (c != LF) {
throw new IOException ("invalid chunk end");
}
}
/**
* returns the number of bytes available to read in the current chunk
* which may be less than the real amount, but we'll live with that
* limitation for the moment. It only affects potential efficiency
* rather than correctness.
*/
public int available () throws IOException {
if (eof || closed) {
return 0;
}
int n = in.available();
return n > remaining? remaining: n;
}
/* called after the stream is closed to see if bytes
* have been read from the underlying channel
* and buffered internally
*/
public boolean isDataBuffered () throws IOException {
assert eof;
return in.available() > 0;
}
public boolean markSupported () {return false;}
public void mark (int l) {
}
public void reset () throws IOException {
throw new IOException ("mark/reset not supported");
}
}

View file

@ -0,0 +1,163 @@
/*
* Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.io.*;
import java.net.*;
import com.sun.net.httpserver.*;
import com.sun.net.httpserver.spi.*;
/**
* a class which allows the caller to write an arbitrary
* number of bytes to an underlying stream.
* normal close() does not close the underlying stream
*
* This class is buffered.
*
* Each chunk is written in one go as :-
* abcd\r\nxxxxxxxxxxxxxx\r\n
*
* abcd is the chunk-size, and xxx is the chunk data
* If the length is less than 4 chars (in size) then the buffer
* is written with an offset.
* Final chunk is:
* 0\r\n\r\n
*/
class ChunkedOutputStream extends FilterOutputStream
{
private boolean closed = false;
/* max. amount of user data per chunk */
final static int CHUNK_SIZE = 4096;
/* allow 4 bytes for chunk-size plus 4 for CRLFs */
final static int OFFSET = 6; /* initial <=4 bytes for len + CRLF */
private int pos = OFFSET;
private int count = 0;
private byte[] buf = new byte [CHUNK_SIZE+OFFSET+2];
ExchangeImpl t;
ChunkedOutputStream (ExchangeImpl t, OutputStream src) {
super (src);
this.t = t;
}
public void write (int b) throws IOException {
if (closed) {
throw new StreamClosedException ();
}
buf [pos++] = (byte)b;
count ++;
if (count == CHUNK_SIZE) {
writeChunk();
}
assert count < CHUNK_SIZE;
}
public void write (byte[]b, int off, int len) throws IOException {
if (closed) {
throw new StreamClosedException ();
}
int remain = CHUNK_SIZE - count;
if (len > remain) {
System.arraycopy (b,off,buf,pos,remain);
count = CHUNK_SIZE;
writeChunk();
len -= remain;
off += remain;
while (len >= CHUNK_SIZE) {
System.arraycopy (b,off,buf,OFFSET,CHUNK_SIZE);
len -= CHUNK_SIZE;
off += CHUNK_SIZE;
count = CHUNK_SIZE;
writeChunk();
}
}
if (len > 0) {
System.arraycopy (b,off,buf,pos,len);
count += len;
pos += len;
}
if (count == CHUNK_SIZE) {
writeChunk();
}
}
/**
* write out a chunk , and reset the pointers
* chunk does not have to be CHUNK_SIZE bytes
* count must == number of user bytes (<= CHUNK_SIZE)
*/
private void writeChunk () throws IOException {
char[] c = Integer.toHexString (count).toCharArray();
int clen = c.length;
int startByte = 4 - clen;
int i;
for (i=0; i<clen; i++) {
buf[startByte+i] = (byte)c[i];
}
buf[startByte + (i++)] = '\r';
buf[startByte + (i++)] = '\n';
buf[startByte + (i++) + count] = '\r';
buf[startByte + (i++) + count] = '\n';
out.write (buf, startByte, i+count);
count = 0;
pos = OFFSET;
}
public void close () throws IOException {
if (closed) {
return;
}
flush();
try {
/* write an empty chunk */
writeChunk();
out.flush();
LeftOverInputStream is = t.getOriginalInputStream();
if (!is.isClosed()) {
is.close();
}
/* some clients close the connection before empty chunk is sent */
} catch (IOException e) {
} finally {
closed = true;
}
WriteFinishedEvent e = new WriteFinishedEvent (t);
t.getHttpContext().getServerImpl().addEvent (e);
}
public void flush () throws IOException {
if (closed) {
throw new StreamClosedException ();
}
if (count > 0) {
writeChunk();
}
out.flush();
}
}

View file

@ -0,0 +1,109 @@
/*
* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
class Code {
public static final int HTTP_CONTINUE = 100;
public static final int HTTP_OK = 200;
public static final int HTTP_CREATED = 201;
public static final int HTTP_ACCEPTED = 202;
public static final int HTTP_NOT_AUTHORITATIVE = 203;
public static final int HTTP_NO_CONTENT = 204;
public static final int HTTP_RESET = 205;
public static final int HTTP_PARTIAL = 206;
public static final int HTTP_MULT_CHOICE = 300;
public static final int HTTP_MOVED_PERM = 301;
public static final int HTTP_MOVED_TEMP = 302;
public static final int HTTP_SEE_OTHER = 303;
public static final int HTTP_NOT_MODIFIED = 304;
public static final int HTTP_USE_PROXY = 305;
public static final int HTTP_BAD_REQUEST = 400;
public static final int HTTP_UNAUTHORIZED = 401;
public static final int HTTP_PAYMENT_REQUIRED = 402;
public static final int HTTP_FORBIDDEN = 403;
public static final int HTTP_NOT_FOUND = 404;
public static final int HTTP_BAD_METHOD = 405;
public static final int HTTP_NOT_ACCEPTABLE = 406;
public static final int HTTP_PROXY_AUTH = 407;
public static final int HTTP_CLIENT_TIMEOUT = 408;
public static final int HTTP_CONFLICT = 409;
public static final int HTTP_GONE = 410;
public static final int HTTP_LENGTH_REQUIRED = 411;
public static final int HTTP_PRECON_FAILED = 412;
public static final int HTTP_ENTITY_TOO_LARGE = 413;
public static final int HTTP_REQ_TOO_LONG = 414;
public static final int HTTP_UNSUPPORTED_TYPE = 415;
public static final int HTTP_INTERNAL_ERROR = 500;
public static final int HTTP_NOT_IMPLEMENTED = 501;
public static final int HTTP_BAD_GATEWAY = 502;
public static final int HTTP_UNAVAILABLE = 503;
public static final int HTTP_GATEWAY_TIMEOUT = 504;
public static final int HTTP_VERSION = 505;
static String msg (int code) {
switch (code) {
case HTTP_OK: return " OK";
case HTTP_CONTINUE: return " Continue";
case HTTP_CREATED: return " Created";
case HTTP_ACCEPTED: return " Accepted";
case HTTP_NOT_AUTHORITATIVE: return " Non-Authoritative Information";
case HTTP_NO_CONTENT: return " No Content";
case HTTP_RESET: return " Reset Content";
case HTTP_PARTIAL: return " Partial Content";
case HTTP_MULT_CHOICE: return " Multiple Choices";
case HTTP_MOVED_PERM: return " Moved Permanently";
case HTTP_MOVED_TEMP: return " Temporary Redirect";
case HTTP_SEE_OTHER: return " See Other";
case HTTP_NOT_MODIFIED: return " Not Modified";
case HTTP_USE_PROXY: return " Use Proxy";
case HTTP_BAD_REQUEST: return " Bad Request";
case HTTP_UNAUTHORIZED: return " Unauthorized" ;
case HTTP_PAYMENT_REQUIRED: return " Payment Required";
case HTTP_FORBIDDEN: return " Forbidden";
case HTTP_NOT_FOUND: return " Not Found";
case HTTP_BAD_METHOD: return " Method Not Allowed";
case HTTP_NOT_ACCEPTABLE: return " Not Acceptable";
case HTTP_PROXY_AUTH: return " Proxy Authentication Required";
case HTTP_CLIENT_TIMEOUT: return " Request Time-Out";
case HTTP_CONFLICT: return " Conflict";
case HTTP_GONE: return " Gone";
case HTTP_LENGTH_REQUIRED: return " Length Required";
case HTTP_PRECON_FAILED: return " Precondition Failed";
case HTTP_ENTITY_TOO_LARGE: return " Request Entity Too Large";
case HTTP_REQ_TOO_LONG: return " Request-URI Too Large";
case HTTP_UNSUPPORTED_TYPE: return " Unsupported Media Type";
case HTTP_INTERNAL_ERROR: return " Internal Server Error";
case HTTP_NOT_IMPLEMENTED: return " Not Implemented";
case HTTP_BAD_GATEWAY: return " Bad Gateway";
case HTTP_UNAVAILABLE: return " Service Unavailable";
case HTTP_GATEWAY_TIMEOUT: return " Gateway Timeout";
case HTTP_VERSION: return " HTTP Version Not Supported";
default: return " ";
}
}
}

View file

@ -0,0 +1,97 @@
/*
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.util.*;
import com.sun.net.httpserver.*;
import com.sun.net.httpserver.spi.*;
class ContextList {
final static int MAX_CONTEXTS = 50;
LinkedList<HttpContextImpl> list = new LinkedList<HttpContextImpl>();
public synchronized void add (HttpContextImpl ctx) {
assert ctx.getPath() != null;
list.add (ctx);
}
public synchronized int size () {
return list.size();
}
/* initially contexts are located only by protocol:path.
* Context with longest prefix matches (currently case-sensitive)
*/
synchronized HttpContextImpl findContext (String protocol, String path) {
return findContext (protocol, path, false);
}
synchronized HttpContextImpl findContext (String protocol, String path, boolean exact) {
protocol = protocol.toLowerCase();
String longest = "";
HttpContextImpl lc = null;
for (HttpContextImpl ctx: list) {
if (!ctx.getProtocol().equals(protocol)) {
continue;
}
String cpath = ctx.getPath();
if (exact && !cpath.equals (path)) {
continue;
} else if (!exact && !path.startsWith(cpath)) {
continue;
}
if (cpath.length() > longest.length()) {
longest = cpath;
lc = ctx;
}
}
return lc;
}
public synchronized void remove (String protocol, String path)
throws IllegalArgumentException
{
HttpContextImpl ctx = findContext (protocol, path, true);
if (ctx == null) {
throw new IllegalArgumentException ("cannot remove element from list");
}
list.remove (ctx);
}
public synchronized void remove (HttpContextImpl context)
throws IllegalArgumentException
{
for (HttpContextImpl ctx: list) {
if (ctx.equals (context)) {
list.remove (ctx);
return;
}
}
throw new IllegalArgumentException ("no such context in list");
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.net.*;
import java.io.*;
import com.sun.net.httpserver.*;
import com.sun.net.httpserver.spi.*;
public class DefaultHttpServerProvider extends HttpServerProvider {
public HttpServer createHttpServer (InetSocketAddress addr, int backlog) throws IOException {
return new HttpServerImpl (addr, backlog);
}
public HttpsServer createHttpsServer (InetSocketAddress addr, int backlog) throws IOException {
return new HttpsServerImpl (addr, backlog);
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import com.sun.net.httpserver.*;
import com.sun.net.httpserver.spi.*;
class Event {
ExchangeImpl exchange;
protected Event (ExchangeImpl t) {
this.exchange = t;
}
}

View file

@ -0,0 +1,457 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.io.*;
import java.net.*;
import javax.net.ssl.*;
import java.util.*;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.text.*;
import com.sun.net.httpserver.*;
class ExchangeImpl {
Headers reqHdrs, rspHdrs;
Request req;
String method;
boolean writefinished;
URI uri;
HttpConnection connection;
long reqContentLen;
long rspContentLen;
/* raw streams which access the socket directly */
InputStream ris;
OutputStream ros;
Thread thread;
/* close the underlying connection when this exchange finished */
boolean close;
boolean closed;
boolean http10 = false;
/* for formatting the Date: header */
private static final String pattern = "EEE, dd MMM yyyy HH:mm:ss zzz";
private static final TimeZone gmtTZ = TimeZone.getTimeZone("GMT");
private static final ThreadLocal<DateFormat> dateFormat =
new ThreadLocal<DateFormat>() {
@Override protected DateFormat initialValue() {
DateFormat df = new SimpleDateFormat(pattern, Locale.US);
df.setTimeZone(gmtTZ);
return df;
}
};
private static final String HEAD = "HEAD";
/* streams which take care of the HTTP protocol framing
* and are passed up to higher layers
*/
InputStream uis;
OutputStream uos;
LeftOverInputStream uis_orig; // uis may have be a user supplied wrapper
PlaceholderOutputStream uos_orig;
boolean sentHeaders; /* true after response headers sent */
Map<String,Object> attributes;
int rcode = -1;
HttpPrincipal principal;
ServerImpl server;
ExchangeImpl (
String m, URI u, Request req, long len, HttpConnection connection
) throws IOException {
this.req = req;
this.reqHdrs = req.headers();
this.rspHdrs = new Headers();
this.method = m;
this.uri = u;
this.connection = connection;
this.reqContentLen = len;
/* ros only used for headers, body written directly to stream */
this.ros = req.outputStream();
this.ris = req.inputStream();
server = getServerImpl();
server.startExchange();
}
public Headers getRequestHeaders () {
return new UnmodifiableHeaders (reqHdrs);
}
public Headers getResponseHeaders () {
return rspHdrs;
}
public URI getRequestURI () {
return uri;
}
public String getRequestMethod (){
return method;
}
public HttpContextImpl getHttpContext (){
return connection.getHttpContext();
}
private boolean isHeadRequest() {
return HEAD.equals(getRequestMethod());
}
public void close () {
if (closed) {
return;
}
closed = true;
/* close the underlying connection if,
* a) the streams not set up yet, no response can be sent, or
* b) if the wrapper output stream is not set up, or
* c) if the close of the input/outpu stream fails
*/
try {
if (uis_orig == null || uos == null) {
connection.close();
return;
}
if (!uos_orig.isWrapped()) {
connection.close();
return;
}
if (!uis_orig.isClosed()) {
uis_orig.close();
}
uos.close();
} catch (IOException e) {
connection.close();
}
}
public InputStream getRequestBody () {
if (uis != null) {
return uis;
}
if (reqContentLen == -1L) {
uis_orig = new ChunkedInputStream (this, ris);
uis = uis_orig;
} else {
uis_orig = new FixedLengthInputStream (this, ris, reqContentLen);
uis = uis_orig;
}
return uis;
}
LeftOverInputStream getOriginalInputStream () {
return uis_orig;
}
public int getResponseCode () {
return rcode;
}
public OutputStream getResponseBody () {
/* TODO. Change spec to remove restriction below. Filters
* cannot work with this restriction
*
* if (!sentHeaders) {
* throw new IllegalStateException ("headers not sent");
* }
*/
if (uos == null) {
uos_orig = new PlaceholderOutputStream (null);
uos = uos_orig;
}
return uos;
}
/* returns the place holder stream, which is the stream
* returned from the 1st call to getResponseBody()
* The "real" ouputstream is then placed inside this
*/
PlaceholderOutputStream getPlaceholderResponseBody () {
getResponseBody();
return uos_orig;
}
public void sendResponseHeaders (int rCode, long contentLen)
throws IOException
{
if (sentHeaders) {
throw new IOException ("headers already sent");
}
this.rcode = rCode;
String statusLine = "HTTP/1.1 "+rCode+Code.msg(rCode)+"\r\n";
OutputStream tmpout = new BufferedOutputStream (ros);
PlaceholderOutputStream o = getPlaceholderResponseBody();
tmpout.write (bytes(statusLine, 0), 0, statusLine.length());
boolean noContentToSend = false; // assume there is content
rspHdrs.set ("Date", dateFormat.get().format (new Date()));
/* check for response type that is not allowed to send a body */
if ((rCode>=100 && rCode <200) /* informational */
||(rCode == 204) /* no content */
||(rCode == 304)) /* not modified */
{
if (contentLen != -1) {
Logger logger = server.getLogger();
String msg = "sendResponseHeaders: rCode = "+ rCode
+ ": forcing contentLen = -1";
logger.log (Level.WARNING, msg);
}
contentLen = -1;
}
if (isHeadRequest()) {
/* HEAD requests should not set a content length by passing it
* through this API, but should instead manually set the required
* headers.*/
if (contentLen >= 0) {
final Logger logger = server.getLogger();
String msg =
"sendResponseHeaders: being invoked with a content length for a HEAD request";
logger.log (Level.WARNING, msg);
}
noContentToSend = true;
contentLen = 0;
} else { /* not a HEAD request */
if (contentLen == 0) {
if (http10) {
o.setWrappedStream (new UndefLengthOutputStream (this, ros));
close = true;
} else {
rspHdrs.set ("Transfer-encoding", "chunked");
o.setWrappedStream (new ChunkedOutputStream (this, ros));
}
} else {
if (contentLen == -1) {
noContentToSend = true;
contentLen = 0;
}
rspHdrs.set("Content-length", Long.toString(contentLen));
o.setWrappedStream (new FixedLengthOutputStream (this, ros, contentLen));
}
}
write (rspHdrs, tmpout);
this.rspContentLen = contentLen;
tmpout.flush() ;
tmpout = null;
sentHeaders = true;
if (noContentToSend) {
WriteFinishedEvent e = new WriteFinishedEvent (this);
server.addEvent (e);
closed = true;
}
server.logReply (rCode, req.requestLine(), null);
}
void write (Headers map, OutputStream os) throws IOException {
Set<Map.Entry<String,List<String>>> entries = map.entrySet();
for (Map.Entry<String,List<String>> entry : entries) {
String key = entry.getKey();
byte[] buf;
List<String> values = entry.getValue();
for (String val : values) {
int i = key.length();
buf = bytes (key, 2);
buf[i++] = ':';
buf[i++] = ' ';
os.write (buf, 0, i);
buf = bytes (val, 2);
i = val.length();
buf[i++] = '\r';
buf[i++] = '\n';
os.write (buf, 0, i);
}
}
os.write ('\r');
os.write ('\n');
}
private byte[] rspbuf = new byte [128]; // used by bytes()
/**
* convert string to byte[], using rspbuf
* Make sure that at least "extra" bytes are free at end
* of rspbuf. Reallocate rspbuf if not big enough.
* caller must check return value to see if rspbuf moved
*/
private byte[] bytes (String s, int extra) {
int slen = s.length();
if (slen+extra > rspbuf.length) {
int diff = slen + extra - rspbuf.length;
rspbuf = new byte [2* (rspbuf.length + diff)];
}
char c[] = s.toCharArray();
for (int i=0; i<c.length; i++) {
rspbuf[i] = (byte)c[i];
}
return rspbuf;
}
public InetSocketAddress getRemoteAddress (){
Socket s = connection.getChannel().socket();
InetAddress ia = s.getInetAddress();
int port = s.getPort();
return new InetSocketAddress (ia, port);
}
public InetSocketAddress getLocalAddress (){
Socket s = connection.getChannel().socket();
InetAddress ia = s.getLocalAddress();
int port = s.getLocalPort();
return new InetSocketAddress (ia, port);
}
public String getProtocol (){
String reqline = req.requestLine();
int index = reqline.lastIndexOf (' ');
return reqline.substring (index+1);
}
public SSLSession getSSLSession () {
SSLEngine e = connection.getSSLEngine();
if (e == null) {
return null;
}
return e.getSession();
}
public Object getAttribute (String name) {
if (name == null) {
throw new NullPointerException ("null name parameter");
}
if (attributes == null) {
attributes = getHttpContext().getAttributes();
}
return attributes.get (name);
}
public void setAttribute (String name, Object value) {
if (name == null) {
throw new NullPointerException ("null name parameter");
}
if (attributes == null) {
attributes = getHttpContext().getAttributes();
}
attributes.put (name, value);
}
public void setStreams (InputStream i, OutputStream o) {
assert uis != null;
if (i != null) {
uis = i;
}
if (o != null) {
uos = o;
}
}
/**
* PP
*/
HttpConnection getConnection () {
return connection;
}
ServerImpl getServerImpl () {
return getHttpContext().getServerImpl();
}
public HttpPrincipal getPrincipal () {
return principal;
}
void setPrincipal (HttpPrincipal principal) {
this.principal = principal;
}
static ExchangeImpl get (HttpExchange t) {
if (t instanceof HttpExchangeImpl) {
return ((HttpExchangeImpl)t).getExchangeImpl();
} else {
assert t instanceof HttpsExchangeImpl;
return ((HttpsExchangeImpl)t).getExchangeImpl();
}
}
}
/**
* An OutputStream which wraps another stream
* which is supplied either at creation time, or sometime later.
* If a caller/user tries to write to this stream before
* the wrapped stream has been provided, then an IOException will
* be thrown.
*/
class PlaceholderOutputStream extends java.io.OutputStream {
OutputStream wrapped;
PlaceholderOutputStream (OutputStream os) {
wrapped = os;
}
void setWrappedStream (OutputStream os) {
wrapped = os;
}
boolean isWrapped () {
return wrapped != null;
}
private void checkWrap () throws IOException {
if (wrapped == null) {
throw new IOException ("response headers not sent yet");
}
}
public void write(int b) throws IOException {
checkWrap();
wrapped.write (b);
}
public void write(byte b[]) throws IOException {
checkWrap();
wrapped.write (b);
}
public void write(byte b[], int off, int len) throws IOException {
checkWrap();
wrapped.write (b, off, len);
}
public void flush() throws IOException {
checkWrap();
wrapped.flush();
}
public void close() throws IOException {
checkWrap();
wrapped.close();
}
}

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.io.*;
import java.net.*;
import com.sun.net.httpserver.*;
import com.sun.net.httpserver.spi.*;
/**
* a class which allows the caller to read up to a defined
* number of bytes off an underlying stream
* close() does not close the underlying stream
*/
class FixedLengthInputStream extends LeftOverInputStream {
private long remaining;
FixedLengthInputStream (ExchangeImpl t, InputStream src, long len) {
super (t, src);
this.remaining = len;
}
protected int readImpl (byte[]b, int off, int len) throws IOException {
eof = (remaining == 0L);
if (eof) {
return -1;
}
if (len > remaining) {
len = (int)remaining;
}
int n = in.read(b, off, len);
if (n > -1) {
remaining -= n;
if (remaining == 0) {
t.getServerImpl().requestCompleted (t.getConnection());
}
}
return n;
}
public int available () throws IOException {
if (eof) {
return 0;
}
int n = in.available();
return n < remaining? n: (int)remaining;
}
public boolean markSupported () {return false;}
public void mark (int l) {
}
public void reset () throws IOException {
throw new IOException ("mark/reset not supported");
}
}

View file

@ -0,0 +1,104 @@
/*
* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.io.*;
import java.net.*;
import com.sun.net.httpserver.*;
import com.sun.net.httpserver.spi.*;
/**
* a class which allows the caller to write up to a defined
* number of bytes to an underlying stream. The caller *must*
* write the pre-defined number or else an exception will be thrown
* and the whole request aborted.
* normal close() does not close the underlying stream
*/
class FixedLengthOutputStream extends FilterOutputStream
{
private long remaining;
private boolean eof = false;
private boolean closed = false;
ExchangeImpl t;
FixedLengthOutputStream (ExchangeImpl t, OutputStream src, long len) {
super (src);
this.t = t;
this.remaining = len;
}
public void write (int b) throws IOException {
if (closed) {
throw new IOException ("stream closed");
}
eof = (remaining == 0);
if (eof) {
throw new StreamClosedException();
}
out.write(b);
remaining --;
}
public void write (byte[]b, int off, int len) throws IOException {
if (closed) {
throw new IOException ("stream closed");
}
eof = (remaining == 0);
if (eof) {
throw new StreamClosedException();
}
if (len > remaining) {
// stream is still open, caller can retry
throw new IOException ("too many bytes to write to stream");
}
out.write(b, off, len);
remaining -= len;
}
public void close () throws IOException {
if (closed) {
return;
}
closed = true;
if (remaining > 0) {
t.close();
throw new IOException ("insufficient bytes written to stream");
}
flush();
eof = true;
LeftOverInputStream is = t.getOriginalInputStream();
if (!is.isClosed()) {
try {
is.close();
} catch (IOException e) {}
}
WriteFinishedEvent e = new WriteFinishedEvent (t);
t.getHttpContext().getServerImpl().addEvent (e);
}
// flush is a pass-through
}

View file

@ -0,0 +1,197 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.io.*;
import javax.net.ssl.*;
import java.nio.channels.*;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import com.sun.net.httpserver.*;
import com.sun.net.httpserver.spi.*;
/**
* encapsulates all the connection specific state for a HTTP/S connection
* one of these is hung from the selector attachment and is used to locate
* everything from that.
*/
class HttpConnection {
HttpContextImpl context;
SSLEngine engine;
SSLContext sslContext;
SSLStreams sslStreams;
/* high level streams returned to application */
InputStream i;
/* low level stream that sits directly over channel */
InputStream raw;
OutputStream rawout;
SocketChannel chan;
SelectionKey selectionKey;
String protocol;
long time;
volatile long creationTime; // time this connection was created
volatile long rspStartedTime; // time we started writing the response
int remaining;
boolean closed = false;
Logger logger;
public enum State {IDLE, REQUEST, RESPONSE};
volatile State state;
public String toString() {
String s = null;
if (chan != null) {
s = chan.toString();
}
return s;
}
HttpConnection () {
}
void setChannel (SocketChannel c) {
chan = c;
}
void setContext (HttpContextImpl ctx) {
context = ctx;
}
State getState() {
return state;
}
void setState (State s) {
state = s;
}
void setParameters (
InputStream in, OutputStream rawout, SocketChannel chan,
SSLEngine engine, SSLStreams sslStreams, SSLContext sslContext, String protocol,
HttpContextImpl context, InputStream raw
)
{
this.context = context;
this.i = in;
this.rawout = rawout;
this.raw = raw;
this.protocol = protocol;
this.engine = engine;
this.chan = chan;
this.sslContext = sslContext;
this.sslStreams = sslStreams;
this.logger = context.getLogger();
}
SocketChannel getChannel () {
return chan;
}
synchronized void close () {
if (closed) {
return;
}
closed = true;
if (logger != null && chan != null) {
logger.log (Level.TRACE, "Closing connection: " + chan.toString());
}
if (!chan.isOpen()) {
ServerImpl.dprint ("Channel already closed");
return;
}
try {
/* need to ensure temporary selectors are closed */
if (raw != null) {
raw.close();
}
} catch (IOException e) {
ServerImpl.dprint (e);
}
try {
if (rawout != null) {
rawout.close();
}
} catch (IOException e) {
ServerImpl.dprint (e);
}
try {
if (sslStreams != null) {
sslStreams.close();
}
} catch (IOException e) {
ServerImpl.dprint (e);
}
try {
chan.close();
} catch (IOException e) {
ServerImpl.dprint (e);
}
}
/* remaining is the number of bytes left on the lowest level inputstream
* after the exchange is finished
*/
void setRemaining (int r) {
remaining = r;
}
int getRemaining () {
return remaining;
}
SelectionKey getSelectionKey () {
return selectionKey;
}
InputStream getInputStream () {
return i;
}
OutputStream getRawOutputStream () {
return rawout;
}
String getProtocol () {
return protocol;
}
SSLEngine getSSLEngine () {
return engine;
}
SSLContext getSSLContext () {
return sslContext;
}
HttpContextImpl getHttpContext () {
return context;
}
}

View file

@ -0,0 +1,152 @@
/*
* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.io.*;
import java.util.*;
import java.lang.System.Logger;
import com.sun.net.httpserver.*;
import com.sun.net.httpserver.spi.*;
/**
* HttpContext represents a mapping between a protocol (http or https) together with a root URI path
* to a {@link HttpHandler} which is invoked to handle requests destined
* for the protocol/path on the associated HttpServer.
* <p>
* HttpContext instances are created by {@link HttpServer#createContext(String,String,HttpHandler,Object)}
* <p>
*/
class HttpContextImpl extends HttpContext {
private String path;
private String protocol;
private HttpHandler handler;
private Map<String,Object> attributes = new HashMap<String,Object>();
private ServerImpl server;
/* system filters, not visible to applications */
private LinkedList<Filter> sfilters = new LinkedList<Filter>();
/* user filters, set by applications */
private LinkedList<Filter> ufilters = new LinkedList<Filter>();
private Authenticator authenticator;
private AuthFilter authfilter;
/**
* constructor is package private.
*/
HttpContextImpl (String protocol, String path, HttpHandler cb, ServerImpl server) {
if (path == null || protocol == null || path.length() < 1 || path.charAt(0) != '/') {
throw new IllegalArgumentException ("Illegal value for path or protocol");
}
this.protocol = protocol.toLowerCase();
this.path = path;
if (!this.protocol.equals ("http") && !this.protocol.equals ("https")) {
throw new IllegalArgumentException ("Illegal value for protocol");
}
this.handler = cb;
this.server = server;
authfilter = new AuthFilter(null);
sfilters.add (authfilter);
}
/**
* returns the handler for this context
* @return the HttpHandler for this context
*/
public HttpHandler getHandler () {
return handler;
}
public void setHandler (HttpHandler h) {
if (h == null) {
throw new NullPointerException ("Null handler parameter");
}
if (handler != null) {
throw new IllegalArgumentException ("handler already set");
}
handler = h;
}
/**
* returns the path this context was created with
* @return this context's path
*/
public String getPath() {
return path;
}
/**
* returns the server this context was created with
* @return this context's server
*/
public HttpServer getServer () {
return server.getWrapper();
}
ServerImpl getServerImpl () {
return server;
}
/**
* returns the protocol this context was created with
* @return this context's path
*/
public String getProtocol() {
return protocol;
}
/**
* returns a mutable Map, which can be used to pass
* configuration and other data to Filter modules
* and to the context's exchange handler.
* <p>
* Every attribute stored in this Map will be visible to
* every HttpExchange processed by this context
*/
public Map<String,Object> getAttributes() {
return attributes;
}
public List<Filter> getFilters () {
return ufilters;
}
List<Filter> getSystemFilters () {
return sfilters;
}
public Authenticator setAuthenticator (Authenticator auth) {
Authenticator old = authenticator;
authenticator = auth;
authfilter.setAuthenticator (auth);
return old;
}
public Authenticator getAuthenticator () {
return authenticator;
}
Logger getLogger () {
return server.getLogger();
}
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
/**
* A Http error
*/
class HttpError extends RuntimeException {
private static final long serialVersionUID = 8769596371344178179L;
public HttpError (String msg) {
super (msg);
}
}

View file

@ -0,0 +1,119 @@
/*
* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import javax.net.ssl.*;
import java.util.*;
import com.sun.net.httpserver.*;
import com.sun.net.httpserver.spi.*;
class HttpExchangeImpl extends HttpExchange {
ExchangeImpl impl;
HttpExchangeImpl (ExchangeImpl impl) {
this.impl = impl;
}
public Headers getRequestHeaders () {
return impl.getRequestHeaders();
}
public Headers getResponseHeaders () {
return impl.getResponseHeaders();
}
public URI getRequestURI () {
return impl.getRequestURI();
}
public String getRequestMethod (){
return impl.getRequestMethod();
}
public HttpContextImpl getHttpContext (){
return impl.getHttpContext();
}
public void close () {
impl.close();
}
public InputStream getRequestBody () {
return impl.getRequestBody();
}
public int getResponseCode () {
return impl.getResponseCode();
}
public OutputStream getResponseBody () {
return impl.getResponseBody();
}
public void sendResponseHeaders (int rCode, long contentLen)
throws IOException
{
impl.sendResponseHeaders (rCode, contentLen);
}
public InetSocketAddress getRemoteAddress (){
return impl.getRemoteAddress();
}
public InetSocketAddress getLocalAddress (){
return impl.getLocalAddress();
}
public String getProtocol (){
return impl.getProtocol();
}
public Object getAttribute (String name) {
return impl.getAttribute (name);
}
public void setAttribute (String name, Object value) {
impl.setAttribute (name, value);
}
public void setStreams (InputStream i, OutputStream o) {
impl.setStreams (i, o);
}
public HttpPrincipal getPrincipal () {
return impl.getPrincipal();
}
ExchangeImpl getExchangeImpl () {
return impl;
}
}

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.net.*;
import java.io.*;
import java.nio.*;
import java.security.*;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import javax.net.ssl.*;
import com.sun.net.httpserver.*;
import com.sun.net.httpserver.spi.*;
public class HttpServerImpl extends HttpServer {
ServerImpl server;
HttpServerImpl () throws IOException {
this (new InetSocketAddress(80), 0);
}
HttpServerImpl (
InetSocketAddress addr, int backlog
) throws IOException {
server = new ServerImpl (this, "http", addr, backlog);
}
public void bind (InetSocketAddress addr, int backlog) throws IOException {
server.bind (addr, backlog);
}
public void start () {
server.start();
}
public void setExecutor (Executor executor) {
server.setExecutor(executor);
}
public Executor getExecutor () {
return server.getExecutor();
}
public void stop (int delay) {
server.stop (delay);
}
public HttpContextImpl createContext (String path, HttpHandler handler) {
return server.createContext (path, handler);
}
public HttpContextImpl createContext (String path) {
return server.createContext (path);
}
public void removeContext (String path) throws IllegalArgumentException {
server.removeContext (path);
}
public void removeContext (HttpContext context) throws IllegalArgumentException {
server.removeContext (context);
}
public InetSocketAddress getAddress() {
return server.getAddress();
}
}

View file

@ -0,0 +1,123 @@
/*
* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import javax.net.ssl.*;
import java.util.*;
import com.sun.net.httpserver.*;
import com.sun.net.httpserver.spi.*;
class HttpsExchangeImpl extends HttpsExchange {
ExchangeImpl impl;
HttpsExchangeImpl (ExchangeImpl impl) throws IOException {
this.impl = impl;
}
public Headers getRequestHeaders () {
return impl.getRequestHeaders();
}
public Headers getResponseHeaders () {
return impl.getResponseHeaders();
}
public URI getRequestURI () {
return impl.getRequestURI();
}
public String getRequestMethod (){
return impl.getRequestMethod();
}
public HttpContextImpl getHttpContext (){
return impl.getHttpContext();
}
public void close () {
impl.close();
}
public InputStream getRequestBody () {
return impl.getRequestBody();
}
public int getResponseCode () {
return impl.getResponseCode();
}
public OutputStream getResponseBody () {
return impl.getResponseBody();
}
public void sendResponseHeaders (int rCode, long contentLen)
throws IOException
{
impl.sendResponseHeaders (rCode, contentLen);
}
public InetSocketAddress getRemoteAddress (){
return impl.getRemoteAddress();
}
public InetSocketAddress getLocalAddress (){
return impl.getLocalAddress();
}
public String getProtocol (){
return impl.getProtocol();
}
public SSLSession getSSLSession () {
return impl.getSSLSession ();
}
public Object getAttribute (String name) {
return impl.getAttribute (name);
}
public void setAttribute (String name, Object value) {
impl.setAttribute (name, value);
}
public void setStreams (InputStream i, OutputStream o) {
impl.setStreams (i, o);
}
public HttpPrincipal getPrincipal () {
return impl.getPrincipal();
}
ExchangeImpl getExchangeImpl () {
return impl;
}
}

View file

@ -0,0 +1,100 @@
/*
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.net.*;
import java.io.*;
import java.nio.*;
import java.security.*;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import javax.net.ssl.*;
import com.sun.net.httpserver.*;
import com.sun.net.httpserver.spi.*;
public class HttpsServerImpl extends HttpsServer {
ServerImpl server;
HttpsServerImpl () throws IOException {
this (new InetSocketAddress(443), 0);
}
HttpsServerImpl (
InetSocketAddress addr, int backlog
) throws IOException {
server = new ServerImpl (this, "https", addr, backlog);
}
public void setHttpsConfigurator (HttpsConfigurator config) {
server.setHttpsConfigurator (config);
}
public HttpsConfigurator getHttpsConfigurator () {
return server.getHttpsConfigurator();
}
public void bind (InetSocketAddress addr, int backlog) throws IOException {
server.bind (addr, backlog);
}
public void start () {
server.start();
}
public void setExecutor (Executor executor) {
server.setExecutor(executor);
}
public Executor getExecutor () {
return server.getExecutor();
}
public void stop (int delay) {
server.stop (delay);
}
public HttpContextImpl createContext (String path, HttpHandler handler) {
return server.createContext (path, handler);
}
public HttpContextImpl createContext (String path) {
return server.createContext (path);
}
public void removeContext (String path) throws IllegalArgumentException {
server.removeContext (path);
}
public void removeContext (HttpContext context) throws IllegalArgumentException {
server.removeContext (context);
}
public InetSocketAddress getAddress() {
return server.getAddress();
}
}

View file

@ -0,0 +1,125 @@
/*
* Copyright (c) 2005, 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.io.*;
import com.sun.net.httpserver.*;
import com.sun.net.httpserver.spi.*;
/**
* a (filter) input stream which can tell us if bytes are "left over"
* on the underlying stream which can be read (without blocking)
* on another instance of this class.
*
* The class can also report if all bytes "expected" to be read
* were read, by the time close() was called. In that case,
* bytes may be drained to consume them (by calling drain() ).
*
* isEOF() returns true, when all expected bytes have been read
*/
abstract class LeftOverInputStream extends FilterInputStream {
final ExchangeImpl t;
final ServerImpl server;
protected boolean closed = false;
protected boolean eof = false;
byte[] one = new byte [1];
public LeftOverInputStream (ExchangeImpl t, InputStream src) {
super (src);
this.t = t;
this.server = t.getServerImpl();
}
/**
* if bytes are left over buffered on *the UNDERLYING* stream
*/
public boolean isDataBuffered () throws IOException {
assert eof;
return super.available() > 0;
}
public void close () throws IOException {
if (closed) {
return;
}
closed = true;
if (!eof) {
eof = drain (ServerConfig.getDrainAmount());
}
}
public boolean isClosed () {
return closed;
}
public boolean isEOF () {
return eof;
}
protected abstract int readImpl (byte[]b, int off, int len) throws IOException;
public synchronized int read () throws IOException {
if (closed) {
throw new IOException ("Stream is closed");
}
int c = readImpl (one, 0, 1);
if (c == -1 || c == 0) {
return c;
} else {
return one[0] & 0xFF;
}
}
public synchronized int read (byte[]b, int off, int len) throws IOException {
if (closed) {
throw new IOException ("Stream is closed");
}
return readImpl (b, off, len);
}
/**
* read and discard up to l bytes or "eof" occurs,
* (whichever is first). Then return true if the stream
* is at eof (ie. all bytes were read) or false if not
* (still bytes to be read)
*/
public boolean drain (long l) throws IOException {
int bufSize = 2048;
byte[] db = new byte [bufSize];
while (l > 0) {
if (server.isFinishing()) {
break;
}
long len = readImpl (db, 0, bufSize);
if (len == -1) {
eof = true;
return true;
} else {
l = l - len;
}
}
return false;
}
}

View file

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

View file

@ -0,0 +1,660 @@
/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.net.*;
import java.nio.*;
import java.io.*;
import java.nio.channels.*;
import java.util.concurrent.locks.*;
import javax.net.ssl.*;
import javax.net.ssl.SSLEngineResult.*;
import com.sun.net.httpserver.*;
/**
* given a non-blocking SocketChannel, it produces
* (blocking) streams which encrypt/decrypt the SSL content
* and handle the SSL handshaking automatically.
*/
class SSLStreams {
SSLContext sslctx;
SocketChannel chan;
TimeSource time;
ServerImpl server;
SSLEngine engine;
EngineWrapper wrapper;
OutputStream os;
InputStream is;
/* held by thread doing the hand-shake on this connection */
Lock handshaking = new ReentrantLock();
SSLStreams (ServerImpl server, SSLContext sslctx, SocketChannel chan) throws IOException {
this.server = server;
this.time= (TimeSource)server;
this.sslctx= sslctx;
this.chan= chan;
InetSocketAddress addr =
(InetSocketAddress)chan.socket().getRemoteSocketAddress();
engine = sslctx.createSSLEngine (addr.getHostName(), addr.getPort());
engine.setUseClientMode (false);
HttpsConfigurator cfg = server.getHttpsConfigurator();
configureEngine (cfg, addr);
wrapper = new EngineWrapper (chan, engine);
}
private void configureEngine(HttpsConfigurator cfg, InetSocketAddress addr){
if (cfg != null) {
Parameters params = new Parameters (cfg, addr);
//BEGIN_TIGER_EXCLUDE
cfg.configure (params);
SSLParameters sslParams = params.getSSLParameters();
if (sslParams != null) {
engine.setSSLParameters (sslParams);
} else
//END_TIGER_EXCLUDE
{
/* tiger compatibility */
if (params.getCipherSuites() != null) {
try {
engine.setEnabledCipherSuites (
params.getCipherSuites()
);
} catch (IllegalArgumentException e) { /* LOG */}
}
engine.setNeedClientAuth (params.getNeedClientAuth());
engine.setWantClientAuth (params.getWantClientAuth());
if (params.getProtocols() != null) {
try {
engine.setEnabledProtocols (
params.getProtocols()
);
} catch (IllegalArgumentException e) { /* LOG */}
}
}
}
}
class Parameters extends HttpsParameters {
InetSocketAddress addr;
HttpsConfigurator cfg;
Parameters (HttpsConfigurator cfg, InetSocketAddress addr) {
this.addr = addr;
this.cfg = cfg;
}
public InetSocketAddress getClientAddress () {
return addr;
}
public HttpsConfigurator getHttpsConfigurator() {
return cfg;
}
//BEGIN_TIGER_EXCLUDE
SSLParameters params;
public void setSSLParameters (SSLParameters p) {
params = p;
}
SSLParameters getSSLParameters () {
return params;
}
//END_TIGER_EXCLUDE
}
/**
* cleanup resources allocated inside this object
*/
void close () throws IOException {
wrapper.close();
}
/**
* return the SSL InputStream
*/
InputStream getInputStream () throws IOException {
if (is == null) {
is = new InputStream();
}
return is;
}
/**
* return the SSL OutputStream
*/
OutputStream getOutputStream () throws IOException {
if (os == null) {
os = new OutputStream();
}
return os;
}
SSLEngine getSSLEngine () {
return engine;
}
/**
* request the engine to repeat the handshake on this session
* the handshake must be driven by reads/writes on the streams
* Normally, not necessary to call this.
*/
void beginHandshake() throws SSLException {
engine.beginHandshake();
}
class WrapperResult {
SSLEngineResult result;
/* if passed in buffer was not big enough then the
* a reallocated buffer is returned here
*/
ByteBuffer buf;
}
int app_buf_size;
int packet_buf_size;
enum BufType {
PACKET, APPLICATION
};
private ByteBuffer allocate (BufType type) {
return allocate (type, -1);
}
private ByteBuffer allocate (BufType type, int len) {
assert engine != null;
synchronized (this) {
int size;
if (type == BufType.PACKET) {
if (packet_buf_size == 0) {
SSLSession sess = engine.getSession();
packet_buf_size = sess.getPacketBufferSize();
}
if (len > packet_buf_size) {
packet_buf_size = len;
}
size = packet_buf_size;
} else {
if (app_buf_size == 0) {
SSLSession sess = engine.getSession();
app_buf_size = sess.getApplicationBufferSize();
}
if (len > app_buf_size) {
app_buf_size = len;
}
size = app_buf_size;
}
return ByteBuffer.allocate (size);
}
}
/* reallocates the buffer by :-
* 1. creating a new buffer double the size of the old one
* 2. putting the contents of the old buffer into the new one
* 3. set xx_buf_size to the new size if it was smaller than new size
*
* flip is set to true if the old buffer needs to be flipped
* before it is copied.
*/
private ByteBuffer realloc (ByteBuffer b, boolean flip, BufType type) {
synchronized (this) {
int nsize = 2 * b.capacity();
ByteBuffer n = allocate (type, nsize);
if (flip) {
b.flip();
}
n.put(b);
b = n;
}
return b;
}
/**
* This is a thin wrapper over SSLEngine and the SocketChannel,
* which guarantees the ordering of wraps/unwraps with respect to the underlying
* channel read/writes. It handles the UNDER/OVERFLOW status codes
* It does not handle the handshaking status codes, or the CLOSED status code
* though once the engine is closed, any attempt to read/write to it
* will get an exception. The overall result is returned.
* It functions synchronously/blocking
*/
class EngineWrapper {
SocketChannel chan;
SSLEngine engine;
Object wrapLock, unwrapLock;
ByteBuffer unwrap_src, wrap_dst;
boolean closed = false;
int u_remaining; // the number of bytes left in unwrap_src after an unwrap()
EngineWrapper (SocketChannel chan, SSLEngine engine) throws IOException {
this.chan = chan;
this.engine = engine;
wrapLock = new Object();
unwrapLock = new Object();
unwrap_src = allocate(BufType.PACKET);
wrap_dst = allocate(BufType.PACKET);
}
void close () throws IOException {
}
/* try to wrap and send the data in src. Handles OVERFLOW.
* Might block if there is an outbound blockage or if another
* thread is calling wrap(). Also, might not send any data
* if an unwrap is needed.
*/
WrapperResult wrapAndSend(ByteBuffer src) throws IOException {
return wrapAndSendX(src, false);
}
WrapperResult wrapAndSendX(ByteBuffer src, boolean ignoreClose) throws IOException {
if (closed && !ignoreClose) {
throw new IOException ("Engine is closed");
}
Status status;
WrapperResult r = new WrapperResult();
synchronized (wrapLock) {
wrap_dst.clear();
do {
r.result = engine.wrap (src, wrap_dst);
status = r.result.getStatus();
if (status == Status.BUFFER_OVERFLOW) {
wrap_dst = realloc (wrap_dst, true, BufType.PACKET);
}
} while (status == Status.BUFFER_OVERFLOW);
if (status == Status.CLOSED && !ignoreClose) {
closed = true;
return r;
}
if (r.result.bytesProduced() > 0) {
wrap_dst.flip();
int l = wrap_dst.remaining();
assert l == r.result.bytesProduced();
while (l>0) {
l -= chan.write (wrap_dst);
}
}
}
return r;
}
/* block until a complete message is available and return it
* in dst, together with the Result. dst may have been re-allocated
* so caller should check the returned value in Result
* If handshaking is in progress then, possibly no data is returned
*/
WrapperResult recvAndUnwrap(ByteBuffer dst) throws IOException {
Status status = Status.OK;
WrapperResult r = new WrapperResult();
r.buf = dst;
if (closed) {
throw new IOException ("Engine is closed");
}
boolean needData;
if (u_remaining > 0) {
unwrap_src.compact();
unwrap_src.flip();
needData = false;
} else {
unwrap_src.clear();
needData = true;
}
synchronized (unwrapLock) {
int x;
do {
if (needData) {
do {
x = chan.read (unwrap_src);
} while (x == 0);
if (x == -1) {
throw new IOException ("connection closed for reading");
}
unwrap_src.flip();
}
r.result = engine.unwrap (unwrap_src, r.buf);
status = r.result.getStatus();
if (status == Status.BUFFER_UNDERFLOW) {
if (unwrap_src.limit() == unwrap_src.capacity()) {
/* buffer not big enough */
unwrap_src = realloc (
unwrap_src, false, BufType.PACKET
);
} else {
/* Buffer not full, just need to read more
* data off the channel. Reset pointers
* for reading off SocketChannel
*/
unwrap_src.position (unwrap_src.limit());
unwrap_src.limit (unwrap_src.capacity());
}
needData = true;
} else if (status == Status.BUFFER_OVERFLOW) {
r.buf = realloc (r.buf, true, BufType.APPLICATION);
needData = false;
} else if (status == Status.CLOSED) {
closed = true;
r.buf.flip();
return r;
}
} while (status != Status.OK);
}
u_remaining = unwrap_src.remaining();
return r;
}
}
/**
* send the data in the given ByteBuffer. If a handshake is needed
* then this is handled within this method. When this call returns,
* all of the given user data has been sent and any handshake has been
* completed. Caller should check if engine has been closed.
*/
public WrapperResult sendData (ByteBuffer src) throws IOException {
WrapperResult r=null;
while (src.remaining() > 0) {
r = wrapper.wrapAndSend(src);
Status status = r.result.getStatus();
if (status == Status.CLOSED) {
doClosure ();
return r;
}
HandshakeStatus hs_status = r.result.getHandshakeStatus();
if (hs_status != HandshakeStatus.FINISHED &&
hs_status != HandshakeStatus.NOT_HANDSHAKING)
{
doHandshake(hs_status);
}
}
return r;
}
/**
* read data thru the engine into the given ByteBuffer. If the
* given buffer was not large enough, a new one is allocated
* and returned. This call handles handshaking automatically.
* Caller should check if engine has been closed.
*/
public WrapperResult recvData (ByteBuffer dst) throws IOException {
/* we wait until some user data arrives */
WrapperResult r = null;
assert dst.position() == 0;
while (dst.position() == 0) {
r = wrapper.recvAndUnwrap (dst);
dst = (r.buf != dst) ? r.buf: dst;
Status status = r.result.getStatus();
if (status == Status.CLOSED) {
doClosure ();
return r;
}
HandshakeStatus hs_status = r.result.getHandshakeStatus();
if (hs_status != HandshakeStatus.FINISHED &&
hs_status != HandshakeStatus.NOT_HANDSHAKING)
{
doHandshake (hs_status);
}
}
dst.flip();
return r;
}
/* we've received a close notify. Need to call wrap to send
* the response
*/
void doClosure () throws IOException {
try {
handshaking.lock();
ByteBuffer tmp = allocate(BufType.APPLICATION);
WrapperResult r;
do {
tmp.clear();
tmp.flip ();
r = wrapper.wrapAndSendX (tmp, true);
} while (r.result.getStatus() != Status.CLOSED);
} finally {
handshaking.unlock();
}
}
/* do the (complete) handshake after acquiring the handshake lock.
* If two threads call this at the same time, then we depend
* on the wrapper methods being idempotent. eg. if wrapAndSend()
* is called with no data to send then there must be no problem
*/
@SuppressWarnings("fallthrough")
void doHandshake (HandshakeStatus hs_status) throws IOException {
try {
handshaking.lock();
ByteBuffer tmp = allocate(BufType.APPLICATION);
while (hs_status != HandshakeStatus.FINISHED &&
hs_status != HandshakeStatus.NOT_HANDSHAKING)
{
WrapperResult r = null;
switch (hs_status) {
case NEED_TASK:
Runnable task;
while ((task = engine.getDelegatedTask()) != null) {
/* run in current thread, because we are already
* running an external Executor
*/
task.run();
}
/* fall thru - call wrap again */
case NEED_WRAP:
tmp.clear();
tmp.flip();
r = wrapper.wrapAndSend(tmp);
break;
case NEED_UNWRAP:
tmp.clear();
r = wrapper.recvAndUnwrap (tmp);
if (r.buf != tmp) {
tmp = r.buf;
}
assert tmp.position() == 0;
break;
}
hs_status = r.result.getHandshakeStatus();
}
} finally {
handshaking.unlock();
}
}
/**
* represents an SSL input stream. Multiple https requests can
* be sent over one stream. closing this stream causes an SSL close
* input.
*/
class InputStream extends java.io.InputStream {
ByteBuffer bbuf;
boolean closed = false;
/* this stream eof */
boolean eof = false;
boolean needData = true;
InputStream () {
bbuf = allocate (BufType.APPLICATION);
}
public int read (byte[] buf, int off, int len) throws IOException {
if (closed) {
throw new IOException ("SSL stream is closed");
}
if (eof) {
return 0;
}
int available=0;
if (!needData) {
available = bbuf.remaining();
needData = (available==0);
}
if (needData) {
bbuf.clear();
WrapperResult r = recvData (bbuf);
bbuf = r.buf== bbuf? bbuf: r.buf;
if ((available=bbuf.remaining()) == 0) {
eof = true;
return 0;
} else {
needData = false;
}
}
/* copy as much as possible from buf into users buf */
if (len > available) {
len = available;
}
bbuf.get (buf, off, len);
return len;
}
public int available () throws IOException {
return bbuf.remaining();
}
public boolean markSupported () {
return false; /* not possible with SSLEngine */
}
public void reset () throws IOException {
throw new IOException ("mark/reset not supported");
}
public long skip (long s) throws IOException {
int n = (int)s;
if (closed) {
throw new IOException ("SSL stream is closed");
}
if (eof) {
return 0;
}
int ret = n;
while (n > 0) {
if (bbuf.remaining() >= n) {
bbuf.position (bbuf.position()+n);
return ret;
} else {
n -= bbuf.remaining();
bbuf.clear();
WrapperResult r = recvData (bbuf);
bbuf = r.buf==bbuf? bbuf: r.buf;
}
}
return ret; /* not reached */
}
/**
* close the SSL connection. All data must have been consumed
* before this is called. Otherwise an exception will be thrown.
* [Note. May need to revisit this. not quite the normal close() symantics
*/
public void close () throws IOException {
eof = true;
engine.closeInbound ();
}
public int read (byte[] buf) throws IOException {
return read (buf, 0, buf.length);
}
byte single[] = new byte [1];
public int read () throws IOException {
int n = read (single, 0, 1);
if (n == 0) {
return -1;
} else {
return single[0] & 0xFF;
}
}
}
/**
* represents an SSL output stream. plain text data written to this stream
* is encrypted by the stream. Multiple HTTPS responses can be sent on
* one stream. closing this stream initiates an SSL closure
*/
class OutputStream extends java.io.OutputStream {
ByteBuffer buf;
boolean closed = false;
byte single[] = new byte[1];
OutputStream() {
buf = allocate(BufType.APPLICATION);
}
public void write(int b) throws IOException {
single[0] = (byte)b;
write (single, 0, 1);
}
public void write(byte b[]) throws IOException {
write (b, 0, b.length);
}
public void write(byte b[], int off, int len) throws IOException {
if (closed) {
throw new IOException ("output stream is closed");
}
while (len > 0) {
int l = len > buf.capacity() ? buf.capacity() : len;
buf.clear();
buf.put (b, off, l);
len -= l;
off += l;
buf.flip();
WrapperResult r = sendData (buf);
if (r.result.getStatus() == Status.CLOSED) {
closed = true;
if (len > 0) {
throw new IOException ("output stream is closed");
}
}
}
}
public void flush() throws IOException {
/* no-op */
}
public void close() throws IOException {
WrapperResult r=null;
engine.closeOutbound();
closed = true;
HandshakeStatus stat = HandshakeStatus.NEED_WRAP;
buf.clear();
while (stat == HandshakeStatus.NEED_WRAP) {
r = wrapper.wrapAndSend (buf);
stat = r.result.getHandshakeStatus();
}
assert r.result.getStatus() == Status.CLOSED;
}
}
}

View file

@ -0,0 +1,187 @@
/*
* Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.security.PrivilegedAction;
/**
* Parameters that users will not likely need to set
* but are useful for debugging
*/
class ServerConfig {
private static final int DEFAULT_CLOCK_TICK = 10000 ; // 10 sec.
/* These values must be a reasonable multiple of clockTick */
private static final long DEFAULT_IDLE_INTERVAL = 30 ; // 5 min
private static final int DEFAULT_MAX_IDLE_CONNECTIONS = 200 ;
private static final long DEFAULT_MAX_REQ_TIME = -1; // default: forever
private static final long DEFAULT_MAX_RSP_TIME = -1; // default: forever
private static final long DEFAULT_TIMER_MILLIS = 1000;
private static final int DEFAULT_MAX_REQ_HEADERS = 200;
private static final long DEFAULT_DRAIN_AMOUNT = 64 * 1024;
private static int clockTick;
private static long idleInterval;
// The maximum number of bytes to drain from an inputstream
private static long drainAmount;
private static int maxIdleConnections;
// The maximum number of request headers allowable
private static int maxReqHeaders;
// max time a request or response is allowed to take
private static long maxReqTime;
private static long maxRspTime;
private static long timerMillis;
private static boolean debug;
// the value of the TCP_NODELAY socket-level option
private static boolean noDelay;
static {
java.security.AccessController.doPrivileged(
new PrivilegedAction<Void>() {
@Override
public Void run () {
idleInterval = Long.getLong("sun.net.httpserver.idleInterval",
DEFAULT_IDLE_INTERVAL) * 1000;
clockTick = Integer.getInteger("sun.net.httpserver.clockTick",
DEFAULT_CLOCK_TICK);
maxIdleConnections = Integer.getInteger(
"sun.net.httpserver.maxIdleConnections",
DEFAULT_MAX_IDLE_CONNECTIONS);
drainAmount = Long.getLong("sun.net.httpserver.drainAmount",
DEFAULT_DRAIN_AMOUNT);
maxReqHeaders = Integer.getInteger(
"sun.net.httpserver.maxReqHeaders",
DEFAULT_MAX_REQ_HEADERS);
maxReqTime = Long.getLong("sun.net.httpserver.maxReqTime",
DEFAULT_MAX_REQ_TIME);
maxRspTime = Long.getLong("sun.net.httpserver.maxRspTime",
DEFAULT_MAX_RSP_TIME);
timerMillis = Long.getLong("sun.net.httpserver.timerMillis",
DEFAULT_TIMER_MILLIS);
debug = Boolean.getBoolean("sun.net.httpserver.debug");
noDelay = Boolean.getBoolean("sun.net.httpserver.nodelay");
return null;
}
});
}
static void checkLegacyProperties(final Logger logger) {
// legacy properties that are no longer used
// print a warning to logger if they are set.
java.security.AccessController.doPrivileged(
new PrivilegedAction<Void>() {
public Void run () {
if (System.getProperty("sun.net.httpserver.readTimeout")
!=null)
{
logger.log (Level.WARNING,
"sun.net.httpserver.readTimeout "+
"property is no longer used. "+
"Use sun.net.httpserver.maxReqTime instead."
);
}
if (System.getProperty("sun.net.httpserver.writeTimeout")
!=null)
{
logger.log (Level.WARNING,
"sun.net.httpserver.writeTimeout "+
"property is no longer used. Use "+
"sun.net.httpserver.maxRspTime instead."
);
}
if (System.getProperty("sun.net.httpserver.selCacheTimeout")
!=null)
{
logger.log (Level.WARNING,
"sun.net.httpserver.selCacheTimeout "+
"property is no longer used."
);
}
return null;
}
}
);
}
static boolean debugEnabled() {
return debug;
}
static long getIdleInterval() {
return idleInterval;
}
static int getClockTick() {
return clockTick;
}
static int getMaxIdleConnections() {
return maxIdleConnections;
}
static long getDrainAmount() {
return drainAmount;
}
static int getMaxReqHeaders() {
return maxReqHeaders;
}
static long getMaxReqTime() {
return maxReqTime;
}
static long getMaxRspTime() {
return maxRspTime;
}
static long getTimerMillis() {
return timerMillis;
}
static boolean noDelay() {
return noDelay;
}
}

View file

@ -0,0 +1,894 @@
/*
* Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import javax.net.ssl.*;
import com.sun.net.httpserver.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import sun.net.httpserver.HttpConnection.State;
/**
* Provides implementation for both HTTP and HTTPS
*/
class ServerImpl implements TimeSource {
private String protocol;
private boolean https;
private Executor executor;
private HttpsConfigurator httpsConfig;
private SSLContext sslContext;
private ContextList contexts;
private InetSocketAddress address;
private ServerSocketChannel schan;
private Selector selector;
private SelectionKey listenerKey;
private Set<HttpConnection> idleConnections;
private Set<HttpConnection> allConnections;
/* following two are used to keep track of the times
* when a connection/request is first received
* and when we start to send the response
*/
private Set<HttpConnection> reqConnections;
private Set<HttpConnection> rspConnections;
private List<Event> events;
private Object lolock = new Object();
private volatile boolean finished = false;
private volatile boolean terminating = false;
private boolean bound = false;
private boolean started = false;
private volatile long time; /* current time */
private volatile long subticks = 0;
private volatile long ticks; /* number of clock ticks since server started */
private HttpServer wrapper;
final static int CLOCK_TICK = ServerConfig.getClockTick();
final static long IDLE_INTERVAL = ServerConfig.getIdleInterval();
final static int MAX_IDLE_CONNECTIONS = ServerConfig.getMaxIdleConnections();
final static long TIMER_MILLIS = ServerConfig.getTimerMillis ();
final static long MAX_REQ_TIME=getTimeMillis(ServerConfig.getMaxReqTime());
final static long MAX_RSP_TIME=getTimeMillis(ServerConfig.getMaxRspTime());
final static boolean timer1Enabled = MAX_REQ_TIME != -1 || MAX_RSP_TIME != -1;
private Timer timer, timer1;
private final Logger logger;
private Thread dispatcherThread;
ServerImpl (
HttpServer wrapper, String protocol, InetSocketAddress addr, int backlog
) throws IOException {
this.protocol = protocol;
this.wrapper = wrapper;
this.logger = System.getLogger ("com.sun.net.httpserver");
ServerConfig.checkLegacyProperties (logger);
https = protocol.equalsIgnoreCase ("https");
this.address = addr;
contexts = new ContextList();
schan = ServerSocketChannel.open();
if (addr != null) {
ServerSocket socket = schan.socket();
socket.bind (addr, backlog);
bound = true;
}
selector = Selector.open ();
schan.configureBlocking (false);
listenerKey = schan.register (selector, SelectionKey.OP_ACCEPT);
dispatcher = new Dispatcher();
idleConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
allConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
reqConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
rspConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
time = System.currentTimeMillis();
timer = new Timer ("server-timer", true);
timer.schedule (new ServerTimerTask(), CLOCK_TICK, CLOCK_TICK);
if (timer1Enabled) {
timer1 = new Timer ("server-timer1", true);
timer1.schedule (new ServerTimerTask1(),TIMER_MILLIS,TIMER_MILLIS);
logger.log (Level.DEBUG, "HttpServer timer1 enabled period in ms: ", TIMER_MILLIS);
logger.log (Level.DEBUG, "MAX_REQ_TIME: "+MAX_REQ_TIME);
logger.log (Level.DEBUG, "MAX_RSP_TIME: "+MAX_RSP_TIME);
}
events = new LinkedList<Event>();
logger.log (Level.DEBUG, "HttpServer created "+protocol+" "+ addr);
}
public void bind (InetSocketAddress addr, int backlog) throws IOException {
if (bound) {
throw new BindException ("HttpServer already bound");
}
if (addr == null) {
throw new NullPointerException ("null address");
}
ServerSocket socket = schan.socket();
socket.bind (addr, backlog);
bound = true;
}
public void start () {
if (!bound || started || finished) {
throw new IllegalStateException ("server in wrong state");
}
if (executor == null) {
executor = new DefaultExecutor();
}
dispatcherThread = new Thread(null, dispatcher, "HTTP-Dispatcher", 0, false);
started = true;
dispatcherThread.start();
}
public void setExecutor (Executor executor) {
if (started) {
throw new IllegalStateException ("server already started");
}
this.executor = executor;
}
private static class DefaultExecutor implements Executor {
public void execute (Runnable task) {
task.run();
}
}
public Executor getExecutor () {
return executor;
}
public void setHttpsConfigurator (HttpsConfigurator config) {
if (config == null) {
throw new NullPointerException ("null HttpsConfigurator");
}
if (started) {
throw new IllegalStateException ("server already started");
}
this.httpsConfig = config;
sslContext = config.getSSLContext();
}
public HttpsConfigurator getHttpsConfigurator () {
return httpsConfig;
}
public final boolean isFinishing() {
return finished;
}
public void stop (int delay) {
if (delay < 0) {
throw new IllegalArgumentException ("negative delay parameter");
}
terminating = true;
try { schan.close(); } catch (IOException e) {}
selector.wakeup();
long latest = System.currentTimeMillis() + delay * 1000;
while (System.currentTimeMillis() < latest) {
delay();
if (finished) {
break;
}
}
finished = true;
selector.wakeup();
synchronized (allConnections) {
for (HttpConnection c : allConnections) {
c.close();
}
}
allConnections.clear();
idleConnections.clear();
timer.cancel();
if (timer1Enabled) {
timer1.cancel();
}
if (dispatcherThread != null) {
try {
dispatcherThread.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.log (Level.TRACE, "ServerImpl.stop: ", e);
}
}
}
Dispatcher dispatcher;
public synchronized HttpContextImpl createContext (String path, HttpHandler handler) {
if (handler == null || path == null) {
throw new NullPointerException ("null handler, or path parameter");
}
HttpContextImpl context = new HttpContextImpl (protocol, path, handler, this);
contexts.add (context);
logger.log (Level.DEBUG, "context created: " + path);
return context;
}
public synchronized HttpContextImpl createContext (String path) {
if (path == null) {
throw new NullPointerException ("null path parameter");
}
HttpContextImpl context = new HttpContextImpl (protocol, path, null, this);
contexts.add (context);
logger.log (Level.DEBUG, "context created: " + path);
return context;
}
public synchronized void removeContext (String path) throws IllegalArgumentException {
if (path == null) {
throw new NullPointerException ("null path parameter");
}
contexts.remove (protocol, path);
logger.log (Level.DEBUG, "context removed: " + path);
}
public synchronized void removeContext (HttpContext context) throws IllegalArgumentException {
if (!(context instanceof HttpContextImpl)) {
throw new IllegalArgumentException ("wrong HttpContext type");
}
contexts.remove ((HttpContextImpl)context);
logger.log (Level.DEBUG, "context removed: " + context.getPath());
}
public InetSocketAddress getAddress() {
return AccessController.doPrivileged(
new PrivilegedAction<InetSocketAddress>() {
public InetSocketAddress run() {
return
(InetSocketAddress)schan.socket()
.getLocalSocketAddress();
}
});
}
Selector getSelector () {
return selector;
}
void addEvent (Event r) {
synchronized (lolock) {
events.add (r);
selector.wakeup();
}
}
/* main server listener task */
class Dispatcher implements Runnable {
private void handleEvent (Event r) {
ExchangeImpl t = r.exchange;
HttpConnection c = t.getConnection();
try {
if (r instanceof WriteFinishedEvent) {
int exchanges = endExchange();
if (terminating && exchanges == 0) {
finished = true;
}
responseCompleted (c);
LeftOverInputStream is = t.getOriginalInputStream();
if (!is.isEOF()) {
t.close = true;
}
if (t.close || idleConnections.size() >= MAX_IDLE_CONNECTIONS) {
c.close();
allConnections.remove (c);
} else {
if (is.isDataBuffered()) {
/* don't re-enable the interestops, just handle it */
requestStarted (c);
handle (c.getChannel(), c);
} else {
connsToRegister.add (c);
}
}
}
} catch (IOException e) {
logger.log (
Level.TRACE, "Dispatcher (1)", e
);
c.close();
}
}
final LinkedList<HttpConnection> connsToRegister =
new LinkedList<HttpConnection>();
void reRegister (HttpConnection c) {
/* re-register with selector */
try {
SocketChannel chan = c.getChannel();
chan.configureBlocking (false);
SelectionKey key = chan.register (selector, SelectionKey.OP_READ);
key.attach (c);
c.selectionKey = key;
c.time = getTime() + IDLE_INTERVAL;
idleConnections.add (c);
} catch (IOException e) {
dprint(e);
logger.log (Level.TRACE, "Dispatcher(8)", e);
c.close();
}
}
public void run() {
while (!finished) {
try {
List<Event> list = null;
synchronized (lolock) {
if (events.size() > 0) {
list = events;
events = new LinkedList<Event>();
}
}
if (list != null) {
for (Event r: list) {
handleEvent (r);
}
}
for (HttpConnection c : connsToRegister) {
reRegister(c);
}
connsToRegister.clear();
selector.select(1000);
/* process the selected list now */
Set<SelectionKey> selected = selector.selectedKeys();
Iterator<SelectionKey> iter = selected.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
iter.remove ();
if (key.equals (listenerKey)) {
if (terminating) {
continue;
}
SocketChannel chan = schan.accept();
// optimist there's a channel
if (chan != null) {
// Set TCP_NODELAY, if appropriate
if (ServerConfig.noDelay()) {
chan.socket().setTcpNoDelay(true);
}
chan.configureBlocking (false);
SelectionKey newkey =
chan.register (selector, SelectionKey.OP_READ);
HttpConnection c = new HttpConnection ();
c.selectionKey = newkey;
c.setChannel (chan);
newkey.attach (c);
requestStarted (c);
allConnections.add (c);
}
} else {
try {
if (key.isReadable()) {
boolean closed;
SocketChannel chan = (SocketChannel)key.channel();
HttpConnection conn = (HttpConnection)key.attachment();
key.cancel();
chan.configureBlocking (true);
if (idleConnections.remove(conn)) {
// was an idle connection so add it
// to reqConnections set.
requestStarted (conn);
}
handle (chan, conn);
} else {
assert false;
}
} catch (CancelledKeyException e) {
handleException(key, null);
} catch (IOException e) {
handleException(key, e);
}
}
}
// call the selector just to process the cancelled keys
selector.selectNow();
} catch (IOException e) {
logger.log (Level.TRACE, "Dispatcher (4)", e);
} catch (Exception e) {
logger.log (Level.TRACE, "Dispatcher (7)", e);
}
}
try {selector.close(); } catch (Exception e) {}
}
private void handleException (SelectionKey key, Exception e) {
HttpConnection conn = (HttpConnection)key.attachment();
if (e != null) {
logger.log (Level.TRACE, "Dispatcher (2)", e);
}
closeConnection(conn);
}
public void handle (SocketChannel chan, HttpConnection conn)
throws IOException
{
try {
Exchange t = new Exchange (chan, protocol, conn);
executor.execute (t);
} catch (HttpError e1) {
logger.log (Level.TRACE, "Dispatcher (4)", e1);
closeConnection(conn);
} catch (IOException e) {
logger.log (Level.TRACE, "Dispatcher (5)", e);
closeConnection(conn);
}
}
}
static boolean debug = ServerConfig.debugEnabled ();
static synchronized void dprint (String s) {
if (debug) {
System.out.println (s);
}
}
static synchronized void dprint (Exception e) {
if (debug) {
System.out.println (e);
e.printStackTrace();
}
}
Logger getLogger () {
return logger;
}
private void closeConnection(HttpConnection conn) {
conn.close();
allConnections.remove(conn);
switch (conn.getState()) {
case REQUEST:
reqConnections.remove(conn);
break;
case RESPONSE:
rspConnections.remove(conn);
break;
case IDLE:
idleConnections.remove(conn);
break;
}
assert !reqConnections.remove(conn);
assert !rspConnections.remove(conn);
assert !idleConnections.remove(conn);
}
/* per exchange task */
class Exchange implements Runnable {
SocketChannel chan;
HttpConnection connection;
HttpContextImpl context;
InputStream rawin;
OutputStream rawout;
String protocol;
ExchangeImpl tx;
HttpContextImpl ctx;
boolean rejected = false;
Exchange (SocketChannel chan, String protocol, HttpConnection conn) throws IOException {
this.chan = chan;
this.connection = conn;
this.protocol = protocol;
}
public void run () {
/* context will be null for new connections */
context = connection.getHttpContext();
boolean newconnection;
SSLEngine engine = null;
String requestLine = null;
SSLStreams sslStreams = null;
try {
if (context != null ) {
this.rawin = connection.getInputStream();
this.rawout = connection.getRawOutputStream();
newconnection = false;
} else {
/* figure out what kind of connection this is */
newconnection = true;
if (https) {
if (sslContext == null) {
logger.log (Level.WARNING,
"SSL connection received. No https contxt created");
throw new HttpError ("No SSL context established");
}
sslStreams = new SSLStreams (ServerImpl.this, sslContext, chan);
rawin = sslStreams.getInputStream();
rawout = sslStreams.getOutputStream();
engine = sslStreams.getSSLEngine();
connection.sslStreams = sslStreams;
} else {
rawin = new BufferedInputStream(
new Request.ReadStream (
ServerImpl.this, chan
));
rawout = new Request.WriteStream (
ServerImpl.this, chan
);
}
connection.raw = rawin;
connection.rawout = rawout;
}
Request req = new Request (rawin, rawout);
requestLine = req.requestLine();
if (requestLine == null) {
/* connection closed */
closeConnection(connection);
return;
}
int space = requestLine.indexOf (' ');
if (space == -1) {
reject (Code.HTTP_BAD_REQUEST,
requestLine, "Bad request line");
return;
}
String method = requestLine.substring (0, space);
int start = space+1;
space = requestLine.indexOf(' ', start);
if (space == -1) {
reject (Code.HTTP_BAD_REQUEST,
requestLine, "Bad request line");
return;
}
String uriStr = requestLine.substring (start, space);
URI uri = new URI (uriStr);
start = space+1;
String version = requestLine.substring (start);
Headers headers = req.headers();
String s = headers.getFirst ("Transfer-encoding");
long clen = 0L;
if (s !=null && s.equalsIgnoreCase ("chunked")) {
clen = -1L;
} else {
s = headers.getFirst ("Content-Length");
if (s != null) {
clen = Long.parseLong(s);
}
if (clen == 0) {
requestCompleted (connection);
}
}
ctx = contexts.findContext (protocol, uri.getPath());
if (ctx == null) {
reject (Code.HTTP_NOT_FOUND,
requestLine, "No context found for request");
return;
}
connection.setContext (ctx);
if (ctx.getHandler() == null) {
reject (Code.HTTP_INTERNAL_ERROR,
requestLine, "No handler for context");
return;
}
tx = new ExchangeImpl (
method, uri, req, clen, connection
);
String chdr = headers.getFirst("Connection");
Headers rheaders = tx.getResponseHeaders();
if (chdr != null && chdr.equalsIgnoreCase ("close")) {
tx.close = true;
}
if (version.equalsIgnoreCase ("http/1.0")) {
tx.http10 = true;
if (chdr == null) {
tx.close = true;
rheaders.set ("Connection", "close");
} else if (chdr.equalsIgnoreCase ("keep-alive")) {
rheaders.set ("Connection", "keep-alive");
int idle=(int)(ServerConfig.getIdleInterval()/1000);
int max=ServerConfig.getMaxIdleConnections();
String val = "timeout="+idle+", max="+max;
rheaders.set ("Keep-Alive", val);
}
}
if (newconnection) {
connection.setParameters (
rawin, rawout, chan, engine, sslStreams,
sslContext, protocol, ctx, rawin
);
}
/* check if client sent an Expect 100 Continue.
* In that case, need to send an interim response.
* In future API may be modified to allow app to
* be involved in this process.
*/
String exp = headers.getFirst("Expect");
if (exp != null && exp.equalsIgnoreCase ("100-continue")) {
logReply (100, requestLine, null);
sendReply (
Code.HTTP_CONTINUE, false, null
);
}
/* uf is the list of filters seen/set by the user.
* sf is the list of filters established internally
* and which are not visible to the user. uc and sc
* are the corresponding Filter.Chains.
* They are linked together by a LinkHandler
* so that they can both be invoked in one call.
*/
List<Filter> sf = ctx.getSystemFilters();
List<Filter> uf = ctx.getFilters();
Filter.Chain sc = new Filter.Chain(sf, ctx.getHandler());
Filter.Chain uc = new Filter.Chain(uf, new LinkHandler (sc));
/* set up the two stream references */
tx.getRequestBody();
tx.getResponseBody();
if (https) {
uc.doFilter (new HttpsExchangeImpl (tx));
} else {
uc.doFilter (new HttpExchangeImpl (tx));
}
} catch (IOException e1) {
logger.log (Level.TRACE, "ServerImpl.Exchange (1)", e1);
closeConnection(connection);
} catch (NumberFormatException e3) {
reject (Code.HTTP_BAD_REQUEST,
requestLine, "NumberFormatException thrown");
} catch (URISyntaxException e) {
reject (Code.HTTP_BAD_REQUEST,
requestLine, "URISyntaxException thrown");
} catch (Exception e4) {
logger.log (Level.TRACE, "ServerImpl.Exchange (2)", e4);
closeConnection(connection);
}
}
/* used to link to 2 or more Filter.Chains together */
class LinkHandler implements HttpHandler {
Filter.Chain nextChain;
LinkHandler (Filter.Chain nextChain) {
this.nextChain = nextChain;
}
public void handle (HttpExchange exchange) throws IOException {
nextChain.doFilter (exchange);
}
}
void reject (int code, String requestStr, String message) {
rejected = true;
logReply (code, requestStr, message);
sendReply (
code, false, "<h1>"+code+Code.msg(code)+"</h1>"+message
);
closeConnection(connection);
}
void sendReply (
int code, boolean closeNow, String text)
{
try {
StringBuilder builder = new StringBuilder (512);
builder.append ("HTTP/1.1 ")
.append (code).append (Code.msg(code)).append ("\r\n");
if (text != null && text.length() != 0) {
builder.append ("Content-Length: ")
.append (text.length()).append ("\r\n")
.append ("Content-Type: text/html\r\n");
} else {
builder.append ("Content-Length: 0\r\n");
text = "";
}
if (closeNow) {
builder.append ("Connection: close\r\n");
}
builder.append ("\r\n").append (text);
String s = builder.toString();
byte[] b = s.getBytes("ISO8859_1");
rawout.write (b);
rawout.flush();
if (closeNow) {
closeConnection(connection);
}
} catch (IOException e) {
logger.log (Level.TRACE, "ServerImpl.sendReply", e);
closeConnection(connection);
}
}
}
void logReply (int code, String requestStr, String text) {
if (!logger.isLoggable(Level.DEBUG)) {
return;
}
if (text == null) {
text = "";
}
String r;
if (requestStr.length() > 80) {
r = requestStr.substring (0, 80) + "<TRUNCATED>";
} else {
r = requestStr;
}
String message = r + " [" + code + " " +
Code.msg(code) + "] ("+text+")";
logger.log (Level.DEBUG, message);
}
long getTicks() {
return ticks;
}
public long getTime() {
return time;
}
void delay () {
Thread.yield();
try {
Thread.sleep (200);
} catch (InterruptedException e) {}
}
private int exchangeCount = 0;
synchronized void startExchange () {
exchangeCount ++;
}
synchronized int endExchange () {
exchangeCount --;
assert exchangeCount >= 0;
return exchangeCount;
}
HttpServer getWrapper () {
return wrapper;
}
void requestStarted (HttpConnection c) {
c.creationTime = getTime();
c.setState (State.REQUEST);
reqConnections.add (c);
}
// called after a request has been completely read
// by the server. This stops the timer which would
// close the connection if the request doesn't arrive
// quickly enough. It then starts the timer
// that ensures the client reads the response in a timely
// fashion.
void requestCompleted (HttpConnection c) {
assert c.getState() == State.REQUEST;
reqConnections.remove (c);
c.rspStartedTime = getTime();
rspConnections.add (c);
c.setState (State.RESPONSE);
}
// called after response has been sent
void responseCompleted (HttpConnection c) {
assert c.getState() == State.RESPONSE;
rspConnections.remove (c);
c.setState (State.IDLE);
}
/**
* TimerTask run every CLOCK_TICK ms
*/
class ServerTimerTask extends TimerTask {
public void run () {
LinkedList<HttpConnection> toClose = new LinkedList<HttpConnection>();
time = System.currentTimeMillis();
ticks ++;
synchronized (idleConnections) {
for (HttpConnection c : idleConnections) {
if (c.time <= time) {
toClose.add (c);
}
}
for (HttpConnection c : toClose) {
idleConnections.remove (c);
allConnections.remove (c);
c.close();
}
}
}
}
class ServerTimerTask1 extends TimerTask {
// runs every TIMER_MILLIS
public void run () {
LinkedList<HttpConnection> toClose = new LinkedList<HttpConnection>();
time = System.currentTimeMillis();
synchronized (reqConnections) {
if (MAX_REQ_TIME != -1) {
for (HttpConnection c : reqConnections) {
if (c.creationTime + TIMER_MILLIS + MAX_REQ_TIME <= time) {
toClose.add (c);
}
}
for (HttpConnection c : toClose) {
logger.log (Level.DEBUG, "closing: no request: " + c);
reqConnections.remove (c);
allConnections.remove (c);
c.close();
}
}
}
toClose = new LinkedList<HttpConnection>();
synchronized (rspConnections) {
if (MAX_RSP_TIME != -1) {
for (HttpConnection c : rspConnections) {
if (c.rspStartedTime + TIMER_MILLIS +MAX_RSP_TIME <= time) {
toClose.add (c);
}
}
for (HttpConnection c : toClose) {
logger.log (Level.DEBUG, "closing: no response: " + c);
rspConnections.remove (c);
allConnections.remove (c);
c.close();
}
}
}
}
}
void logStackTrace (String s) {
logger.log (Level.TRACE, s);
StringBuilder b = new StringBuilder ();
StackTraceElement[] e = Thread.currentThread().getStackTrace();
for (int i=0; i<e.length; i++) {
b.append (e[i].toString()).append("\n");
}
logger.log (Level.TRACE, b.toString());
}
static long getTimeMillis(long secs) {
if (secs == -1) {
return -1;
} else {
return secs * 1000;
}
}
}

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.io.*;
class StreamClosedException extends IOException {
private static final long serialVersionUID = -4485921499356327937L;
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
interface TimeSource {
public long getTime();
}

View file

@ -0,0 +1,81 @@
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.io.*;
import java.net.*;
import com.sun.net.httpserver.*;
import com.sun.net.httpserver.spi.*;
/**
* a class which allows the caller to write an indefinite
* number of bytes to an underlying stream , but without using
* chunked encoding. Used for http/1.0 clients only
* The underlying connection needs to be closed afterwards.
*/
class UndefLengthOutputStream extends FilterOutputStream
{
private boolean closed = false;
ExchangeImpl t;
UndefLengthOutputStream (ExchangeImpl t, OutputStream src) {
super (src);
this.t = t;
}
public void write (int b) throws IOException {
if (closed) {
throw new IOException ("stream closed");
}
out.write(b);
}
public void write (byte[]b, int off, int len) throws IOException {
if (closed) {
throw new IOException ("stream closed");
}
out.write(b, off, len);
}
public void close () throws IOException {
if (closed) {
return;
}
closed = true;
flush();
LeftOverInputStream is = t.getOriginalInputStream();
if (!is.isClosed()) {
try {
is.close();
} catch (IOException e) {}
}
WriteFinishedEvent e = new WriteFinishedEvent (t);
t.getHttpContext().getServerImpl().addEvent (e);
}
// flush is a pass-through
}

View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.util.*;
import com.sun.net.httpserver.*;
class UnmodifiableHeaders extends Headers {
Headers map;
UnmodifiableHeaders(Headers map) {
this.map = map;
}
public int size() {return map.size();}
public boolean isEmpty() {return map.isEmpty();}
public boolean containsKey(Object key) {
return map.containsKey (key);
}
public boolean containsValue(Object value) {
return map.containsValue(value);
}
public List<String> get(Object key) {
return map.get(key);
}
public String getFirst (String key) {
return map.getFirst(key);
}
public List<String> put(String key, List<String> value) {
return map.put (key, value);
}
public void add (String key, String value) {
throw new UnsupportedOperationException ("unsupported operation");
}
public void set (String key, String value) {
throw new UnsupportedOperationException ("unsupported operation");
}
public List<String> remove(Object key) {
throw new UnsupportedOperationException ("unsupported operation");
}
public void putAll(Map<? extends String,? extends List<String>> t) {
throw new UnsupportedOperationException ("unsupported operation");
}
public void clear() {
throw new UnsupportedOperationException ("unsupported operation");
}
public Set<String> keySet() {
return Collections.unmodifiableSet (map.keySet());
}
public Collection<List<String>> values() {
return Collections.unmodifiableCollection(map.values());
}
/* TODO check that contents of set are not modifable : security */
public Set<Map.Entry<String, List<String>>> entrySet() {
return Collections.unmodifiableSet (map.entrySet());
}
public boolean equals(Object o) {return map.equals(o);}
public int hashCode() {return map.hashCode();}
}

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
class WriteFinishedEvent extends Event {
WriteFinishedEvent (ExchangeImpl t) {
super (t);
assert !t.writefinished;
t.writefinished = true;
}
}