<property name="tomcat.lite.src" value="${basedir}/" />
<property name="classes" value="${basedir}/target/tomcat-lite/classes" />
<property name="test-classes" value="${basedir}/target/tomcat-lite/test-classes" />
+ <property name="http.test-classes" value="${basedir}/target/tomcat-lite/http-test-classes" />
<property name="jar.dir" value="${basedir}/target/tomcat-lite/" />
<property name="MAIN" value="org.apache.tomcat.integration.simple.Main"/>
<property name="compile.source" value="1.6"/>
+ <property name="compile.debug" value="true"/>
<!-- All Ivy downloads -->
<path id='lite-classpath'>
<pathelement location="target/lib/commons-codec.jar"/>
</path>
+ <target name="http" depends="http.compile,http.test,http.pack"/>
+
+ <target name="http.compile"
+ description="Compile only the HTTP connector classes, no servlets">
+ <mkdir dir="${classes}"/>
+ <javac destdir="${classes}"
+ debug="${compile.debug}"
+ deprecation="${compile.deprecation}"
+ source="${compile.source}"
+ optimize="${compile.optimize}"
+ encoding="ISO-8859-1">
+ <classpath refid="lite-classpath" />
+ <src path="${tomcat.lite.src}/java" />
+ <classpath refid="head-classpath" />
+ <exclude name="org/apache/tomcat/servlets/**"/>
+ <exclude name="org/apache/tomcat/lite/servlet/**"/>
+ </javac>
+ <copy todir="${classes}">
+ <fileset dir="${tomcat.lite.src}/java"
+ includes="**/*.properties **/*.xml" />
+ </copy>
+ </target>
+
+ <target name="http.test.compile"
+ description="Test only the HTTP connector classes, no servlets">
+ <mkdir dir="${http.test-classes}"/>
+ <javac destdir="${http.test-classes}"
+ debug="${compile.debug}"
+ deprecation="${compile.deprecation}"
+ source="${compile.source}"
+ optimize="${compile.optimize}"
+ encoding="ISO-8859-1"
+ >
+ <classpath refid="lite-classpath" />
+ <classpath refid="head-classpath" />
+ <classpath path="${classes}" />
+ <src path="${tomcat.lite.src}/test" />
+ <exclude name="org/apache/tomcat/lite/servlet/**"/>
+ <exclude name="org/apache/coyote/lite/**"/>
+ <exclude name="org/apache/tomcat/test/watchdog/**"/>
+ </javac>
+ <copy todir="${http.test-classes}">
+ <fileset dir="${tomcat.lite.src}/test"
+ includes="**/*.properties **/*.xml **/*.bin **/*.keystore" />
+ </copy>
+ </target>
+
+ <target name="http.test" depends="http.test.compile" >
+ <!-- also: perTest(default) -->
+ <junit printsummary="on" fork="once"
+ timeout="600000" maxmemory="1G" outputtoformatters="no"
+ >
+ <classpath refid="lite-classpath" />
+ <classpath refid="head-classpath" />
+ <classpath path="${http.test-classes}" />
+ <classpath path="${classes}" />
+
+ <formatter type="brief" usefile="false" />
+
+ <batchtest>
+ <fileset dir="test" >
+ <!-- Include all by default -->
+ <include name="**/*Test.java" />
+ <include name="**/*Tests.java" />
+ <!-- Exclude TestAll ortherwise there will be duplicated -->
+ <exclude name="**/TestAll.java" />
+ <!-- Exclude helper classes -->
+ <exclude name="**/Tester*.java" />
+
+ <exclude name="org/apache/tomcat/lite/servlet/**"/>
+ <exclude name="org/apache/coyote/lite/**"/>
+ <exclude name="org/apache/tomcat/test/watchdog/**"/>
+ </fileset>
+ </batchtest>
+ </junit>
+ </target>
+
+ <target name="runtest" depends="http.test.compile">
+ <junit printsummary="withOutAndErr" fork="no" dir="${tomcat.base}">
+ <sysproperty key="tests" value="${tests}"/>
+ <classpath refid="lite-classpath" />
+ <classpath refid="head-classpath" />
+ <classpath path="${http.test-classes}" />
+ <classpath path="${classes}" />
+
+ <formatter type="plain" usefile="false" />
+
+ <batchtest>
+ <fileset dir="test" >
+ <include name="**/${test}.java" />
+ </fileset>
+ </batchtest>
+ </junit>
+
+ </target>
+
+ <target name="http.pack"
+ description="Pack the HTTP client and connector">
+ </target>
<target name="compile"
- description="Build against tomcat head">
+ description="Build all classes against tomcat head.">
+ <mkdir dir="${classes}"/>
<javac destdir="${classes}"
debug="${compile.debug}"
deprecation="${compile.deprecation}"
deprecation="${compile.deprecation}"
source="${compile.source}"
optimize="${compile.optimize}"
- encoding="ISO-8859-1"
- >
+ encoding="ISO-8859-1">
<classpath refid="lite-classpath" />
<src path="${tomcat.lite.src}/java" />
<exclude name="**/ServletApi30.java"/>
<exclude name="org/apache/tomcat/coyote/servlet/*.java"/>
</javac>
-
<copy todir="${classes}">
<fileset dir="${tomcat.lite.src}/java"
includes="**/*.properties **/*.xml" />
*
* All objects of interest are registered automatically.
*/
-public class JmxObjectManagerSpi extends ObjectManager {
+public class JmxObjectManagerSpi extends ObjectManager implements Runnable {
Registry registry;
Logger log = Logger.getLogger("JmxObjectManager");
public Object get(String key) {
return null;
}
+
+ ObjectManager om;
+
+ public void setObjectManager(ObjectManager om) {
+ this.om = om;
+ }
+ public void run() {
+ om.register(this);
+ // TODO: register existing objects in JMX
+ }
}
import java.io.IOException;
import java.io.PrintWriter;
-import java.util.HashMap;
import java.util.Iterator;
-import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import org.apache.tomcat.integration.DynamicObject;
-import org.apache.tomcat.integration.ObjectManager;
import org.apache.tomcat.lite.http.HttpRequest;
import org.apache.tomcat.lite.http.HttpResponse;
import org.apache.tomcat.lite.http.HttpWriter;
*/
public class UJmxHandler implements HttpService {
- private static Logger log = Logger.getLogger(UJmxHandler.class.getName());
+ protected static Logger log = Logger.getLogger(UJmxHandler.class.getName());
private UJmxObjectManagerSpi jmx;
+ public UJmxHandler() {
+ }
+
public UJmxHandler(UJmxObjectManagerSpi jmx) {
this.jmx = jmx;
}
public void getAttribute(PrintWriter writer, String onameStr, String att) {
try {
-
Object bean = jmx.objects.get(onameStr);
Class beanClass = bean.getClass();
DynamicObject ci = jmx.getClassInfo(beanClass);
int index = type.indexOf(';');
while (index != -1) {
index++;
- while (index < len && Character.isSpace(type.charAt(index))) {
+ while (index < len && Character.isWhitespace(type.charAt(index))) {
index++;
}
if (index+8 < len
import java.io.IOException;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
return closeInHead();
}
}
+
+
return true;
}
if (!headersReceived) {
headRecvBuf.wrapTo(headW);
parseMessage(activeHttp, headW);
+ // Part of parseMessage we can switch the protocol
+ if (switchedProtocol != null) {
+ return;
+ }
+
if (serverMode && activeHttp.httpReq.decodedUri.remaining() == 0) {
abort(activeHttp, "Invalid url");
}
return statusCode.remaining() > 0;
}
+ List<String> connectionHeaders = new ArrayList<String>();
+
private void parseHeaders(HttpChannel http, HttpMessageBytes msgBytes,
BBuffer head)
throws IOException {
head.readLine(line);
int idx = 0;
+
+ BBuffer upgrade = null;
+
while(line.remaining() > 0) {
// not empty..
idx = msgBytes.addHeader();
parseHeader(http, head, line, nameBuf, valBuf);
// TODO: process 'interesting' headers here.
+ if (nameBuf.equalsIgnoreCase("connection")) {
+ // TODO: save and remove if not recognized
+ }
+ if (nameBuf.equalsIgnoreCase("upgrade")) {
+ upgrade = valBuf;
+ }
+ }
+
+ if (upgrade != null) {
+ if (upgrade.equalsIgnoreCase("WebSocket")) {
+
+ } else if (upgrade.equalsIgnoreCase("SPDY/1.0")) {
+
+ }
}
+
+ // TODO: process connection headers
}
/**
HttpChannel httpCh = activeHttp;
boolean ssl = httpCh.getRequest().isSecure();
if (ssl) {
- SslChannel ch1 = new SslChannel();
- ch1.setSslContext(httpConnector.sslConnector.getSSLContext());
+ String[] hostPort = httpCh.getTarget().split(":");
+
+ SslChannel ch1 = httpConnector.sslConnector.channel(
+ hostPort[0], Integer.parseInt(hostPort[1]));
ch1.setSink(net);
net.addFilterAfter(ch1);
net = ch1;
}
if (!net.isOpen()) {
- httpCh.abort("Can't connect");
+ httpCh.abort(net.lastException());
return;
}
if (con.isOpen()) {
hits.incrementAndGet();
- if (debug) {
- httpCh.trace("HTTP_CONNECT: Reuse connection " + target + " " + this);
- }
+// if (debug) {
+// log.info("HTTP_CONNECT: Reuse connection " + target + " " + this);
+// }
con.sendRequest(httpCh);
} else {
misses.incrementAndGet();
if (debug) {
- httpCh.trace("HTTP_CONNECT: Start connection " + target + " " + this);
+ log.info("HTTP_CONNECT: Start connection " + target + " " + this);
}
httpConnect(httpCh, target, ssl,
(Http11Connection) con);
boolean ssl, IOConnector.ConnectedCallback cb)
throws IOException {
if (debug) {
- httpCh.trace("HTTP_CONNECT: New connection " + target);
+ log.info("HTTP_CONNECT: New connection " + target);
}
String[] hostPort = target.split(":");
// again.
if (remoteServer.pending.size() == 0) {
con.activeHttp = null;
- if (debug) {
- log.info("After request: no pending");
- }
return;
}
req = remoteServer.pending.remove();
return dispatcher;
}
+ public HttpConnectionPool getConnectionPool() {
+ return cpool;
+ }
+
public HttpConnector withIOConnector(IOConnector selectors) {
ioConnector = selectors;
return this;
import java.io.BufferedReader;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
import org.apache.tomcat.lite.http.HttpChannel.RequestCompleted;
import org.apache.tomcat.lite.http.HttpConnector.HttpConnection;
import org.apache.tomcat.lite.io.BBuffer;
import org.apache.tomcat.lite.io.BufferedIOReader;
import org.apache.tomcat.lite.io.CBuffer;
+import org.apache.tomcat.lite.io.FastHttpDateFormat;
import org.apache.tomcat.lite.io.IOBuffer;
import org.apache.tomcat.lite.io.IOInputStream;
import org.apache.tomcat.lite.io.IOOutputStream;
}
}
}
-
+
+ protected static final TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT");
+
private HttpMessageBytes msgBytes = new HttpMessageBytes();
protected HttpMessage.State state = HttpMessage.State.HEAD;
long contentLength = -2;
boolean chunked;
+ /**
+ * The set of SimpleDateFormat formats to use in getDateHeader().
+ *
+ * Notice that because SimpleDateFormat is not thread-safe, we can't
+ * declare formats[] as a static variable.
+ */
+ protected SimpleDateFormat formats[] = null;
+
+
BBuffer clBuffer = BBuffer.allocate(64);
public HttpMessage(HttpChannel httpCh) {
return headers;
}
+ /**
+ * Return the value of the specified date header, if any; otherwise
+ * return -1.
+ *
+ * @param name Name of the requested date header
+ *
+ * @exception IllegalArgumentException if the specified header value
+ * cannot be converted to a date
+ */
+ public long getDateHeader(String name) {
+
+ String value = getHeader(name);
+ if (value == null)
+ return (-1L);
+ if (formats == null) {
+ formats = new SimpleDateFormat[] {
+ new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
+ new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
+ new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
+ };
+ formats[0].setTimeZone(GMT_ZONE);
+ formats[1].setTimeZone(GMT_ZONE);
+ formats[2].setTimeZone(GMT_ZONE);
+ }
+
+ // Attempt to convert the date header in a variety of formats
+ long result = FastHttpDateFormat.parseDate(value, formats);
+ if (result != (-1L)) {
+ return result;
+ }
+ throw new IllegalArgumentException(value);
+
+ }
+
+
public Collection<String> getHeaderNames() {
MultiMap headers = getMimeHeaders();
return in;
}
+ public InputStream getInputStream() {
+ return in;
+ }
+
+ public IOOutputStream getOutputStream() {
+ return out;
+ }
+
public IOOutputStream getBodyOutputStream() {
return out;
}
reader.setEncoding(getCharacterEncoding());
return bufferedReader;
}
+
+ public PrintWriter getWriter() {
+ return new PrintWriter(getBodyWriter());
+ }
public HttpWriter getBodyWriter() {
conv.setEncoding(getCharacterEncoding());
return (mappingData);
}
+ /**
+ * Return the portion of the request URI used to select the Context
+ * of the Request.
+ */
+ public String getContextPath() {
+ return (getMappingData().contextPath.toString());
+ }
+
+ public String getPathInfo() {
+ CBuffer pathInfo = getMappingData().pathInfo;
+ if (pathInfo.length() == 0) {
+ return null;
+ }
+ return (getMappingData().pathInfo.toString());
+ }
+
+ /**
+ * Return the portion of the request URI used to select the servlet
+ * that will process this request.
+ */
+ public String getServletPath() {
+ return (getMappingData().wrapperPath.toString());
+ }
+
/**
* Parse query parameters - but not POST body.
*
*/
protected void processReceivedHeaders() throws IOException {
BBuffer url = getMsgBytes().url();
+ if (url.remaining() == 0) {
+ System.err.println("No input");
+ }
if (url.get(0) == 'h') {
int firstSlash = url.indexOf('/', 0);
schemeMB.appendAscii(url.array(),
import org.apache.tomcat.lite.io.BBucket;
import org.apache.tomcat.lite.io.BBuffer;
import org.apache.tomcat.lite.io.CBuffer;
-import org.apache.tomcat.lite.io.IOBuffer;
public class HttpResponse extends HttpMessage {
+
+ /*
+ * Server status codes; see RFC 2068.
+ */
+
+ /**
+ * Status code (100) indicating the client can continue.
+ */
+
+ public static final int SC_CONTINUE = 100;
+
+
+ /**
+ * Status code (101) indicating the server is switching protocols
+ * according to Upgrade header.
+ */
+
+ public static final int SC_SWITCHING_PROTOCOLS = 101;
+
+ /**
+ * Status code (200) indicating the request succeeded normally.
+ */
+
+ public static final int SC_OK = 200;
+
+ /**
+ * Status code (201) indicating the request succeeded and created
+ * a new resource on the server.
+ */
+
+ public static final int SC_CREATED = 201;
+
+ /**
+ * Status code (202) indicating that a request was accepted for
+ * processing, but was not completed.
+ */
+
+ public static final int SC_ACCEPTED = 202;
+
+ /**
+ * Status code (203) indicating that the meta information presented
+ * by the client did not originate from the server.
+ */
+
+ public static final int SC_NON_AUTHORITATIVE_INFORMATION = 203;
+
+ /**
+ * Status code (204) indicating that the request succeeded but that
+ * there was no new information to return.
+ */
+
+ public static final int SC_NO_CONTENT = 204;
+
+ /**
+ * Status code (205) indicating that the agent <em>SHOULD</em> reset
+ * the document view which caused the request to be sent.
+ */
+
+ public static final int SC_RESET_CONTENT = 205;
+
+ /**
+ * Status code (206) indicating that the server has fulfilled
+ * the partial GET request for the resource.
+ */
+
+ public static final int SC_PARTIAL_CONTENT = 206;
+
+ /**
+ * Used by Webdav.
+ */
+ public static final int SC_MULTI_STATUS = 207;
+ // This one collides with HTTP 1.1
+ // "207 Partial Update OK"
+
+ /**
+ * Status code (300) indicating that the requested resource
+ * corresponds to any one of a set of representations, each with
+ * its own specific location.
+ */
+
+ public static final int SC_MULTIPLE_CHOICES = 300;
+
+ /**
+ * Status code (301) indicating that the resource has permanently
+ * moved to a new location, and that future references should use a
+ * new URI with their requests.
+ */
+
+ public static final int SC_MOVED_PERMANENTLY = 301;
+
+ /**
+ * Status code (302) indicating that the resource has temporarily
+ * moved to another location, but that future references should
+ * still use the original URI to access the resource.
+ *
+ * This definition is being retained for backwards compatibility.
+ * SC_FOUND is now the preferred definition.
+ */
+
+ public static final int SC_MOVED_TEMPORARILY = 302;
+
+ /**
+ * Status code (302) indicating that the resource reside
+ * temporarily under a different URI. Since the redirection might
+ * be altered on occasion, the client should continue to use the
+ * Request-URI for future requests.(HTTP/1.1) To represent the
+ * status code (302), it is recommended to use this variable.
+ */
+
+ public static final int SC_FOUND = 302;
+
+ /**
+ * Status code (303) indicating that the response to the request
+ * can be found under a different URI.
+ */
+
+ public static final int SC_SEE_OTHER = 303;
+
+ /**
+ * Status code (304) indicating that a conditional GET operation
+ * found that the resource was available and not modified.
+ */
+
+ public static final int SC_NOT_MODIFIED = 304;
+
+ /**
+ * Status code (305) indicating that the requested resource
+ * <em>MUST</em> be accessed through the proxy given by the
+ * <code><em>Location</em></code> field.
+ */
+
+ public static final int SC_USE_PROXY = 305;
+
+ /**
+ * Status code (307) indicating that the requested resource
+ * resides temporarily under a different URI. The temporary URI
+ * <em>SHOULD</em> be given by the <code><em>Location</em></code>
+ * field in the response.
+ */
+
+ public static final int SC_TEMPORARY_REDIRECT = 307;
+
+ /**
+ * Status code (400) indicating the request sent by the client was
+ * syntactically incorrect.
+ */
+
+ public static final int SC_BAD_REQUEST = 400;
+
+ /**
+ * Status code (401) indicating that the request requires HTTP
+ * authentication.
+ */
+
+ public static final int SC_UNAUTHORIZED = 401;
+
+ /**
+ * Status code (402) reserved for future use.
+ */
+
+ public static final int SC_PAYMENT_REQUIRED = 402;
+
+ /**
+ * Status code (403) indicating the server understood the request
+ * but refused to fulfill it.
+ */
+
+ public static final int SC_FORBIDDEN = 403;
+
+ /**
+ * Status code (404) indicating that the requested resource is not
+ * available.
+ */
+
+ public static final int SC_NOT_FOUND = 404;
+
+ /**
+ * Status code (405) indicating that the method specified in the
+ * <code><em>Request-Line</em></code> is not allowed for the resource
+ * identified by the <code><em>Request-URI</em></code>.
+ */
+
+ public static final int SC_METHOD_NOT_ALLOWED = 405;
+
+ /**
+ * Status code (406) indicating that the resource identified by the
+ * request is only capable of generating response entities which have
+ * content characteristics not acceptable according to the accept
+ * headers sent in the request.
+ */
+
+ public static final int SC_NOT_ACCEPTABLE = 406;
+
+ /**
+ * Status code (407) indicating that the client <em>MUST</em> first
+ * authenticate itself with the proxy.
+ */
+
+ public static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
+
+ /**
+ * Status code (408) indicating that the client did not produce a
+ * request within the time that the server was prepared to wait.
+ */
+
+ public static final int SC_REQUEST_TIMEOUT = 408;
+
+ /**
+ * Status code (409) indicating that the request could not be
+ * completed due to a conflict with the current state of the
+ * resource.
+ */
+
+ public static final int SC_CONFLICT = 409;
+
+ /**
+ * Status code (410) indicating that the resource is no longer
+ * available at the server and no forwarding address is known.
+ * This condition <em>SHOULD</em> be considered permanent.
+ */
+
+ public static final int SC_GONE = 410;
+
+ /**
+ * Status code (411) indicating that the request cannot be handled
+ * without a defined <code><em>Content-Length</em></code>.
+ */
+
+ public static final int SC_LENGTH_REQUIRED = 411;
+
+ /**
+ * Status code (412) indicating that the precondition given in one
+ * or more of the request-header fields evaluated to false when it
+ * was tested on the server.
+ */
+
+ public static final int SC_PRECONDITION_FAILED = 412;
+
+ /**
+ * Status code (413) indicating that the server is refusing to process
+ * the request because the request entity is larger than the server is
+ * willing or able to process.
+ */
+
+ public static final int SC_REQUEST_ENTITY_TOO_LARGE = 413;
+
+ /**
+ * Status code (414) indicating that the server is refusing to service
+ * the request because the <code><em>Request-URI</em></code> is longer
+ * than the server is willing to interpret.
+ */
+
+ public static final int SC_REQUEST_URI_TOO_LONG = 414;
+
+ /**
+ * Status code (415) indicating that the server is refusing to service
+ * the request because the entity of the request is in a format not
+ * supported by the requested resource for the requested method.
+ */
+
+ public static final int SC_UNSUPPORTED_MEDIA_TYPE = 415;
+
+ /**
+ * Status code (416) indicating that the server cannot serve the
+ * requested byte range.
+ */
+
+ public static final int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
+
+ /**
+ * Status code (417) indicating that the server could not meet the
+ * expectation given in the Expect request header.
+ */
+
+ public static final int SC_EXPECTATION_FAILED = 417;
+
+ /**
+ * Status code (423) indicating the destination resource of a
+ * method is locked, and either the request did not contain a
+ * valid Lock-Info header, or the Lock-Info header identifies
+ * a lock held by another principal.
+ */
+ public static final int SC_LOCKED = 423;
+
+ /**
+ * Status code (500) indicating an error inside the HTTP server
+ * which prevented it from fulfilling the request.
+ */
+
+ public static final int SC_INTERNAL_SERVER_ERROR = 500;
+
+ /**
+ * Status code (501) indicating the HTTP server does not support
+ * the functionality needed to fulfill the request.
+ */
+
+ public static final int SC_NOT_IMPLEMENTED = 501;
+
+ /**
+ * Status code (502) indicating that the HTTP server received an
+ * invalid response from a server it consulted when acting as a
+ * proxy or gateway.
+ */
+
+ public static final int SC_BAD_GATEWAY = 502;
+
+ /**
+ * Status code (503) indicating that the HTTP server is
+ * temporarily overloaded, and unable to handle the request.
+ */
+
+ public static final int SC_SERVICE_UNAVAILABLE = 503;
+
+ /**
+ * Status code (504) indicating that the server did not receive
+ * a timely response from the upstream server while acting as
+ * a gateway or proxy.
+ */
+
+ public static final int SC_GATEWAY_TIMEOUT = 504;
+
+ /**
+ * Status code (505) indicating that the server does not support
+ * or refuses to support the HTTP protocol version that was used
+ * in the request message.
+ */
+
+ public static final int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
// will not be recycled
public Object nativeResponse;
status = i;
}
+ public void sendError(int status) {
+ this.status = status;
+ }
+
+ public void sendError(int status, String msg) {
+ message.set(msg);
+ }
+
public int getStatus() {
if (status >= 0) {
return status;
* Common messages are cached.
*
*/
- BBucket getMessage( int status ) {
+ static BBucket getMessage( int status ) {
// method from Response.
// Does HTTP requires/allow international messages or
return bb;
}
+ public static String getStatusText(int code) {
+ return getMessage(code).toString();
+ }
static BBucket st_unknown = BBuffer.wrapper("No Message");
static BBucket st_200 = BBuffer.wrapper("OK");
addStatus(504, "Gateway Timeout");
addStatus(505, "HTTP Version Not Supported");
addStatus(507, "Insufficient Storage");
+ addStatus(SC_LOCKED, "Locked");
+
}
CompressFilter headCompressIn = new CompressFilter()
.setDictionary(SPDY_DICT, DICT_ID);
+
CompressFilter headCompressOut = new CompressFilter()
.setDictionary(SPDY_DICT, DICT_ID);
+
IOBuffer headerCompressBuffer = new IOBuffer();
+ IOBuffer headerDeCompressBuffer = new IOBuffer();
AtomicInteger inFrames = new AtomicInteger();
AtomicInteger inDataFrames = new AtomicInteger();
}
@Override
- public synchronized void dataReceived(IOBuffer iob) throws IOException {
- while (true) {
- int avail = iob.available();
- if (avail == 0) {
- return;
- }
- if (currentInFrame == null) {
- if (inFrameBuffer.remaining() + avail < 8) {
+ public void dataReceived(IOBuffer iob) throws IOException {
+ // Only one thread doing receive at a time.
+ synchronized (inFrameBuffer) {
+ while (true) {
+ int avail = iob.available();
+ if (avail == 0) {
return;
}
- if (inFrameBuffer.remaining() < 8) {
- int headRest = 8 - inFrameBuffer.remaining();
- int rd = iob.read(inFrameBuffer, headRest);
+ if (currentInFrame == null) {
+ if (inFrameBuffer.remaining() + avail < 8) {
+ return;
+ }
+ if (inFrameBuffer.remaining() < 8) {
+ int headRest = 8 - inFrameBuffer.remaining();
+ int rd = iob.read(inFrameBuffer, headRest);
+ }
+ currentInFrame = new SpdyConnection.Frame(); // TODO: reuse
+ currentInFrame.parse(this, inFrameBuffer);
}
- currentInFrame = new SpdyConnection.Frame(); // TODO: reuse
- currentInFrame.parse(this, inFrameBuffer);
- }
- if (iob.available() < currentInFrame.length) {
- return;
- }
- // We have a full frame. Process it.
- onFrame(iob);
+ if (iob.available() < currentInFrame.length) {
+ return;
+ }
+ // We have a full frame. Process it.
+ onFrame(iob);
- // TODO: extra checks, make sure the frame is correct and
- // it consumed all data.
- currentInFrame = null;
+ // TODO: extra checks, make sure the frame is correct and
+ // it consumed all data.
+ currentInFrame = null;
+ }
}
}
ch.setHttpService(this.httpConnector.defaultService);
}
- synchronized (this) {
+ synchronized (channels) {
channels.put(ch.channelId, ch);
}
} else if (currentInFrame.type == SpdyConnection.Frame.TYPE_SYN_REPLY) {
int chId = SpdyConnection.readInt(iob);
HttpChannel ch;
- synchronized (this) {
+ synchronized (channels) {
ch = channels.get(chId);
if (ch == null) {
abort("Channel not found");
inDataFrames.incrementAndGet();
// data frame - part of an existing stream
HttpChannel ch;
- synchronized (this) {
+ synchronized (channels) {
ch = channels.get(currentInFrame.streamId);
}
if (ch == null) {
*/
private void abort(String msg) throws IOException {
streamErrors.incrementAndGet();
- for (HttpChannel ch : channels.values()) {
- ch.abort(msg);
+ synchronized(channels) {
+ for (HttpChannel ch : channels.values()) {
+ ch.abort(msg);
+ }
}
close();
}
if (headerCompression) {
// 0x800 headers seems a bit too much - assume compressed.
// I wish this was a flag...
- headerCompressBuffer.recycle();
+ headerDeCompressBuffer.recycle();
// stream id ( 4 ) + unused ( 2 )
// nvCount is compressed in impl - spec is different
- headCompressIn.decompress(iob, headerCompressBuffer,
+ headCompressIn.decompress(iob, headerDeCompressBuffer,
currentInFrame.length - 6);
- headerCompressBuffer.copyAll(headRecvBuf);
- headerCompressBuffer.recycle();
+ headerDeCompressBuffer.copyAll(headRecvBuf);
+ headerDeCompressBuffer.recycle();
nvCount = readShort(headRecvBuf);
} else {
nvCount = readShort(iob);
http.setConnection(this);
- synchronized (this) {
+ synchronized (channels) {
channels.put(http.channelId, http);
}
public synchronized Collection<HttpChannel> getActives() {
- return channels.values();
+ synchronized(channels) {
+ return channels.values();
+ }
}
@Override
@Override
protected void endSendReceive(HttpChannel http) throws IOException {
- synchronized (this) {
+ synchronized (channels) {
HttpChannel doneHttp = channels.remove(http.channelId);
if (doneHttp != http) {
log.severe("Error removing " + doneHttp + " " + http);
}
secure = httpCh.getRequest().isSecure();
if (secure) {
- SslChannel ch1 = new SslChannel();
- ch1.setSslContext(httpConnector.sslConnector.getSSLContext());
+ if (httpConnector.debugHttp) {
+ IOChannel ch1 = new DumpChannel("NET-IN");
+ net.addFilterAfter(ch1);
+ net = ch1;
+ }
+ String[] hostPort = httpCh.getTarget().split(":");
+
+ SslChannel ch1 = httpConnector.sslConnector.channel(
+ hostPort[0], Integer.parseInt(hostPort[1]));
ch1.setSink(net);
net.addFilterAfter(ch1);
net = ch1;
}
any = true;
out("IN", first, false);
- in.queue(first);
+ if (!in.isAppendClosed()) {
+ in.queue(first);
+ }
}
}
public int write(ByteBuffer bb) throws IOException {
int len = bb.remaining();
+ int pos = bb.position();
+ if (len == 0) {
+ return 0;
+ }
append(bb);
- bb.position(bb.position() + len);
+ bb.position(pos + len);
return len;
}
* @author Costin Manolache
*/
public abstract class IOChannel implements ByteChannel, IOConnector.DataReceivedCallback,
- IOConnector.DataFlushedCallback { //, IOConnector.ClosedCallback {
+ IOConnector.DataFlushedCallback {
protected IOChannel net;
protected IOChannel app;
// TODO: update, etc
public long ts;
+ protected Throwable lastException;
+
public void setConnectedCallback(IOConnector.ConnectedCallback connectedCallback) {
this.connectedCallback = connectedCallback;
}
}
}
}
-
+
+ /**
+ * Return last IO exception.
+ *
+ * The channel is async, exceptions can happen at any time.
+ * The normal callback will be called ( connected, received ), it
+ * should check if the channel is closed and the exception.
+ */
+ public Throwable lastException() {
+ return lastException;
+ }
+
public void close() throws IOException {
shutdownOutput();
// Should it read the buffers ?
public void handleReceived(IOChannel ch) throws IOException;
}
+ /**
+ * Callback for accept and connect.
+ *
+ * Will also be called if an error happens while connecting, in
+ * which case the connection will be closed.
+ */
public static interface ConnectedCallback {
public void handleConnected(IOChannel ch) throws IOException;
}
return timer;
}
+ public IOConnector getNet() {
+ return null;
+ }
+
public abstract void acceptor(IOConnector.ConnectedCallback sc,
CharSequence port, Object extra)
throws IOException;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
-import java.util.BitSet;
import java.util.HashMap;
import java.util.Map;
}
}
+ // TODO: use it for utf-8
+ public static int char2utf8(byte[] ba, int off, char c, char c1) {
+ int i = 0;
+ if (c < 0x80) {
+ ba[off++] = (byte) (c & 0xFF);
+ return 1;
+ } else if (c < 0x800)
+ {
+ ba[off++] = (byte) (0xC0 | c >> 6);
+ ba[off++] = (byte) (0x80 | c & 0x3F);
+ return 2;
+ }
+ else if (c < 0x10000)
+ {
+ ba[off++] = (byte) ((0xE0 | c >> 12));
+ ba[off++] = (byte) ((0x80 | c >> 6 & 0x3F));
+ ba[off++] = (byte) ((0x80 | c & 0x3F));
+ return 3;
+ }
+ else if (c < 0x200000)
+ {
+ ba[off++] = (byte) ((0xF0 | c >> 18));
+ ba[off++] = (byte) ((0x80 | c >> 12 & 0x3F));
+ ba[off++] = (byte) ((0x80 | c >> 6 & 0x3F));
+ ba[off++] = (byte) ((0x80 | c & 0x3F));
+ return 4;
+ }
+
+
+ return i;
+ }
+
+
+
/**
* Just send the chars to the byte[], without flushing down.
*
import java.nio.ByteBuffer;
import java.nio.channels.ByteChannel;
import java.nio.channels.Channel;
-import java.nio.channels.SelectionKey;
/**
}
readInterest(ch, true);
-
- if (ch.callback != null) {
- ch.callback.handleConnected(ch);
- }
} catch (Throwable t) {
- log.warning("Error in connect, closing " + t);
close(ch, t);
- try {
- if (ch.callback != null) {
- ch.callback.handleConnected(ch);
- }
- } catch(Throwable t1) {
- log.warning("Double error in connect, callback broken too");
- t1.printStackTrace();
+ }
+ try {
+ if (ch.callback != null) {
+ ch.callback.handleConnected(ch);
}
-
+ } catch(Throwable t1) {
+ log.log(Level.WARNING, "Error in connect callback", t1);
}
+
}
private void handleAccept(NioChannel ch, SelectionKey sk)
*/
public int close(NioChannel selectorData, Throwable exception) throws IOException {
synchronized (closeInterest) {
+ if (exception != null) {
+ selectorData.lastException = exception;
+ }
+ selectorData.readInterest = false;
if (isSelectorThread()) {
closeIOThread(selectorData, true);
return 0;
}
- if (exception != null) {
- selectorData.lastException = exception;
- }
if (!selectorData.inClosed) {
closeInterest.add(selectorData);
}
- selectorData.readInterest = false;
}
selector.wakeup();
return 0;
@Override
public void handleClosed(NioChannel ch) throws IOException {
+ lastException = ch.lastException;
closed(); // our callback.
}
import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
+import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import javax.net.ssl.SSLEngineResult.Status;
IOBuffer in = new IOBuffer(this);
IOBuffer out = new IOBuffer(this);
+
+ long handshakeTimeout = 10000;
+ // Used for session reuse
+ String host;
+ int port;
public SslChannel() {
-
}
ByteBuffer myAppOutData;
private boolean closeHandshake = false;
- private void initSsl() throws GeneralSecurityException {
+ /**
+ * Setting the host/port enables clients to reuse SSL session -
+ * less traffic and encryption overhead at startup, assuming the
+ * server caches the session ( i.e. single server or distributed cache ).
+ *
+ * SSL ticket extension is another possibility.
+ */
+ public SslChannel setTarget(String host, int port) {
+ this.host = host;
+ this.port = port;
+ return this;
+ }
+
+ private synchronized void initSsl() throws GeneralSecurityException {
if (sslEngine != null) {
return;
}
if (client) {
- sslEngine = sslCtx.createSSLEngine();
+ if (port > 0) {
+ sslEngine = sslCtx.createSSLEngine(host, port);
+ } else {
+ sslEngine = sslCtx.createSSLEngine();
+ }
sslEngine.setUseClientMode(client);
} else {
sslEngine = sslCtx.createSSLEngine();
@Override
- public void setSink(IOChannel net) throws IOException {
+ public synchronized void setSink(IOChannel net) throws IOException {
try {
initSsl();
super.setSink(net);
log.log(Level.SEVERE, "Error initializing ", e);
}
}
-
+
@Override
public IOBuffer getIn() {
return in;
*/
public int processInput(IOBuffer netIn, IOBuffer appIn) throws IOException {
if (log.isLoggable(Level.FINEST)) {
- log.finest("JSSE: processInput " + handshakeInProgress + " " + netIn.getBufferCount());
+ log.info("JSSE: processInput " + handshakeInProgress + " " + netIn.getBufferCount());
}
if (!handshakeDone && !handshakeInProgress) {
handshakeInProgress = true;
private synchronized int processRealInput(IOBuffer netIn, IOBuffer appIn) throws IOException {
int rd = 0;
boolean needsMore = true;
+ boolean notEnough = false;
while (needsMore) {
if (netIn.isClosedAndEmpty()) {
return -1;
}
myNetInData.compact();
- int rdNow = netIn.read(myNetInData);
- myNetInData.flip();
- if (rdNow == 0) {
+ int rdNow;
+ try {
+ rdNow = netIn.read(myNetInData);
+ } finally {
+ myNetInData.flip();
+ }
+ if (rdNow == 0 && (myNetInData.remaining() == 0 ||
+ notEnough)) {
return rd;
}
if (rdNow == -1) {
return rd;
}
+ notEnough = true; // next read of 0
while (myNetInData.remaining() > 0) {
myAppInData.compact();
- unwrapR = sslEngine.unwrap(myNetInData, myAppInData);
- myAppInData.flip();
+ try {
+ unwrapR = sslEngine.unwrap(myNetInData, myAppInData);
+ } catch (SSLException ex) {
+ log.warning("Read error: " + ex);
+ close();
+ return -1;
+ } finally {
+ myAppInData.flip();
+ }
if (myAppInData.remaining() > 0) {
in.write(myAppInData); // all will be written
}
}
public void close() throws IOException {
+ if (net.getOut().isAppendClosed()) {
+ return;
+ }
sslEngine.closeOutbound(); // mark as closed
- myNetOutData.compact();
- SSLEngineResult wrap = sslEngine.wrap(EMPTY,
- myNetOutData);
- myNetOutData.flip();
- if (wrap.getStatus() != Status.CLOSED) {
- System.err.println("Unexpected status " + wrap);
+ synchronized(myNetOutData) {
+ myNetOutData.compact();
+
+ SSLEngineResult wrap;
+ try {
+ wrap = sslEngine.wrap(EMPTY, myNetOutData);
+ if (wrap.getStatus() != Status.CLOSED) {
+ log.warning("Unexpected close status " + wrap);
+ }
+ } catch (Throwable t ) {
+ log.info("Error wrapping " + myNetOutData);
+ } finally {
+ myNetOutData.flip();
+ }
+ if (myNetOutData.remaining() > 0) {
+ net.getOut().write(myNetOutData);
+ }
}
- net.getOut().write(myNetOutData);
// TODO: timer to close socket if we don't get
// clean close handshake
+ super.close();
}
- private synchronized void startRealSending() throws IOException {
- while (true) {
-
- myAppOutData.compact();
- int rd = out.read(myAppOutData);
- myAppOutData.flip();
- if (rd == 0) {
- break;
- }
- if (rd < 0) {
- close();
- break;
- }
+ private Object sendLock = new Object();
- myNetOutData.compact();
- SSLEngineResult wrap = sslEngine.wrap(myAppOutData,
- myNetOutData);
- myNetOutData.flip();
- net.getOut().write(myNetOutData);
-
- if (wrap != null) {
- switch (wrap.getStatus()) {
- case BUFFER_UNDERFLOW: {
+ private void startRealSending() throws IOException {
+ // Only one thread at a time
+ synchronized (sendLock) {
+ while (true) {
+
+ myAppOutData.compact();
+ int rd;
+ try {
+ rd = out.read(myAppOutData);
+ } finally {
+ myAppOutData.flip();
+ }
+ if (rd == 0) {
break;
}
- case OK: {
+ if (rd < 0) {
+ close();
break;
}
- case BUFFER_OVERFLOW: {
- throw new IOException("Overflow");
+
+ SSLEngineResult wrap;
+ synchronized(myNetOutData) {
+ myNetOutData.compact();
+ try {
+ wrap = sslEngine.wrap(myAppOutData,
+ myNetOutData);
+ } finally {
+ myNetOutData.flip();
+ }
+ net.getOut().write(myNetOutData);
}
+ if (wrap != null) {
+ switch (wrap.getStatus()) {
+ case BUFFER_UNDERFLOW: {
+ break;
+ }
+ case OK: {
+ break;
+ }
+ case BUFFER_OVERFLOW: {
+ throw new IOException("Overflow");
+ }
+ }
}
}
}
// We'll need to unregister and register again from the selector.
private void handleHandshking() {
if (log.isLoggable(Level.FINEST)) {
- log.finest("Starting handshake");
+ log.info("Starting handshake");
}
handshakeInProgress = true;
- new Thread(this).start();
+ ((SslConnector) connector).handshakeExecutor.execute(this);
}
private void endHandshake() throws IOException {
if (log.isLoggable(Level.FINEST)) {
- log.finest("Handshake done");
+ log.info("Handshake done " + net.getIn().available());
}
handshakeDone = true;
handshakeInProgress = false;
flushing = false;
startSending();
}
+ if (myNetInData.remaining() > 0 || net.getIn().available() > 0) {
+ // Last SSL packet also includes data.
+ handleReceived(net);
+ }
}
/**
long t0 = System.currentTimeMillis();
- while (hstatus != HandshakeStatus.FINISHED) {
+ while (hstatus != HandshakeStatus.NOT_HANDSHAKING
+ && hstatus != HandshakeStatus.FINISHED
+ && !net.getIn().isAppendClosed()) {
+ if (System.currentTimeMillis() - t0 > handshakeTimeout) {
+ throw new TimeoutException();
+ }
if (wrap != null && wrap.getStatus() == Status.CLOSED) {
break;
}
if (log.isLoggable(Level.FINEST)) {
- log.finest("-->doHandshake() loop: status = " + hstatus + " " +
+ log.info("-->doHandshake() loop: status = " + hstatus + " " +
sslEngine.getHandshakeStatus());
}
if (hstatus == HandshakeStatus.NEED_WRAP) {
// || initial - for client
initial = false;
- myNetOutData.compact();
-
- wrap = sslEngine.wrap(myAppOutData, myNetOutData);
- myNetOutData.flip();
-
- hstatus = wrap.getHandshakeStatus();
+ synchronized(myNetOutData) {
+ myNetOutData.compact();
- net.getOut().write(myNetOutData);
+ try {
+ wrap = sslEngine.wrap(myAppOutData, myNetOutData);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ close();
+ return;
+ } finally {
+ myNetOutData.flip();
+ }
+
+ hstatus = wrap.getHandshakeStatus();
+ if (myNetOutData.remaining() > 0) {
+ net.getOut().write(myNetOutData);
+ }
+ }
net.startSending();
|| wrap.getStatus() == Status.BUFFER_UNDERFLOW
|| (hstatus == HandshakeStatus.NEED_UNWRAP && myNetInData.remaining() == 0)) {
myNetInData.compact();
- int rd = net.getIn().read(myNetInData);
- myNetInData.flip();
+ // non-blocking
+ int rd;
+ try {
+ rd = net.getIn().read(myNetInData);
+ } finally {
+ myNetInData.flip();
+ }
if (rd == 0) {
- net.getIn().waitData(10000);
- continue;
+ net.getIn().waitData(handshakeTimeout -
+ (System.currentTimeMillis() - t0));
+ rd = net.getIn().read(myNetInData);
}
if (rd < 0) {
// in closed
}
}
if (log.isLoggable(Level.FINEST)) {
- log.finest("Unwrap chunk done " + hstatus + " " + wrap
+ log.info("Unwrap chunk done " + hstatus + " " + wrap
+ " " + sslEngine.getHandshakeStatus());
}
long t1task = System.currentTimeMillis();
hstatus = sslEngine.getHandshakeStatus();
if (log.isLoggable(Level.FINEST)) {
- log.finest("Tasks done in " + (t1task - t0task) + " new status " +
+ log.info("Tasks done in " + (t1task - t0task) + " new status " +
hstatus);
}
try {
close();
net.close();
+ sendHandleReceivedCallback();
} catch (IOException ex) {
log.log(Level.SEVERE, "Error closing", ex);
}
this.sslCtx = sslCtx;
return this;
}
+
+ public SslChannel setSslConnector(SslConnector con) {
+ this.connector = con;
+ return this;
+ }
}
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.PrivateKey;
-import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.RSAKeyGenParameterSpec;
-import java.security.spec.X509EncodedKeySpec;
import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
public class SslConnector extends IOConnector {
+ /**
+ * TODO: option to require validation.
+ * TODO: remember cert signature. This is needed to support self-signed
+ * certs, like those used by the test.
+ *
+ */
public static class BasicTrustManager implements X509TrustManager {
private X509Certificate[] chain;
public static TrustManager[] trustAllCerts = new TrustManager[] {
new BasicTrustManager() };
- static final boolean debug = false;
- static {
- if (debug) {
- System.setProperty("javax.net.debug", "ssl");
- }
- }
+ static final boolean debug = false;
+
IOConnector net;
private KeyManager[] keyManager;
SSLContext sslCtx;
boolean server;
private TrustManager[] trustManagers;
- Executor handshakeExecutor;
+ public AtomicInteger handshakeCount = new AtomicInteger();
+ public AtomicInteger handshakeOk = new AtomicInteger();
+ public AtomicInteger handshakeErr = new AtomicInteger();
+ public AtomicInteger handshakeTime = new AtomicInteger();
+
+ Executor handshakeExecutor = Executors.newCachedThreadPool();
static int id = 0;
public SslConnector() {
try {
sslCtx = SSLContext.getInstance("TLS");
if (trustManagers == null) {
- trustManagers =
+ trustManagers =
new TrustManager[] {new BasicTrustManager()};
}
return net;
}
+ public SslChannel channel(String host, int port) {
+ return new SslChannel()
+ .setTarget(host, port)
+ .setSslContext(getSSLContext())
+ .setSslConnector(this);
+ }
+
+ public SslChannel serverChannel() {
+ return new SslChannel()
+ .setSslContext(getSSLContext())
+ .setSslConnector(this).withServer();
+ }
@Override
public void acceptor(final ConnectedCallback sc, CharSequence port, Object extra)
first = dch;
}
- IOChannel sslch = new SslChannel()
- .setSslContext(sslCtx)
- .withServer();
+ IOChannel sslch = serverChannel();
sslch.setSink(first);
first.addFilterAfter(sslch);
}
@Override
- public void connect(String host, int port, final ConnectedCallback sc)
+ public void connect(final String host, final int port, final ConnectedCallback sc)
throws IOException {
getNet().connect(host, port, new ConnectedCallback() {
first = dch;
}
- IOChannel sslch = new SslChannel()
- .setSslContext(sslCtx);
+ IOChannel sslch = channel(host, port);
sslch.setSink(first);
first.addFilterAfter(sslch);
public static void fixUrlConnection() {
try {
- SSLContext sc = SSLContext.getInstance("SSL");
+ SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, SslConnector.trustAllCerts, null);
javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(
sc.getSocketFactory());
public class ServletApi30 extends ServletApi {
public ServletContextImpl newContext() {
- return new ServletContextImpl() {
- protected void initEngineDefaults() throws ServletException {
- super.initEngineDefaults();
- setAttribute(InstanceManager.class.getName(),
- new LiteInstanceManager(getObjectManager()));
- }
- @Override
- public Dynamic addFilter(String filterName, String className) {
- FilterConfigImpl fc = new FilterConfigImpl(this);
- fc.setData(filterName, null, new HashMap());
- fc.setData(filterName, className, new HashMap());
- filters.put(filterName, fc);
- return new DynamicFilterRegistration(fc);
- }
-
- @Override
- public Dynamic addFilter(String filterName, Filter filter) {
- FilterConfigImpl fc = new FilterConfigImpl(this);
- fc.setData(filterName, null, new HashMap());
- fc.setFilter(filter);
- filters.put(filterName, fc);
- return new DynamicFilterRegistration(fc);
- }
-
- @Override
- public Dynamic addFilter(String filterName,
- Class<? extends Filter> filterClass) {
- FilterConfigImpl fc = new FilterConfigImpl(this);
- fc.setData(filterName, null, new HashMap());
- fc.setFilterClass(filterClass);
- filters.put(filterName, fc);
- return new DynamicFilterRegistration(fc);
- }
-
- @Override
- public javax.servlet.ServletRegistration.Dynamic addServlet(
- String servletName, String className) {
- return null;
- }
-
- @Override
- public javax.servlet.ServletRegistration.Dynamic addServlet(
- String servletName, Servlet servlet) {
- return null;
- }
-
- @Override
- public javax.servlet.ServletRegistration.Dynamic addServlet(
- String servletName, Class<? extends Servlet> servletClass) {
- return null;
- }
-
-
- @Override
- public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
- return null;
- }
-
-
- @Override
- public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
- return null;
- }
-
- @Override
- public FilterRegistration getFilterRegistration(String filterName) {
- return null;
- }
-
- @Override
- public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
- return null;
- }
-
- @Override
- public JspConfigDescriptor getJspConfigDescriptor() {
- return null;
- }
-
- @Override
- public ServletRegistration getServletRegistration(String servletName) {
- return null;
- }
-
- @Override
- public Map<String, ? extends ServletRegistration> getServletRegistrations() {
- return null;
- }
-
- @Override
- public SessionCookieConfig getSessionCookieConfig() {
- return null;
- }
-
- @Override
- public void setSessionTrackingModes(
- EnumSet<SessionTrackingMode> sessionTrackingModes)
- throws IllegalStateException, IllegalArgumentException {
- }
-
- public int getMajorVersion() {
- return 3;
- }
-
- public int getMinorVersion() {
- return 0;
- }
-
- };
+ return new Servlet30ContextImpl();
}
public ServletRequestImpl newRequest(HttpRequest req) {
return new ServletRequestImpl(req) {
- @Override
public Part getPart(String name) {
return null;
}
- @Override
public Collection<Part> getParts() throws IOException,
ServletException {
return null;
}
- @Override
public AsyncContext getAsyncContext() {
return null;
}
- @Override
public DispatcherType getDispatcherType() {
return null;
}
- @Override
public AsyncContext startAsync() {
return null;
}
- @Override
public AsyncContext startAsync(ServletRequest servletRequest,
ServletResponse servletResponse) {
return null;
};
}
+ public final class Servlet30ContextImpl extends ServletContextImpl {
+ protected void initEngineDefaults() throws ServletException {
+ super.initEngineDefaults();
+ setAttribute(InstanceManager.class.getName(),
+ new LiteInstanceManager(getObjectManager()));
+ }
+
+ public Dynamic addFilter(String filterName, String className) {
+ FilterConfigImpl fc = new FilterConfigImpl(this);
+ fc.setData(filterName, null, new HashMap());
+ fc.setData(filterName, className, new HashMap());
+ filters.put(filterName, fc);
+ return new DynamicFilterRegistration(fc);
+ }
+
+ public Dynamic addFilter(String filterName, Filter filter) {
+ FilterConfigImpl fc = new FilterConfigImpl(this);
+ fc.setData(filterName, null, new HashMap());
+ fc.setFilter(filter);
+ filters.put(filterName, fc);
+ return new DynamicFilterRegistration(fc);
+ }
+
+ public Dynamic addFilter(String filterName,
+ Class<? extends Filter> filterClass) {
+ FilterConfigImpl fc = new FilterConfigImpl(this);
+ fc.setData(filterName, null, new HashMap());
+ fc.setFilterClass(filterClass);
+ filters.put(filterName, fc);
+ return new DynamicFilterRegistration(fc);
+ }
+
+ public javax.servlet.ServletRegistration.Dynamic addServlet(
+ String servletName, String className) {
+ return null;
+ }
+
+ public javax.servlet.ServletRegistration.Dynamic addServlet(
+ String servletName, Servlet servlet) {
+ return null;
+ }
+
+ public javax.servlet.ServletRegistration.Dynamic addServlet(
+ String servletName, Class<? extends Servlet> servletClass) {
+ return null;
+ }
+
+ public Set<SessionTrackingMode> getDefaultSessionTrackingModes() {
+ return null;
+ }
+
+ public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() {
+ return null;
+ }
+
+ public FilterRegistration getFilterRegistration(String filterName) {
+ return null;
+ }
+
+ public Map<String, ? extends FilterRegistration> getFilterRegistrations() {
+ return null;
+ }
+
+ public JspConfigDescriptor getJspConfigDescriptor() {
+ return null;
+ }
+
+ public ServletRegistration getServletRegistration(String servletName) {
+ return null;
+ }
+
+ public Map<String, ? extends ServletRegistration> getServletRegistrations() {
+ return null;
+ }
+
+ public SessionCookieConfig getSessionCookieConfig() {
+ return null;
+ }
+
+ public int getMajorVersion() {
+ return 3;
+ }
+
+ public int getMinorVersion() {
+ return 0;
+ }
+
+ public void setSessionTrackingModes(
+ Set<SessionTrackingMode> sessionTrackingModes)
+ throws IllegalStateException, IllegalArgumentException {
+ }
+ }
+
private final class LiteInstanceManager implements InstanceManager {
private ObjectManager om;
import org.apache.tomcat.integration.ObjectManager;
import org.apache.tomcat.lite.http.BaseMapper;
import org.apache.tomcat.lite.io.FileConnectorJavaIo;
+import org.apache.tomcat.lite.util.MimeMap;
+import org.apache.tomcat.lite.util.UrlUtils;
import org.apache.tomcat.servlets.config.ConfigLoader;
import org.apache.tomcat.servlets.config.ServletContextConfig;
import org.apache.tomcat.servlets.config.ServletContextConfig.FilterData;
import org.apache.tomcat.servlets.config.ServletContextConfig.ServletData;
import org.apache.tomcat.servlets.session.UserSessionManager;
import org.apache.tomcat.servlets.util.Enumerator;
-import org.apache.tomcat.servlets.util.MimeMap;
import org.apache.tomcat.servlets.util.RequestUtil;
-import org.apache.tomcat.servlets.util.UrlUtils;
/**
import org.apache.tomcat.lite.io.BBuffer;
import org.apache.tomcat.lite.io.CBuffer;
import org.apache.tomcat.lite.io.FastHttpDateFormat;
+import org.apache.tomcat.lite.util.LocaleParser;
import org.apache.tomcat.servlets.session.UserSessionManager;
import org.apache.tomcat.servlets.util.Enumerator;
-import org.apache.tomcat.servlets.util.LocaleParser;
import org.apache.tomcat.servlets.util.RequestUtil;
public static final String WORK_DIR_ATTR =
"javax.servlet.context.tempdir";
- protected static final TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT");
-
/**
* The default Locale if none are specified.
protected Cookie[] cookies = null;
- /**
- * The set of SimpleDateFormat formats to use in getDateHeader().
- *
- * Notice that because SimpleDateFormat is not thread-safe, we can't
- * declare formats[] as a static variable.
- */
- protected SimpleDateFormat formats[] = null;
/**
* cannot be converted to a date
*/
public long getDateHeader(String name) {
-
- String value = getHeader(name);
- if (value == null)
- return (-1L);
- if (formats == null) {
- formats = new SimpleDateFormat[] {
- new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
- new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
- new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
- };
- formats[0].setTimeZone(GMT_ZONE);
- formats[1].setTimeZone(GMT_ZONE);
- formats[2].setTimeZone(GMT_ZONE);
- }
-
- // Attempt to convert the date header in a variety of formats
- long result = FastHttpDateFormat.parseDate(value, formats);
- if (result != (-1L)) {
- return result;
- }
- throw new IllegalArgumentException(value);
-
+ return httpRequest.getDateHeader(name);
}
/**
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
+import org.apache.tomcat.lite.util.URLEncoder;
import org.apache.tomcat.servlets.util.RequestUtil;
/**
}
public void setURLPattern(String urlPattern) {
- this.urlPattern = RequestUtil.URLDecode(urlPattern);
+ this.urlPattern = URLEncoder.URLDecode(urlPattern);
}
/**
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.tomcat.lite.util;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * This class provides encode/decode for RFC 2045 Base64 as
+ * defined by RFC 2045, N. Freed and N. Borenstein.
+ * RFC 2045: Multipurpose Internet Mail Extensions (MIME)
+ * Part One: Format of Internet Message Bodies. Reference
+ * 1996 Available at: http://www.ietf.org/rfc/rfc2045.txt
+ * This class is used by XML Schema binary format validation
+ *
+ * @author Jeffrey Rodriguez
+ * @version $Revision: 467222 $ $Date: 2006-10-23 20:17:11 -0700 (Mon, 23 Oct 2006) $
+ */
+
+public final class Base64 {
+
+
+ private static Logger log=
+ Logger.getLogger( Base64.class.getName() );
+
+ static private final int BASELENGTH = 255;
+ static private final int LOOKUPLENGTH = 63;
+ static private final int TWENTYFOURBITGROUP = 24;
+ static private final int EIGHTBIT = 8;
+ static private final int SIXTEENBIT = 16;
+ static private final int SIXBIT = 6;
+ static private final int FOURBYTE = 4;
+
+
+ static private final byte PAD = ( byte ) '=';
+ static private byte [] base64Alphabet = new byte[BASELENGTH];
+ static private byte [] lookUpBase64Alphabet = new byte[LOOKUPLENGTH + 1];
+
+ static {
+
+ for (int i = 0; i<BASELENGTH; i++ ) {
+ base64Alphabet[i] = -1;
+ }
+ for ( int i = 'Z'; i >= 'A'; i-- ) {
+ base64Alphabet[i] = (byte) (i-'A');
+ }
+ for ( int i = 'z'; i>= 'a'; i--) {
+ base64Alphabet[i] = (byte) ( i-'a' + 26);
+ }
+
+ for ( int i = '9'; i >= '0'; i--) {
+ base64Alphabet[i] = (byte) (i-'0' + 52);
+ }
+
+ base64Alphabet['+'] = 62;
+ base64Alphabet['/'] = 63;
+ String lookupString =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +
+ "0123456789+/";
+
+ for (int i = 0; i < lookupString.length(); i++ ) {
+ lookUpBase64Alphabet[i] = (byte) lookupString.charAt(i);
+ }
+ }
+
+
+ static boolean isBase64( byte octect ) {
+ //shall we ignore white space? JEFF??
+ return(octect == PAD || base64Alphabet[octect] != -1 );
+ }
+
+
+ static boolean isArrayByteBase64( byte[] arrayOctect ) {
+ int length = arrayOctect.length;
+ if ( length == 0 )
+ return false;
+ for ( int i=0; i < length; i++ ) {
+ if ( Base64.isBase64( arrayOctect[i] ) == false)
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Encodes hex octects into Base64
+ *
+ * @param binaryData Array containing binaryData
+ * @return Encoded Base64 array
+ */
+ public static byte[] encode( byte[] binaryData ) {
+ int lengthDataBits = binaryData.length*EIGHTBIT;
+ int fewerThan24bits = lengthDataBits%TWENTYFOURBITGROUP;
+ int numberTriplets = lengthDataBits/TWENTYFOURBITGROUP;
+ byte encodedData[] = null;
+
+
+ if ( fewerThan24bits != 0 ) //data not divisible by 24 bit
+ encodedData = new byte[ (numberTriplets + 1 )*4 ];
+ else // 16 or 8 bit
+ encodedData = new byte[ numberTriplets*4 ];
+
+ int k=0, l=0, b1=0,b2=0,b3=0;
+
+ int encodedIndex = 0;
+ int dataIndex = 0;
+ int i = 0;
+ for ( i = 0; i<numberTriplets; i++ ) {
+
+ dataIndex = i*3;
+ b1 = binaryData[dataIndex] & 0xFF;
+ b2 = binaryData[dataIndex + 1] & 0xFF;
+ b3 = binaryData[dataIndex + 2] & 0xFF;
+
+ l = (byte)(b2 & 0x0f);
+ k = (byte)(b1 & 0x03);
+
+ encodedIndex = i*4;
+ encodedData[encodedIndex] = lookUpBase64Alphabet[ b1 >>2 ];
+ encodedData[encodedIndex+1] = lookUpBase64Alphabet[(b2 >>4 ) |
+( k<<4 )];
+ encodedData[encodedIndex+2] = lookUpBase64Alphabet[ (l <<2 ) |
+( b3>>6)];
+ encodedData[encodedIndex+3] = lookUpBase64Alphabet[ b3 & 0x3f ];
+ }
+
+ // form integral number of 6-bit groups
+ dataIndex = i*3;
+ encodedIndex = i*4;
+ if (fewerThan24bits == EIGHTBIT ) {
+ b1 = binaryData[dataIndex];
+ k = (byte) ( b1 &0x03 );
+ encodedData[encodedIndex] = lookUpBase64Alphabet[ b1 >>2 ];
+ encodedData[encodedIndex + 1] = lookUpBase64Alphabet[ k<<4 ];
+ encodedData[encodedIndex + 2] = PAD;
+ encodedData[encodedIndex + 3] = PAD;
+ } else if ( fewerThan24bits == SIXTEENBIT ) {
+
+ b1 = binaryData[dataIndex];
+ b2 = binaryData[dataIndex +1 ];
+ l = ( byte ) ( b2 &0x0f );
+ k = ( byte ) ( b1 &0x03 );
+ encodedData[encodedIndex] = lookUpBase64Alphabet[ b1 >>2 ];
+ encodedData[encodedIndex + 1] = lookUpBase64Alphabet[ (b2 >>4 )
+| ( k<<4 )];
+ encodedData[encodedIndex + 2] = lookUpBase64Alphabet[ l<<2 ];
+ encodedData[encodedIndex + 3] = PAD;
+ }
+ return encodedData;
+ }
+
+ public byte[] decode(String enc) {
+ return decode(enc.getBytes());
+ }
+
+ /**
+ * Decodes Base64 data into octects
+ *
+ * @param base64Data Byte array containing Base64 data
+ * @return Array containind decoded data.
+ */
+ public static byte[] decode( byte[] base64Data ) {
+ int numberQuadruple = base64Data.length/FOURBYTE;
+ byte decodedData[] = null;
+ byte b1=0,b2=0,b3=0, b4=0, marker0=0, marker1=0;
+
+ // Throw away anything not in base64Data
+ // Adjust size
+
+ int encodedIndex = 0;
+ int dataIndex = 0;
+ decodedData = new byte[ numberQuadruple*3 + 1 ];
+
+ for (int i = 0; i<numberQuadruple; i++ ) {
+ dataIndex = i*4;
+ marker0 = base64Data[dataIndex +2];
+ marker1 = base64Data[dataIndex +3];
+
+ b1 = base64Alphabet[base64Data[dataIndex]];
+ b2 = base64Alphabet[base64Data[dataIndex +1]];
+
+ if ( marker0 != PAD && marker1 != PAD ) { //No PAD e.g 3cQl
+ b3 = base64Alphabet[ marker0 ];
+ b4 = base64Alphabet[ marker1 ];
+
+ decodedData[encodedIndex] = (byte)( b1 <<2 | b2>>4 ) ;
+ decodedData[encodedIndex+1] = (byte)(((b2 & 0xf)<<4 ) |(
+(b3>>2) & 0xf) );
+ decodedData[encodedIndex+2] = (byte)( b3<<6 | b4 );
+ } else if ( marker0 == PAD ) { //Two PAD e.g. 3c[Pad][Pad]
+ decodedData[encodedIndex] = (byte)( b1 <<2 | b2>>4 ) ;
+ decodedData[encodedIndex+1] = (byte)((b2 & 0xf)<<4 );
+ decodedData[encodedIndex+2] = (byte) 0;
+ } else if ( marker1 == PAD ) { //One PAD e.g. 3cQ[Pad]
+ b3 = base64Alphabet[ marker0 ];
+
+ decodedData[encodedIndex] = (byte)( b1 <<2 | b2>>4 );
+ decodedData[encodedIndex+1] = (byte)(((b2 & 0xf)<<4 ) |(
+(b3>>2) & 0xf) );
+ decodedData[encodedIndex+2] = (byte)( b3<<6);
+ }
+ encodedIndex += 3;
+ }
+ return decodedData;
+
+ }
+
+ static final int base64[]= {
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
+ 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
+ 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
+ };
+
+ public static String base64Decode( String orig ) {
+ char chars[]=orig.toCharArray();
+ StringBuffer sb=new StringBuffer();
+ int i=0;
+
+ int shift = 0; // # of excess bits stored in accum
+ int acc = 0;
+
+ for (i=0; i<chars.length; i++) {
+ int v = base64[ chars[i] & 0xFF ];
+
+ if ( v >= 64 ) {
+ if( chars[i] != '=' )
+ if (log.isLoggable(Level.INFO))
+ log.info("Wrong char in base64: " + chars[i]);
+ } else {
+ acc= ( acc << 6 ) | v;
+ shift += 6;
+ if ( shift >= 8 ) {
+ shift -= 8;
+ sb.append( (char) ((acc >> shift) & 0xff));
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+
+}
+
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.lite.util;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Utility class to generate HTTP dates.
+ *
+ * @author Remy Maucherat
+ */
+public final class FastHttpDateFormat {
+
+
+ // -------------------------------------------------------------- Variables
+
+
+ protected static final int CACHE_SIZE =
+ Integer.parseInt(System.getProperty("org.apache.tomcat.util.http.FastHttpDateFormat.CACHE_SIZE", "1000"));
+
+
+ /**
+ * HTTP date format.
+ */
+ protected static final SimpleDateFormat format =
+ new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
+
+
+ /**
+ * The set of SimpleDateFormat formats to use in getDateHeader().
+ */
+ protected static final SimpleDateFormat formats[] = {
+ new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
+ new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
+ new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
+ };
+
+
+ protected final static TimeZone gmtZone = TimeZone.getTimeZone("GMT");
+
+
+ /**
+ * GMT timezone - all HTTP dates are on GMT
+ */
+ static {
+
+ format.setTimeZone(gmtZone);
+
+ formats[0].setTimeZone(gmtZone);
+ formats[1].setTimeZone(gmtZone);
+ formats[2].setTimeZone(gmtZone);
+
+ }
+
+
+ /**
+ * Instant on which the currentDate object was generated.
+ */
+ protected static long currentDateGenerated = 0L;
+
+
+ /**
+ * Current formatted date.
+ */
+ protected static String currentDate = null;
+
+
+ /**
+ * Formatter cache.
+ */
+ protected static final ConcurrentHashMap<Long, String> formatCache =
+ new ConcurrentHashMap<Long, String>(CACHE_SIZE);
+
+
+ /**
+ * Parser cache.
+ */
+ protected static final ConcurrentHashMap<String, Long> parseCache =
+ new ConcurrentHashMap<String, Long>(CACHE_SIZE);
+
+
+ // --------------------------------------------------------- Public Methods
+
+
+ /**
+ * Get the current date in HTTP format.
+ */
+ public static final String getCurrentDate() {
+
+ long now = System.currentTimeMillis();
+ if ((now - currentDateGenerated) > 1000) {
+ synchronized (format) {
+ if ((now - currentDateGenerated) > 1000) {
+ currentDateGenerated = now;
+ currentDate = format.format(new Date(now));
+ }
+ }
+ }
+ return currentDate;
+
+ }
+
+
+ /**
+ * Get the HTTP format of the specified date.
+ */
+ public static final String formatDate
+ (long value, DateFormat threadLocalformat) {
+
+ Long longValue = new Long(value);
+ String cachedDate = formatCache.get(longValue);
+ if (cachedDate != null)
+ return cachedDate;
+
+ String newDate = null;
+ Date dateValue = new Date(value);
+ if (threadLocalformat != null) {
+ newDate = threadLocalformat.format(dateValue);
+ updateFormatCache(longValue, newDate);
+ } else {
+ synchronized (formatCache) {
+ synchronized (format) {
+ newDate = format.format(dateValue);
+ }
+ updateFormatCache(longValue, newDate);
+ }
+ }
+ return newDate;
+
+ }
+
+
+ /**
+ * Try to parse the given date as a HTTP date.
+ */
+ public static final long parseDate(String value,
+ DateFormat[] threadLocalformats) {
+
+ Long cachedDate = parseCache.get(value);
+ if (cachedDate != null)
+ return cachedDate.longValue();
+
+ Long date = null;
+ if (threadLocalformats != null) {
+ date = internalParseDate(value, threadLocalformats);
+ updateParseCache(value, date);
+ } else {
+ synchronized (parseCache) {
+ date = internalParseDate(value, formats);
+ updateParseCache(value, date);
+ }
+ }
+ if (date == null) {
+ return (-1L);
+ } else {
+ return date.longValue();
+ }
+
+ }
+
+
+ /**
+ * Parse date with given formatters.
+ */
+ private static final Long internalParseDate
+ (String value, DateFormat[] formats) {
+ Date date = null;
+ for (int i = 0; (date == null) && (i < formats.length); i++) {
+ try {
+ date = formats[i].parse(value);
+ } catch (ParseException e) {
+ ;
+ }
+ }
+ if (date == null) {
+ return null;
+ }
+ return new Long(date.getTime());
+ }
+
+
+ /**
+ * Update cache.
+ */
+ private static void updateFormatCache(Long key, String value) {
+ if (value == null) {
+ return;
+ }
+ if (formatCache.size() > CACHE_SIZE) {
+ formatCache.clear();
+ }
+ formatCache.put(key, value);
+ }
+
+
+ /**
+ * Update cache.
+ */
+ private static void updateParseCache(String key, Long value) {
+ if (value == null) {
+ return;
+ }
+ if (parseCache.size() > CACHE_SIZE) {
+ parseCache.clear();
+ }
+ parseCache.put(key, value);
+ }
+
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.tomcat.lite.util;
+
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.TreeMap;
+
+
+/**
+ * Utility class for string parsing that is higher performance than
+ * StringParser for simple delimited text cases. Parsing is performed
+ * by setting the string, and then using the <code>findXxxx()</code> and
+ * <code>skipXxxx()</code> families of methods to remember significant
+ * offsets. To retrieve the parsed substrings, call the <code>extract()</code>
+ * method with the appropriate saved offset values.
+ *
+ * @author Craig R. McClanahan
+ */
+public final class LocaleParser {
+
+ public LocaleParser() {
+ this(null);
+ }
+
+ public LocaleParser(String string) {
+ setString(string);
+ }
+
+ public TreeMap parseLocale(String value) {
+ // Store the accumulated languages that have been requested in
+ // a local collection, sorted by the quality value (so we can
+ // add Locales in descending order). The values will be ArrayLists
+ // containing the corresponding Locales to be added
+ TreeMap locales = new TreeMap();
+
+ // Preprocess the value to remove all whitespace
+ int white = value.indexOf(' ');
+ if (white < 0)
+ white = value.indexOf('\t');
+ if (white >= 0) {
+ StringBuilder sb = new StringBuilder();
+ int len = value.length();
+ for (int i = 0; i < len; i++) {
+ char ch = value.charAt(i);
+ if ((ch != ' ') && (ch != '\t'))
+ sb.append(ch);
+ }
+ value = sb.toString();
+ }
+
+ LocaleParser parser = this;
+ // Process each comma-delimited language specification
+ parser.setString(value); // ASSERT: parser is available to us
+ int length = parser.getLength();
+ while (true) {
+
+ // Extract the next comma-delimited entry
+ int start = parser.getIndex();
+ if (start >= length)
+ break;
+ int end = parser.findChar(',');
+ String entry = parser.extract(start, end).trim();
+ parser.advance(); // For the following entry
+
+ // Extract the quality factor for this entry
+ double quality = 1.0;
+ int semi = entry.indexOf(";q=");
+ if (semi >= 0) {
+ try {
+ quality = Double.parseDouble(entry.substring(semi + 3));
+ } catch (NumberFormatException e) {
+ quality = 0.0;
+ }
+ entry = entry.substring(0, semi);
+ }
+
+ // Skip entries we are not going to keep track of
+ if (quality < 0.00005)
+ continue; // Zero (or effectively zero) quality factors
+ if ("*".equals(entry))
+ continue; // FIXME - "*" entries are not handled
+
+ // Extract the language and country for this entry
+ String language = null;
+ String country = null;
+ String variant = null;
+ int dash = entry.indexOf('-');
+ if (dash < 0) {
+ language = entry;
+ country = "";
+ variant = "";
+ } else {
+ language = entry.substring(0, dash);
+ country = entry.substring(dash + 1);
+ int vDash = country.indexOf('-');
+ if (vDash > 0) {
+ String cTemp = country.substring(0, vDash);
+ variant = country.substring(vDash + 1);
+ country = cTemp;
+ } else {
+ variant = "";
+ }
+ }
+
+ // Add a new Locale to the list of Locales for this quality level
+ Locale locale = new Locale(language, country, variant);
+ Double key = new Double(-quality); // Reverse the order
+ ArrayList values = (ArrayList) locales.get(key);
+ if (values == null) {
+ values = new ArrayList();
+ locales.put(key, values);
+ }
+ values.add(locale);
+
+ }
+
+ return locales;
+ }
+
+ /**
+ * The characters of the current string, as a character array. Stored
+ * when the string is first specified to speed up access to characters
+ * being compared during parsing.
+ */
+ private char chars[] = null;
+
+
+ /**
+ * The zero-relative index of the current point at which we are
+ * positioned within the string being parsed. <strong>NOTE</strong>:
+ * the value of this index can be one larger than the index of the last
+ * character of the string (i.e. equal to the string length) if you
+ * parse off the end of the string. This value is useful for extracting
+ * substrings that include the end of the string.
+ */
+ private int index = 0;
+
+
+ /**
+ * The length of the String we are currently parsing. Stored when the
+ * string is first specified to avoid repeated recalculations.
+ */
+ private int length = 0;
+
+
+ /**
+ * The String we are currently parsing.
+ */
+ private String string = null;
+
+
+ // ------------------------------------------------------------- Properties
+
+
+ /**
+ * Return the zero-relative index of our current parsing position
+ * within the string being parsed.
+ */
+ public int getIndex() {
+
+ return (this.index);
+
+ }
+
+
+ /**
+ * Return the length of the string we are parsing.
+ */
+ public int getLength() {
+
+ return (this.length);
+
+ }
+
+
+ /**
+ * Return the String we are currently parsing.
+ */
+ public String getString() {
+
+ return (this.string);
+
+ }
+
+
+ /**
+ * Set the String we are currently parsing. The parser state is also reset
+ * to begin at the start of this string.
+ *
+ * @param string The string to be parsed.
+ */
+ public void setString(String string) {
+
+ this.string = string;
+ if (string != null) {
+ this.length = string.length();
+ chars = this.string.toCharArray();
+ } else {
+ this.length = 0;
+ chars = new char[0];
+ }
+ reset();
+
+ }
+
+
+ // --------------------------------------------------------- Public Methods
+
+
+ /**
+ * Advance the current parsing position by one, if we are not already
+ * past the end of the string.
+ */
+ public void advance() {
+
+ if (index < length)
+ index++;
+
+ }
+
+
+ /**
+ * Extract and return a substring that starts at the specified position,
+ * and extends to the end of the string being parsed. If this is not
+ * possible, a zero-length string is returned.
+ *
+ * @param start Starting index, zero relative, inclusive
+ */
+ public String extract(int start) {
+
+ if ((start < 0) || (start >= length))
+ return ("");
+ else
+ return (string.substring(start));
+
+ }
+
+
+ /**
+ * Extract and return a substring that starts at the specified position,
+ * and ends at the character before the specified position. If this is
+ * not possible, a zero-length string is returned.
+ *
+ * @param start Starting index, zero relative, inclusive
+ * @param end Ending index, zero relative, exclusive
+ */
+ public String extract(int start, int end) {
+
+ if ((start < 0) || (start >= end) || (end > length))
+ return ("");
+ else
+ return (string.substring(start, end));
+
+ }
+
+
+ /**
+ * Return the index of the next occurrence of the specified character,
+ * or the index of the character after the last position of the string
+ * if no more occurrences of this character are found. The current
+ * parsing position is updated to the returned value.
+ *
+ * @param ch Character to be found
+ */
+ public int findChar(char ch) {
+
+ while ((index < length) && (ch != chars[index]))
+ index++;
+ return (index);
+
+ }
+
+
+ /**
+ * Return the index of the next occurrence of a non-whitespace character,
+ * or the index of the character after the last position of the string
+ * if no more non-whitespace characters are found. The current
+ * parsing position is updated to the returned value.
+ */
+ public int findText() {
+
+ while ((index < length) && isWhite(chars[index]))
+ index++;
+ return (index);
+
+ }
+
+
+ /**
+ * Return the index of the next occurrence of a whitespace character,
+ * or the index of the character after the last position of the string
+ * if no more whitespace characters are found. The current parsing
+ * position is updated to the returned value.
+ */
+ public int findWhite() {
+
+ while ((index < length) && !isWhite(chars[index]))
+ index++;
+ return (index);
+
+ }
+
+
+ /**
+ * Reset the current state of the parser to the beginning of the
+ * current string being parsed.
+ */
+ public void reset() {
+
+ index = 0;
+
+ }
+
+
+ /**
+ * Advance the current parsing position while it is pointing at the
+ * specified character, or until it moves past the end of the string.
+ * Return the final value.
+ *
+ * @param ch Character to be skipped
+ */
+ public int skipChar(char ch) {
+
+ while ((index < length) && (ch == chars[index]))
+ index++;
+ return (index);
+
+ }
+
+
+ /**
+ * Advance the current parsing position while it is pointing at a
+ * non-whitespace character, or until it moves past the end of the string.
+ * Return the final value.
+ */
+ public int skipText() {
+
+ while ((index < length) && !isWhite(chars[index]))
+ index++;
+ return (index);
+
+ }
+
+
+ /**
+ * Advance the current parsing position while it is pointing at a
+ * whitespace character, or until it moves past the end of the string.
+ * Return the final value.
+ */
+ public int skipWhite() {
+
+ while ((index < length) && isWhite(chars[index]))
+ index++;
+ return (index);
+
+ }
+
+
+ // ------------------------------------------------------ Protected Methods
+
+
+ /**
+ * Is the specified character considered to be whitespace?
+ *
+ * @param ch Character to be checked
+ */
+ protected boolean isWhite(char ch) {
+
+ if ((ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n'))
+ return (true);
+ else
+ return (false);
+
+ }
+
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.lite.util;
+
+import java.net.FileNameMap;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+
+/**
+ * A mime type map that implements the java.net.FileNameMap interface.
+ *
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author Jason Hunter [jch@eng.sun.com]
+ */
+public class MimeMap implements FileNameMap {
+
+ // Defaults - all of them are "well-known" types,
+ // you can add using normal web.xml.
+
+ public static Hashtable<String,String> defaultMap =
+ new Hashtable<String,String>(101);
+ static {
+ defaultMap.put("txt", "text/plain");
+ defaultMap.put("css", "text/css");
+ defaultMap.put("html","text/html");
+ defaultMap.put("htm", "text/html");
+ defaultMap.put("gif", "image/gif");
+ defaultMap.put("jpg", "image/jpeg");
+ defaultMap.put("jpe", "image/jpeg");
+ defaultMap.put("jpeg", "image/jpeg");
+ defaultMap.put("png", "image/png");
+ defaultMap.put("java", "text/plain");
+ defaultMap.put("body", "text/html");
+ defaultMap.put("rtx", "text/richtext");
+ defaultMap.put("tsv", "text/tab-separated-values");
+ defaultMap.put("etx", "text/x-setext");
+ defaultMap.put("ps", "application/x-postscript");
+ defaultMap.put("class", "application/java");
+ defaultMap.put("csh", "application/x-csh");
+ defaultMap.put("sh", "application/x-sh");
+ defaultMap.put("tcl", "application/x-tcl");
+ defaultMap.put("tex", "application/x-tex");
+ defaultMap.put("texinfo", "application/x-texinfo");
+ defaultMap.put("texi", "application/x-texinfo");
+ defaultMap.put("t", "application/x-troff");
+ defaultMap.put("tr", "application/x-troff");
+ defaultMap.put("roff", "application/x-troff");
+ defaultMap.put("man", "application/x-troff-man");
+ defaultMap.put("me", "application/x-troff-me");
+ defaultMap.put("ms", "application/x-wais-source");
+ defaultMap.put("src", "application/x-wais-source");
+ defaultMap.put("zip", "application/zip");
+ defaultMap.put("bcpio", "application/x-bcpio");
+ defaultMap.put("cpio", "application/x-cpio");
+ defaultMap.put("gtar", "application/x-gtar");
+ defaultMap.put("shar", "application/x-shar");
+ defaultMap.put("sv4cpio", "application/x-sv4cpio");
+ defaultMap.put("sv4crc", "application/x-sv4crc");
+ defaultMap.put("tar", "application/x-tar");
+ defaultMap.put("ustar", "application/x-ustar");
+ defaultMap.put("dvi", "application/x-dvi");
+ defaultMap.put("hdf", "application/x-hdf");
+ defaultMap.put("latex", "application/x-latex");
+ defaultMap.put("bin", "application/octet-stream");
+ defaultMap.put("oda", "application/oda");
+ defaultMap.put("pdf", "application/pdf");
+ defaultMap.put("ps", "application/postscript");
+ defaultMap.put("eps", "application/postscript");
+ defaultMap.put("ai", "application/postscript");
+ defaultMap.put("rtf", "application/rtf");
+ defaultMap.put("nc", "application/x-netcdf");
+ defaultMap.put("cdf", "application/x-netcdf");
+ defaultMap.put("cer", "application/x-x509-ca-cert");
+ defaultMap.put("exe", "application/octet-stream");
+ defaultMap.put("gz", "application/x-gzip");
+ defaultMap.put("Z", "application/x-compress");
+ defaultMap.put("z", "application/x-compress");
+ defaultMap.put("hqx", "application/mac-binhex40");
+ defaultMap.put("mif", "application/x-mif");
+ defaultMap.put("ief", "image/ief");
+ defaultMap.put("tiff", "image/tiff");
+ defaultMap.put("tif", "image/tiff");
+ defaultMap.put("ras", "image/x-cmu-raster");
+ defaultMap.put("pnm", "image/x-portable-anymap");
+ defaultMap.put("pbm", "image/x-portable-bitmap");
+ defaultMap.put("pgm", "image/x-portable-graymap");
+ defaultMap.put("ppm", "image/x-portable-pixmap");
+ defaultMap.put("rgb", "image/x-rgb");
+ defaultMap.put("xbm", "image/x-xbitmap");
+ defaultMap.put("xpm", "image/x-xpixmap");
+ defaultMap.put("xwd", "image/x-xwindowdump");
+ defaultMap.put("au", "audio/basic");
+ defaultMap.put("snd", "audio/basic");
+ defaultMap.put("aif", "audio/x-aiff");
+ defaultMap.put("aiff", "audio/x-aiff");
+ defaultMap.put("aifc", "audio/x-aiff");
+ defaultMap.put("wav", "audio/x-wav");
+ defaultMap.put("mpeg", "video/mpeg");
+ defaultMap.put("mpg", "video/mpeg");
+ defaultMap.put("mpe", "video/mpeg");
+ defaultMap.put("qt", "video/quicktime");
+ defaultMap.put("mov", "video/quicktime");
+ defaultMap.put("avi", "video/x-msvideo");
+ defaultMap.put("movie", "video/x-sgi-movie");
+ defaultMap.put("avx", "video/x-rad-screenplay");
+ defaultMap.put("wrl", "x-world/x-vrml");
+ defaultMap.put("mpv2", "video/mpeg2");
+
+ /* Add XML related MIMEs */
+
+ defaultMap.put("xml", "text/xml");
+ defaultMap.put("xsl", "text/xml");
+ defaultMap.put("svg", "image/svg+xml");
+ defaultMap.put("svgz", "image/svg+xml");
+ defaultMap.put("wbmp", "image/vnd.wap.wbmp");
+ defaultMap.put("wml", "text/vnd.wap.wml");
+ defaultMap.put("wmlc", "application/vnd.wap.wmlc");
+ defaultMap.put("wmls", "text/vnd.wap.wmlscript");
+ defaultMap.put("wmlscriptc", "application/vnd.wap.wmlscriptc");
+ }
+
+
+ private Hashtable<String,String> map = new Hashtable<String,String>();
+
+ public void addContentType(String extn, String type) {
+ map.put(extn, type.toLowerCase());
+ }
+
+ public Enumeration getExtensions() {
+ return map.keys();
+ }
+
+ public String getMimeType(String ext) {
+ return getContentTypeFor(ext);
+ }
+
+ public String getContentType(String extn) {
+ String type = (String)map.get(extn.toLowerCase());
+ if( type == null ) type=(String)defaultMap.get( extn );
+ return type;
+ }
+
+ public void removeContentType(String extn) {
+ map.remove(extn.toLowerCase());
+ }
+
+ /** Get extension of file, without fragment id
+ */
+ public static String getExtension( String fileName ) {
+ // play it safe and get rid of any fragment id
+ // that might be there
+ int length=fileName.length();
+
+ int newEnd = fileName.lastIndexOf('#');
+ if( newEnd== -1 ) newEnd=length;
+ // Instead of creating a new string.
+ // if (i != -1) {
+ // fileName = fileName.substring(0, i);
+ // }
+ int i = fileName.lastIndexOf('.', newEnd );
+ if (i != -1) {
+ return fileName.substring(i + 1, newEnd );
+ } else {
+ // no extension, no content type
+ return null;
+ }
+ }
+
+ public String getContentTypeFor(String fileName) {
+ String extn=getExtension( fileName );
+ if (extn!=null) {
+ return getContentType(extn);
+ } else {
+ // no extension, no content type
+ return null;
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.lite.util;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+/**
+ * Utils to process HTTP/1.1 ranges. Used by default servlet, could
+ * be used by any servlet that needs to deal with ranges.
+ *
+ * It is very good to support ranges if you have large content. In most
+ * cases supporting one range is enough - getting multiple ranges doesn't
+ * seem very common, and it's complex (multipart response).
+ *
+ * @author Costin Manolache
+ * @author Remy Maucherat
+ * @author - see DefaultServlet in Catalin for other contributors
+ */
+public class Range {
+
+ public long start;
+ public long end;
+ public long length;
+
+ /**
+ * Validate range.
+ */
+ public boolean validate() {
+ if (end >= length)
+ end = length - 1;
+ return ( (start >= 0) && (end >= 0) && (start <= end)
+ && (length > 0) );
+ }
+
+ public void recycle() {
+ start = 0;
+ end = 0;
+ length = 0;
+ }
+
+ /** Parse ranges.
+ *
+ * @return null if the range is invalid or can't be parsed
+ */
+ public static ArrayList parseRanges(long fileLength,
+ String rangeHeader) throws IOException {
+ ArrayList result = new ArrayList();
+ StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ",");
+
+ // Parsing the range list
+ while (commaTokenizer.hasMoreTokens()) {
+ String rangeDefinition = commaTokenizer.nextToken().trim();
+
+ Range currentRange = new Range();
+ currentRange.length = fileLength;
+
+ int dashPos = rangeDefinition.indexOf('-');
+
+ if (dashPos == -1) {
+ return null;
+ }
+
+ if (dashPos == 0) {
+ try {
+ long offset = Long.parseLong(rangeDefinition);
+ currentRange.start = fileLength + offset;
+ currentRange.end = fileLength - 1;
+ } catch (NumberFormatException e) {
+ return null;
+ }
+ } else {
+
+ try {
+ currentRange.start = Long.parseLong
+ (rangeDefinition.substring(0, dashPos));
+ if (dashPos < rangeDefinition.length() - 1)
+ currentRange.end = Long.parseLong
+ (rangeDefinition.substring
+ (dashPos + 1, rangeDefinition.length()));
+ else
+ currentRange.end = fileLength - 1;
+ } catch (NumberFormatException e) {
+ return null;
+ }
+
+ }
+ if (!currentRange.validate()) {
+ return null;
+ }
+ result.add(currentRange);
+ }
+ return result;
+ }
+
+
+ /**
+ * Parse the Content-Range header. Used with PUT or in response.
+ *
+ * @return Range
+ */
+ public static Range parseContentRange(String rangeHeader)
+ throws IOException {
+ if (rangeHeader == null)
+ return null;
+
+ // bytes is the only range unit supported
+ if (!rangeHeader.startsWith("bytes")) {
+ return null;
+ }
+
+ rangeHeader = rangeHeader.substring(6).trim();
+
+ int dashPos = rangeHeader.indexOf('-');
+ int slashPos = rangeHeader.indexOf('/');
+
+ if (dashPos == -1) {
+ return null;
+ }
+
+ if (slashPos == -1) {
+ return null;
+ }
+
+ Range range = new Range();
+
+ try {
+ range.start = Long.parseLong(rangeHeader.substring(0, dashPos));
+ range.end =
+ Long.parseLong(rangeHeader.substring(dashPos + 1, slashPos));
+ range.length = Long.parseLong
+ (rangeHeader.substring(slashPos + 1, rangeHeader.length()));
+ } catch (NumberFormatException e) {
+ return null;
+ }
+
+ if (!range.validate()) {
+ return null;
+ }
+
+ return range;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.lite.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.BitSet;
+
+/**
+ *
+ * This class is very similar to the java.net.URLEncoder class.
+ *
+ * Unfortunately, with java.net.URLEncoder there is no way to specify to the
+ * java.net.URLEncoder which characters should NOT be encoded.
+ *
+ * This code was moved from DefaultServlet.java
+ *
+ * @author Craig R. McClanahan
+ * @author Remy Maucherat
+ */
+public class URLEncoder {
+ protected static final char[] hexadecimal =
+ {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F'};
+
+ //Array containing the safe characters set.
+ protected BitSet safeChars = new BitSet(128);
+
+ public URLEncoder() {
+ for (char i = 'a'; i <= 'z'; i++) {
+ addSafeCharacter(i);
+ }
+ for (char i = 'A'; i <= 'Z'; i++) {
+ addSafeCharacter(i);
+ }
+ for (char i = '0'; i <= '9'; i++) {
+ addSafeCharacter(i);
+ }
+ //safe
+ safeChars.set('$');
+ safeChars.set('-');
+ safeChars.set('_');
+ safeChars.set('.');
+
+ // Dangerous: someone may treat this as " "
+ // RFC1738 does allow it, it's not reserved
+ // safeChars.set('+');
+ //extra
+ safeChars.set('!');
+ safeChars.set('*');
+ safeChars.set('\'');
+ safeChars.set('(');
+ safeChars.set(')');
+ safeChars.set(',');
+ }
+
+ public void addSafeCharacter( char c ) {
+ safeChars.set( c );
+ }
+
+ public String encodeURL(String path) {
+ return encodeURL(path, "UTF-8", true);
+ }
+
+ public String encodeURL(String path, String enc, boolean allowSlash) {
+ int maxBytesPerChar = 10;
+
+ StringBuffer rewrittenPath = new StringBuffer(path.length());
+ ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar);
+ OutputStreamWriter writer = null;
+ try {
+ writer = new OutputStreamWriter(buf, enc);
+ } catch (UnsupportedEncodingException e1) {
+ // shouldn't happen.
+ }
+
+ for (int i = 0; i < path.length(); i++) {
+ int c = (int) path.charAt(i);
+ if (c < 128 && safeChars.get(c) || allowSlash && c == '/') {
+ rewrittenPath.append((char)c);
+ } else {
+ // convert to external encoding before hex conversion
+ try {
+ writer.write((char)c);
+ if (c >= 0xD800 && c <= 0xDBFF) {
+ if ( (i+1) < path.length()) {
+ int d = path.charAt(i+1);
+ if (d >= 0xDC00 && d <= 0xDFFF) {
+ writer.write((char) d);
+ i++;
+ }
+ }
+ }
+ writer.flush();
+ } catch(IOException e) {
+ buf.reset();
+ continue;
+ }
+ byte[] ba = buf.toByteArray();
+ for (int j = 0; j < ba.length; j++) {
+ // Converting each byte in the buffer
+ byte toEncode = ba[j];
+ rewrittenPath.append('%');
+ int low = (int) (toEncode & 0x0f);
+ int high = (int) ((toEncode & 0xf0) >> 4);
+ rewrittenPath.append(hexadecimal[high]);
+ rewrittenPath.append(hexadecimal[low]);
+ }
+ buf.reset();
+ }
+ }
+ return rewrittenPath.toString();
+ }
+
+ /**
+ * Decode and return the specified URL-encoded String.
+ *
+ * @param str The url-encoded string
+ * @param enc The encoding to use; if null, the default encoding is used
+ * @exception IllegalArgumentException if a '%' character is not followed
+ * by a valid 2-digit hexadecimal number
+ */
+ public static String URLDecode(String str, String enc) {
+
+ if (str == null)
+ return (null);
+
+ // use the specified encoding to extract bytes out of the
+ // given string so that the encoding is not lost. If an
+ // encoding is not specified, let it use platform default
+ byte[] bytes = null;
+ try {
+ if (enc == null) {
+ bytes = str.getBytes();
+ } else {
+ bytes = str.getBytes(enc);
+ }
+ } catch (UnsupportedEncodingException uee) {}
+
+ return URLDecode(bytes, enc);
+
+ }
+
+
+ /**
+ * Decode and return the specified URL-encoded String.
+ * When the byte array is converted to a string, the system default
+ * character encoding is used... This may be different than some other
+ * servers.
+ *
+ * @param str The url-encoded string
+ *
+ * @exception IllegalArgumentException if a '%' character is not followed
+ * by a valid 2-digit hexadecimal number
+ */
+ public static String URLDecode(String str) {
+
+ return URLDecode(str, null);
+
+ }
+
+ /**
+ * Decode and return the specified URL-encoded byte array.
+ *
+ * @param bytes The url-encoded byte array
+ * @param enc The encoding to use; if null, the default encoding is used
+ * @exception IllegalArgumentException if a '%' character is not followed
+ * by a valid 2-digit hexadecimal number
+ */
+ private static String URLDecode(byte[] bytes, String enc) {
+
+ if (bytes == null)
+ return (null);
+
+ int len = bytes.length;
+ int ix = 0;
+ int ox = 0;
+ while (ix < len) {
+ byte b = bytes[ix++]; // Get byte to test
+ if (b == '+') {
+ b = (byte)' ';
+ } else if (b == '%') {
+ b = (byte) ((convertHexDigit(bytes[ix++]) << 4)
+ + convertHexDigit(bytes[ix++]));
+ }
+ bytes[ox++] = b;
+ }
+ if (enc != null) {
+ try {
+ return new String(bytes, 0, ox, enc);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ return new String(bytes, 0, ox);
+
+ }
+
+ /**
+ * Convert a byte character value to hexidecimal digit value.
+ *
+ * @param b the character value byte
+ */
+ private static byte convertHexDigit( byte b ) {
+ if ((b >= '0') && (b <= '9')) return (byte)(b - '0');
+ if ((b >= 'a') && (b <= 'f')) return (byte)(b - 'a' + 10);
+ if ((b >= 'A') && (b <= 'F')) return (byte)(b - 'A' + 10);
+ return 0;
+ }
+
+}
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.lite.util;
+
+public class UrlUtils {
+
+ /** Used by webdav.
+ *
+ * Return a context-relative path, beginning with a "/", that represents
+ * the canonical version of the specified path after ".." and "." elements
+ * are resolved out. If the specified path attempts to go outside the
+ * boundaries of the current context (i.e. too many ".." path elements
+ * are present), return <code>null</code> instead.
+ *
+ * @param path Path to be normalized
+ */
+ public static String normalize(String path) {
+
+ if (path == null)
+ return null;
+
+ // Create a place for the normalized path
+ String normalized = path;
+
+ if (normalized.equals("/."))
+ return "/";
+
+ // Normalize the slashes and add leading slash if necessary
+ if (normalized.indexOf('\\') >= 0)
+ normalized = normalized.replace('\\', '/');
+
+ if (!normalized.startsWith("/"))
+ normalized = "/" + normalized;
+
+ // Resolve occurrences of "//" in the normalized path
+ while (true) {
+ int index = normalized.indexOf("//");
+ if (index < 0)
+ break;
+ normalized = normalized.substring(0, index) +
+ normalized.substring(index + 1);
+ }
+
+ // Resolve occurrences of "/./" in the normalized path
+ while (true) {
+ int index = normalized.indexOf("/./");
+ if (index < 0)
+ break;
+ normalized = normalized.substring(0, index) +
+ normalized.substring(index + 2);
+ }
+
+ // Resolve occurrences of "/../" in the normalized path
+ while (true) {
+ int index = normalized.indexOf("/../");
+ if (index < 0)
+ break;
+ if (index == 0)
+ return (null); // Trying to go outside our context
+ int index2 = normalized.lastIndexOf('/', index - 1);
+ normalized = normalized.substring(0, index2) +
+ normalized.substring(index + 3);
+ }
+
+ // Return the normalized path that we have completed
+ return (normalized);
+ }
+
+}
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.apache.tomcat.servlets.util.Range;
-import org.apache.tomcat.servlets.util.URLEncoder;
+
+import org.apache.tomcat.lite.util.CopyUtils;
+import org.apache.tomcat.lite.util.Dir2Html;
+import org.apache.tomcat.lite.util.Range;
+import org.apache.tomcat.lite.util.URLEncoder;
/**
* The default resource-serving servlet for most web applications,
// --------------------------------------------------------- Public Methods
- protected Filesystem fs;
-
/**
* Finalize this servlet.
*/
* Initialize this servlet.
*/
public void init() throws ServletException {
- if (fs == null) {
- // R/O - no write
- fs = new Filesystem();
- }
String realPath = getServletContext().getRealPath("/");
basePath = new File(realPath);
} catch (IOException e) {
basePathName = basePath.getAbsolutePath();
}
- log("Init fs " + fs + " base: " + basePathName);
+ log("Init default serviet, base: " + basePathName);
// Set our properties from the initialization parameters
String value = null;
output = 256;
}
- public void setFilesystem(Filesystem fs) {
- this.fs = fs;
- }
-
- public Filesystem getFilesystem() {
- return fs;
- }
-
public void setBasePath(String s) {
this.basePathName = s;
this.basePath = new File(s);
// }
}
+ public void renderDir(HttpServletRequest request,
+ HttpServletResponse response,
+ File resFile,
+ String fileEncoding,
+ boolean content,
+ String relativePath) throws IOException {
+
+ String contentType = "text/html;charset=" + fileEncoding;
+
+ ServletOutputStream ostream = null;
+ PrintWriter writer = null;
+
+ if (content) {
+ // Trying to retrieve the servlet output stream
+ try {
+ ostream = response.getOutputStream();
+ } catch (IllegalStateException e) {
+ // If it fails, we try to get a Writer instead if we're
+ // trying to serve a text file
+ if ( (contentType == null)
+ || (contentType.startsWith("text")) ) {
+ writer = response.getWriter();
+ } else {
+ throw e;
+ }
+ }
+
+ }
+
+ // Set the appropriate output headers
+ response.setContentType(contentType);
+
+ InputStream renderResult = null;
+
+ if (content) {
+ // Serve the directory browser
+ renderResult =
+ dir2Html.render(request.getContextPath(), resFile, relativePath);
+ }
+
+
+ // Copy the input stream to our output stream (if requested)
+ if (content) {
+ try {
+ response.setBufferSize(output);
+ } catch (IllegalStateException e) {
+ // Silent catch
+ }
+ if (ostream != null) {
+ CopyUtils.copy(renderResult, ostream);
+ } else {
+ CopyUtils.copy(renderResult, writer, fileEncoding);
+ }
+ }
+
+
+ }
+
/**
* Serve the specified resource, optionally including the data content.
request.getRequestURI());
return;
}
- dir2Html.renderDir(request, response, resFile, fileEncoding, content,
+ renderDir(request, response, resFile, fileEncoding, content,
path);
return;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
-import org.apache.tomcat.servlets.util.FastHttpDateFormat;
-import org.apache.tomcat.servlets.util.Range;
+import org.apache.tomcat.lite.util.CopyUtils;
+import org.apache.tomcat.lite.util.FastHttpDateFormat;
+import org.apache.tomcat.lite.util.Range;
+import org.apache.tomcat.lite.util.URLEncoder;
+import org.apache.tomcat.lite.util.UrlUtils;
+import org.apache.tomcat.lite.util.XMLWriter;
import org.apache.tomcat.servlets.util.RequestUtil;
-import org.apache.tomcat.servlets.util.UrlUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
* @param path Path which has to be rewiten
*/
protected String rewriteUrl(String path) {
- return urlEncoder.encode( path );
+ return urlEncoder.encodeURL( path );
}
try {
// will override
- OutputStream fos = getFilesystem().getOutputStream(resFile.getPath());
+ OutputStream fos = getOut(resFile.getPath());
CopyUtils.copy(resourceInputStream, fos);
} catch(IOException e) {
result = false;
* Handle a partial PUT. New content specified in request is appended to
* existing content in oldRevisionContent (if present). This code does
* not support simultaneous partial updates to the same resource.
+ * @throws FileNotFoundException
*/
// protected File executePartialPut(HttpServletRequest req, Range range,
// String path)
// }
+ private OutputStream getOut(String path) throws FileNotFoundException {
+ return new FileOutputStream(path);
+ }
+
/**
* COPY Method.
*/
}
// Remove url encoding from destination
- destinationPath = RequestUtil.URLDecode(destinationPath, "UTF8");
+ destinationPath = URLEncoder.URLDecode(destinationPath, "UTF8");
destinationPath = removeDestinationPrefix(req, destinationPath);
} else {
try {
- CopyUtils.copy(getFilesystem().getInputStream(object.getPath()),
- getFilesystem().getOutputStream(dest));
+ CopyUtils.copy(new FileInputStream(object.getPath()),
+ getOut(dest));
} catch(IOException ex ) {
errorList.put
(source,
+++ /dev/null
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package org.apache.tomcat.servlets.util;
-
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-
-/**
- * This class provides encode/decode for RFC 2045 Base64 as
- * defined by RFC 2045, N. Freed and N. Borenstein.
- * RFC 2045: Multipurpose Internet Mail Extensions (MIME)
- * Part One: Format of Internet Message Bodies. Reference
- * 1996 Available at: http://www.ietf.org/rfc/rfc2045.txt
- * This class is used by XML Schema binary format validation
- *
- * @author Jeffrey Rodriguez
- * @version $Revision: 467222 $ $Date: 2006-10-23 20:17:11 -0700 (Mon, 23 Oct 2006) $
- */
-
-public final class Base64 {
-
-
- private static Logger log=
- Logger.getLogger( Base64.class.getName() );
-
- static private final int BASELENGTH = 255;
- static private final int LOOKUPLENGTH = 63;
- static private final int TWENTYFOURBITGROUP = 24;
- static private final int EIGHTBIT = 8;
- static private final int SIXTEENBIT = 16;
- static private final int SIXBIT = 6;
- static private final int FOURBYTE = 4;
-
-
- static private final byte PAD = ( byte ) '=';
- static private byte [] base64Alphabet = new byte[BASELENGTH];
- static private byte [] lookUpBase64Alphabet = new byte[LOOKUPLENGTH + 1];
-
- static {
-
- for (int i = 0; i<BASELENGTH; i++ ) {
- base64Alphabet[i] = -1;
- }
- for ( int i = 'Z'; i >= 'A'; i-- ) {
- base64Alphabet[i] = (byte) (i-'A');
- }
- for ( int i = 'z'; i>= 'a'; i--) {
- base64Alphabet[i] = (byte) ( i-'a' + 26);
- }
-
- for ( int i = '9'; i >= '0'; i--) {
- base64Alphabet[i] = (byte) (i-'0' + 52);
- }
-
- base64Alphabet['+'] = 62;
- base64Alphabet['/'] = 63;
- String lookupString =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" +
- "0123456789+/";
-
- for (int i = 0; i < lookupString.length(); i++ ) {
- lookUpBase64Alphabet[i] = (byte) lookupString.charAt(i);
- }
- }
-
-
- static boolean isBase64( byte octect ) {
- //shall we ignore white space? JEFF??
- return(octect == PAD || base64Alphabet[octect] != -1 );
- }
-
-
- static boolean isArrayByteBase64( byte[] arrayOctect ) {
- int length = arrayOctect.length;
- if ( length == 0 )
- return false;
- for ( int i=0; i < length; i++ ) {
- if ( Base64.isBase64( arrayOctect[i] ) == false)
- return false;
- }
- return true;
- }
-
- /**
- * Encodes hex octects into Base64
- *
- * @param binaryData Array containing binaryData
- * @return Encoded Base64 array
- */
- public static byte[] encode( byte[] binaryData ) {
- int lengthDataBits = binaryData.length*EIGHTBIT;
- int fewerThan24bits = lengthDataBits%TWENTYFOURBITGROUP;
- int numberTriplets = lengthDataBits/TWENTYFOURBITGROUP;
- byte encodedData[] = null;
-
-
- if ( fewerThan24bits != 0 ) //data not divisible by 24 bit
- encodedData = new byte[ (numberTriplets + 1 )*4 ];
- else // 16 or 8 bit
- encodedData = new byte[ numberTriplets*4 ];
-
- int k=0, l=0, b1=0,b2=0,b3=0;
-
- int encodedIndex = 0;
- int dataIndex = 0;
- int i = 0;
- for ( i = 0; i<numberTriplets; i++ ) {
-
- dataIndex = i*3;
- b1 = binaryData[dataIndex] & 0xFF;
- b2 = binaryData[dataIndex + 1] & 0xFF;
- b3 = binaryData[dataIndex + 2] & 0xFF;
-
- l = (byte)(b2 & 0x0f);
- k = (byte)(b1 & 0x03);
-
- encodedIndex = i*4;
- encodedData[encodedIndex] = lookUpBase64Alphabet[ b1 >>2 ];
- encodedData[encodedIndex+1] = lookUpBase64Alphabet[(b2 >>4 ) |
-( k<<4 )];
- encodedData[encodedIndex+2] = lookUpBase64Alphabet[ (l <<2 ) |
-( b3>>6)];
- encodedData[encodedIndex+3] = lookUpBase64Alphabet[ b3 & 0x3f ];
- }
-
- // form integral number of 6-bit groups
- dataIndex = i*3;
- encodedIndex = i*4;
- if (fewerThan24bits == EIGHTBIT ) {
- b1 = binaryData[dataIndex];
- k = (byte) ( b1 &0x03 );
- encodedData[encodedIndex] = lookUpBase64Alphabet[ b1 >>2 ];
- encodedData[encodedIndex + 1] = lookUpBase64Alphabet[ k<<4 ];
- encodedData[encodedIndex + 2] = PAD;
- encodedData[encodedIndex + 3] = PAD;
- } else if ( fewerThan24bits == SIXTEENBIT ) {
-
- b1 = binaryData[dataIndex];
- b2 = binaryData[dataIndex +1 ];
- l = ( byte ) ( b2 &0x0f );
- k = ( byte ) ( b1 &0x03 );
- encodedData[encodedIndex] = lookUpBase64Alphabet[ b1 >>2 ];
- encodedData[encodedIndex + 1] = lookUpBase64Alphabet[ (b2 >>4 )
-| ( k<<4 )];
- encodedData[encodedIndex + 2] = lookUpBase64Alphabet[ l<<2 ];
- encodedData[encodedIndex + 3] = PAD;
- }
- return encodedData;
- }
-
-
- /**
- * Decodes Base64 data into octects
- *
- * @param base64Data Byte array containing Base64 data
- * @return Array containind decoded data.
- */
- public static byte[] decode( byte[] base64Data ) {
- int numberQuadruple = base64Data.length/FOURBYTE;
- byte decodedData[] = null;
- byte b1=0,b2=0,b3=0, b4=0, marker0=0, marker1=0;
-
- // Throw away anything not in base64Data
- // Adjust size
-
- int encodedIndex = 0;
- int dataIndex = 0;
- decodedData = new byte[ numberQuadruple*3 + 1 ];
-
- for (int i = 0; i<numberQuadruple; i++ ) {
- dataIndex = i*4;
- marker0 = base64Data[dataIndex +2];
- marker1 = base64Data[dataIndex +3];
-
- b1 = base64Alphabet[base64Data[dataIndex]];
- b2 = base64Alphabet[base64Data[dataIndex +1]];
-
- if ( marker0 != PAD && marker1 != PAD ) { //No PAD e.g 3cQl
- b3 = base64Alphabet[ marker0 ];
- b4 = base64Alphabet[ marker1 ];
-
- decodedData[encodedIndex] = (byte)( b1 <<2 | b2>>4 ) ;
- decodedData[encodedIndex+1] = (byte)(((b2 & 0xf)<<4 ) |(
-(b3>>2) & 0xf) );
- decodedData[encodedIndex+2] = (byte)( b3<<6 | b4 );
- } else if ( marker0 == PAD ) { //Two PAD e.g. 3c[Pad][Pad]
- decodedData[encodedIndex] = (byte)( b1 <<2 | b2>>4 ) ;
- decodedData[encodedIndex+1] = (byte)((b2 & 0xf)<<4 );
- decodedData[encodedIndex+2] = (byte) 0;
- } else if ( marker1 == PAD ) { //One PAD e.g. 3cQ[Pad]
- b3 = base64Alphabet[ marker0 ];
-
- decodedData[encodedIndex] = (byte)( b1 <<2 | b2>>4 );
- decodedData[encodedIndex+1] = (byte)(((b2 & 0xf)<<4 ) |(
-(b3>>2) & 0xf) );
- decodedData[encodedIndex+2] = (byte)( b3<<6);
- }
- encodedIndex += 3;
- }
- return decodedData;
-
- }
-
- static final int base64[]= {
- 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
- 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
- 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
- 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
- 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
- };
-
- public static String base64Decode( String orig ) {
- char chars[]=orig.toCharArray();
- StringBuffer sb=new StringBuffer();
- int i=0;
-
- int shift = 0; // # of excess bits stored in accum
- int acc = 0;
-
- for (i=0; i<chars.length; i++) {
- int v = base64[ chars[i] & 0xFF ];
-
- if ( v >= 64 ) {
- if( chars[i] != '=' )
- if (log.isLoggable(Level.INFO))
- log.info("Wrong char in base64: " + chars[i]);
- } else {
- acc= ( acc << 6 ) | v;
- shift += 6;
- if ( shift >= 8 ) {
- shift -= 8;
- sb.append( (char) ((acc >> shift) & 0xff));
- }
- }
- }
- return sb.toString();
- }
-
-
-}
-
+++ /dev/null
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.tomcat.servlets.util;
-
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-import java.util.TimeZone;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * Utility class to generate HTTP dates.
- *
- * @author Remy Maucherat
- */
-public final class FastHttpDateFormat {
-
-
- // -------------------------------------------------------------- Variables
-
-
- protected static final int CACHE_SIZE =
- Integer.parseInt(System.getProperty("org.apache.tomcat.util.http.FastHttpDateFormat.CACHE_SIZE", "1000"));
-
-
- /**
- * HTTP date format.
- */
- protected static final SimpleDateFormat format =
- new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
-
-
- /**
- * The set of SimpleDateFormat formats to use in getDateHeader().
- */
- protected static final SimpleDateFormat formats[] = {
- new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
- new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
- new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
- };
-
-
- protected final static TimeZone gmtZone = TimeZone.getTimeZone("GMT");
-
-
- /**
- * GMT timezone - all HTTP dates are on GMT
- */
- static {
-
- format.setTimeZone(gmtZone);
-
- formats[0].setTimeZone(gmtZone);
- formats[1].setTimeZone(gmtZone);
- formats[2].setTimeZone(gmtZone);
-
- }
-
-
- /**
- * Instant on which the currentDate object was generated.
- */
- protected static long currentDateGenerated = 0L;
-
-
- /**
- * Current formatted date.
- */
- protected static String currentDate = null;
-
-
- /**
- * Formatter cache.
- */
- protected static final ConcurrentHashMap<Long, String> formatCache =
- new ConcurrentHashMap<Long, String>(CACHE_SIZE);
-
-
- /**
- * Parser cache.
- */
- protected static final ConcurrentHashMap<String, Long> parseCache =
- new ConcurrentHashMap<String, Long>(CACHE_SIZE);
-
-
- // --------------------------------------------------------- Public Methods
-
-
- /**
- * Get the current date in HTTP format.
- */
- public static final String getCurrentDate() {
-
- long now = System.currentTimeMillis();
- if ((now - currentDateGenerated) > 1000) {
- synchronized (format) {
- if ((now - currentDateGenerated) > 1000) {
- currentDateGenerated = now;
- currentDate = format.format(new Date(now));
- }
- }
- }
- return currentDate;
-
- }
-
-
- /**
- * Get the HTTP format of the specified date.
- */
- public static final String formatDate
- (long value, DateFormat threadLocalformat) {
-
- Long longValue = new Long(value);
- String cachedDate = formatCache.get(longValue);
- if (cachedDate != null)
- return cachedDate;
-
- String newDate = null;
- Date dateValue = new Date(value);
- if (threadLocalformat != null) {
- newDate = threadLocalformat.format(dateValue);
- updateFormatCache(longValue, newDate);
- } else {
- synchronized (formatCache) {
- synchronized (format) {
- newDate = format.format(dateValue);
- }
- updateFormatCache(longValue, newDate);
- }
- }
- return newDate;
-
- }
-
-
- /**
- * Try to parse the given date as a HTTP date.
- */
- public static final long parseDate(String value,
- DateFormat[] threadLocalformats) {
-
- Long cachedDate = parseCache.get(value);
- if (cachedDate != null)
- return cachedDate.longValue();
-
- Long date = null;
- if (threadLocalformats != null) {
- date = internalParseDate(value, threadLocalformats);
- updateParseCache(value, date);
- } else {
- synchronized (parseCache) {
- date = internalParseDate(value, formats);
- updateParseCache(value, date);
- }
- }
- if (date == null) {
- return (-1L);
- } else {
- return date.longValue();
- }
-
- }
-
-
- /**
- * Parse date with given formatters.
- */
- private static final Long internalParseDate
- (String value, DateFormat[] formats) {
- Date date = null;
- for (int i = 0; (date == null) && (i < formats.length); i++) {
- try {
- date = formats[i].parse(value);
- } catch (ParseException e) {
- ;
- }
- }
- if (date == null) {
- return null;
- }
- return new Long(date.getTime());
- }
-
-
- /**
- * Update cache.
- */
- private static void updateFormatCache(Long key, String value) {
- if (value == null) {
- return;
- }
- if (formatCache.size() > CACHE_SIZE) {
- formatCache.clear();
- }
- formatCache.put(key, value);
- }
-
-
- /**
- * Update cache.
- */
- private static void updateParseCache(String key, Long value) {
- if (value == null) {
- return;
- }
- if (parseCache.size() > CACHE_SIZE) {
- parseCache.clear();
- }
- parseCache.put(key, value);
- }
-
-
-}
+++ /dev/null
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package org.apache.tomcat.servlets.util;
-
-import java.util.ArrayList;
-import java.util.Locale;
-import java.util.TreeMap;
-
-
-/**
- * Utility class for string parsing that is higher performance than
- * StringParser for simple delimited text cases. Parsing is performed
- * by setting the string, and then using the <code>findXxxx()</code> and
- * <code>skipXxxx()</code> families of methods to remember significant
- * offsets. To retrieve the parsed substrings, call the <code>extract()</code>
- * method with the appropriate saved offset values.
- *
- * @author Craig R. McClanahan
- */
-public final class LocaleParser {
-
- public LocaleParser() {
- this(null);
- }
-
- public LocaleParser(String string) {
- setString(string);
- }
-
- public TreeMap parseLocale(String value) {
- // Store the accumulated languages that have been requested in
- // a local collection, sorted by the quality value (so we can
- // add Locales in descending order). The values will be ArrayLists
- // containing the corresponding Locales to be added
- TreeMap locales = new TreeMap();
-
- // Preprocess the value to remove all whitespace
- int white = value.indexOf(' ');
- if (white < 0)
- white = value.indexOf('\t');
- if (white >= 0) {
- StringBuilder sb = new StringBuilder();
- int len = value.length();
- for (int i = 0; i < len; i++) {
- char ch = value.charAt(i);
- if ((ch != ' ') && (ch != '\t'))
- sb.append(ch);
- }
- value = sb.toString();
- }
-
- LocaleParser parser = this;
- // Process each comma-delimited language specification
- parser.setString(value); // ASSERT: parser is available to us
- int length = parser.getLength();
- while (true) {
-
- // Extract the next comma-delimited entry
- int start = parser.getIndex();
- if (start >= length)
- break;
- int end = parser.findChar(',');
- String entry = parser.extract(start, end).trim();
- parser.advance(); // For the following entry
-
- // Extract the quality factor for this entry
- double quality = 1.0;
- int semi = entry.indexOf(";q=");
- if (semi >= 0) {
- try {
- quality = Double.parseDouble(entry.substring(semi + 3));
- } catch (NumberFormatException e) {
- quality = 0.0;
- }
- entry = entry.substring(0, semi);
- }
-
- // Skip entries we are not going to keep track of
- if (quality < 0.00005)
- continue; // Zero (or effectively zero) quality factors
- if ("*".equals(entry))
- continue; // FIXME - "*" entries are not handled
-
- // Extract the language and country for this entry
- String language = null;
- String country = null;
- String variant = null;
- int dash = entry.indexOf('-');
- if (dash < 0) {
- language = entry;
- country = "";
- variant = "";
- } else {
- language = entry.substring(0, dash);
- country = entry.substring(dash + 1);
- int vDash = country.indexOf('-');
- if (vDash > 0) {
- String cTemp = country.substring(0, vDash);
- variant = country.substring(vDash + 1);
- country = cTemp;
- } else {
- variant = "";
- }
- }
-
- // Add a new Locale to the list of Locales for this quality level
- Locale locale = new Locale(language, country, variant);
- Double key = new Double(-quality); // Reverse the order
- ArrayList values = (ArrayList) locales.get(key);
- if (values == null) {
- values = new ArrayList();
- locales.put(key, values);
- }
- values.add(locale);
-
- }
-
- return locales;
- }
-
- /**
- * The characters of the current string, as a character array. Stored
- * when the string is first specified to speed up access to characters
- * being compared during parsing.
- */
- private char chars[] = null;
-
-
- /**
- * The zero-relative index of the current point at which we are
- * positioned within the string being parsed. <strong>NOTE</strong>:
- * the value of this index can be one larger than the index of the last
- * character of the string (i.e. equal to the string length) if you
- * parse off the end of the string. This value is useful for extracting
- * substrings that include the end of the string.
- */
- private int index = 0;
-
-
- /**
- * The length of the String we are currently parsing. Stored when the
- * string is first specified to avoid repeated recalculations.
- */
- private int length = 0;
-
-
- /**
- * The String we are currently parsing.
- */
- private String string = null;
-
-
- // ------------------------------------------------------------- Properties
-
-
- /**
- * Return the zero-relative index of our current parsing position
- * within the string being parsed.
- */
- public int getIndex() {
-
- return (this.index);
-
- }
-
-
- /**
- * Return the length of the string we are parsing.
- */
- public int getLength() {
-
- return (this.length);
-
- }
-
-
- /**
- * Return the String we are currently parsing.
- */
- public String getString() {
-
- return (this.string);
-
- }
-
-
- /**
- * Set the String we are currently parsing. The parser state is also reset
- * to begin at the start of this string.
- *
- * @param string The string to be parsed.
- */
- public void setString(String string) {
-
- this.string = string;
- if (string != null) {
- this.length = string.length();
- chars = this.string.toCharArray();
- } else {
- this.length = 0;
- chars = new char[0];
- }
- reset();
-
- }
-
-
- // --------------------------------------------------------- Public Methods
-
-
- /**
- * Advance the current parsing position by one, if we are not already
- * past the end of the string.
- */
- public void advance() {
-
- if (index < length)
- index++;
-
- }
-
-
- /**
- * Extract and return a substring that starts at the specified position,
- * and extends to the end of the string being parsed. If this is not
- * possible, a zero-length string is returned.
- *
- * @param start Starting index, zero relative, inclusive
- */
- public String extract(int start) {
-
- if ((start < 0) || (start >= length))
- return ("");
- else
- return (string.substring(start));
-
- }
-
-
- /**
- * Extract and return a substring that starts at the specified position,
- * and ends at the character before the specified position. If this is
- * not possible, a zero-length string is returned.
- *
- * @param start Starting index, zero relative, inclusive
- * @param end Ending index, zero relative, exclusive
- */
- public String extract(int start, int end) {
-
- if ((start < 0) || (start >= end) || (end > length))
- return ("");
- else
- return (string.substring(start, end));
-
- }
-
-
- /**
- * Return the index of the next occurrence of the specified character,
- * or the index of the character after the last position of the string
- * if no more occurrences of this character are found. The current
- * parsing position is updated to the returned value.
- *
- * @param ch Character to be found
- */
- public int findChar(char ch) {
-
- while ((index < length) && (ch != chars[index]))
- index++;
- return (index);
-
- }
-
-
- /**
- * Return the index of the next occurrence of a non-whitespace character,
- * or the index of the character after the last position of the string
- * if no more non-whitespace characters are found. The current
- * parsing position is updated to the returned value.
- */
- public int findText() {
-
- while ((index < length) && isWhite(chars[index]))
- index++;
- return (index);
-
- }
-
-
- /**
- * Return the index of the next occurrence of a whitespace character,
- * or the index of the character after the last position of the string
- * if no more whitespace characters are found. The current parsing
- * position is updated to the returned value.
- */
- public int findWhite() {
-
- while ((index < length) && !isWhite(chars[index]))
- index++;
- return (index);
-
- }
-
-
- /**
- * Reset the current state of the parser to the beginning of the
- * current string being parsed.
- */
- public void reset() {
-
- index = 0;
-
- }
-
-
- /**
- * Advance the current parsing position while it is pointing at the
- * specified character, or until it moves past the end of the string.
- * Return the final value.
- *
- * @param ch Character to be skipped
- */
- public int skipChar(char ch) {
-
- while ((index < length) && (ch == chars[index]))
- index++;
- return (index);
-
- }
-
-
- /**
- * Advance the current parsing position while it is pointing at a
- * non-whitespace character, or until it moves past the end of the string.
- * Return the final value.
- */
- public int skipText() {
-
- while ((index < length) && !isWhite(chars[index]))
- index++;
- return (index);
-
- }
-
-
- /**
- * Advance the current parsing position while it is pointing at a
- * whitespace character, or until it moves past the end of the string.
- * Return the final value.
- */
- public int skipWhite() {
-
- while ((index < length) && isWhite(chars[index]))
- index++;
- return (index);
-
- }
-
-
- // ------------------------------------------------------ Protected Methods
-
-
- /**
- * Is the specified character considered to be whitespace?
- *
- * @param ch Character to be checked
- */
- protected boolean isWhite(char ch) {
-
- if ((ch == ' ') || (ch == '\t') || (ch == '\r') || (ch == '\n'))
- return (true);
- else
- return (false);
-
- }
-
-
-}
+++ /dev/null
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.tomcat.servlets.util;
-
-import java.net.FileNameMap;
-import java.util.Enumeration;
-import java.util.Hashtable;
-
-
-/**
- * A mime type map that implements the java.net.FileNameMap interface.
- *
- * @author James Duncan Davidson [duncan@eng.sun.com]
- * @author Jason Hunter [jch@eng.sun.com]
- */
-public class MimeMap implements FileNameMap {
-
- // Defaults - all of them are "well-known" types,
- // you can add using normal web.xml.
-
- public static Hashtable<String,String> defaultMap =
- new Hashtable<String,String>(101);
- static {
- defaultMap.put("txt", "text/plain");
- defaultMap.put("css", "text/css");
- defaultMap.put("html","text/html");
- defaultMap.put("htm", "text/html");
- defaultMap.put("gif", "image/gif");
- defaultMap.put("jpg", "image/jpeg");
- defaultMap.put("jpe", "image/jpeg");
- defaultMap.put("jpeg", "image/jpeg");
- defaultMap.put("png", "image/png");
- defaultMap.put("java", "text/plain");
- defaultMap.put("body", "text/html");
- defaultMap.put("rtx", "text/richtext");
- defaultMap.put("tsv", "text/tab-separated-values");
- defaultMap.put("etx", "text/x-setext");
- defaultMap.put("ps", "application/x-postscript");
- defaultMap.put("class", "application/java");
- defaultMap.put("csh", "application/x-csh");
- defaultMap.put("sh", "application/x-sh");
- defaultMap.put("tcl", "application/x-tcl");
- defaultMap.put("tex", "application/x-tex");
- defaultMap.put("texinfo", "application/x-texinfo");
- defaultMap.put("texi", "application/x-texinfo");
- defaultMap.put("t", "application/x-troff");
- defaultMap.put("tr", "application/x-troff");
- defaultMap.put("roff", "application/x-troff");
- defaultMap.put("man", "application/x-troff-man");
- defaultMap.put("me", "application/x-troff-me");
- defaultMap.put("ms", "application/x-wais-source");
- defaultMap.put("src", "application/x-wais-source");
- defaultMap.put("zip", "application/zip");
- defaultMap.put("bcpio", "application/x-bcpio");
- defaultMap.put("cpio", "application/x-cpio");
- defaultMap.put("gtar", "application/x-gtar");
- defaultMap.put("shar", "application/x-shar");
- defaultMap.put("sv4cpio", "application/x-sv4cpio");
- defaultMap.put("sv4crc", "application/x-sv4crc");
- defaultMap.put("tar", "application/x-tar");
- defaultMap.put("ustar", "application/x-ustar");
- defaultMap.put("dvi", "application/x-dvi");
- defaultMap.put("hdf", "application/x-hdf");
- defaultMap.put("latex", "application/x-latex");
- defaultMap.put("bin", "application/octet-stream");
- defaultMap.put("oda", "application/oda");
- defaultMap.put("pdf", "application/pdf");
- defaultMap.put("ps", "application/postscript");
- defaultMap.put("eps", "application/postscript");
- defaultMap.put("ai", "application/postscript");
- defaultMap.put("rtf", "application/rtf");
- defaultMap.put("nc", "application/x-netcdf");
- defaultMap.put("cdf", "application/x-netcdf");
- defaultMap.put("cer", "application/x-x509-ca-cert");
- defaultMap.put("exe", "application/octet-stream");
- defaultMap.put("gz", "application/x-gzip");
- defaultMap.put("Z", "application/x-compress");
- defaultMap.put("z", "application/x-compress");
- defaultMap.put("hqx", "application/mac-binhex40");
- defaultMap.put("mif", "application/x-mif");
- defaultMap.put("ief", "image/ief");
- defaultMap.put("tiff", "image/tiff");
- defaultMap.put("tif", "image/tiff");
- defaultMap.put("ras", "image/x-cmu-raster");
- defaultMap.put("pnm", "image/x-portable-anymap");
- defaultMap.put("pbm", "image/x-portable-bitmap");
- defaultMap.put("pgm", "image/x-portable-graymap");
- defaultMap.put("ppm", "image/x-portable-pixmap");
- defaultMap.put("rgb", "image/x-rgb");
- defaultMap.put("xbm", "image/x-xbitmap");
- defaultMap.put("xpm", "image/x-xpixmap");
- defaultMap.put("xwd", "image/x-xwindowdump");
- defaultMap.put("au", "audio/basic");
- defaultMap.put("snd", "audio/basic");
- defaultMap.put("aif", "audio/x-aiff");
- defaultMap.put("aiff", "audio/x-aiff");
- defaultMap.put("aifc", "audio/x-aiff");
- defaultMap.put("wav", "audio/x-wav");
- defaultMap.put("mpeg", "video/mpeg");
- defaultMap.put("mpg", "video/mpeg");
- defaultMap.put("mpe", "video/mpeg");
- defaultMap.put("qt", "video/quicktime");
- defaultMap.put("mov", "video/quicktime");
- defaultMap.put("avi", "video/x-msvideo");
- defaultMap.put("movie", "video/x-sgi-movie");
- defaultMap.put("avx", "video/x-rad-screenplay");
- defaultMap.put("wrl", "x-world/x-vrml");
- defaultMap.put("mpv2", "video/mpeg2");
-
- /* Add XML related MIMEs */
-
- defaultMap.put("xml", "text/xml");
- defaultMap.put("xsl", "text/xml");
- defaultMap.put("svg", "image/svg+xml");
- defaultMap.put("svgz", "image/svg+xml");
- defaultMap.put("wbmp", "image/vnd.wap.wbmp");
- defaultMap.put("wml", "text/vnd.wap.wml");
- defaultMap.put("wmlc", "application/vnd.wap.wmlc");
- defaultMap.put("wmls", "text/vnd.wap.wmlscript");
- defaultMap.put("wmlscriptc", "application/vnd.wap.wmlscriptc");
- }
-
-
- private Hashtable<String,String> map = new Hashtable<String,String>();
-
- public void addContentType(String extn, String type) {
- map.put(extn, type.toLowerCase());
- }
-
- public Enumeration getExtensions() {
- return map.keys();
- }
-
- public String getMimeType(String ext) {
- return getContentTypeFor(ext);
- }
-
- public String getContentType(String extn) {
- String type = (String)map.get(extn.toLowerCase());
- if( type == null ) type=(String)defaultMap.get( extn );
- return type;
- }
-
- public void removeContentType(String extn) {
- map.remove(extn.toLowerCase());
- }
-
- /** Get extension of file, without fragment id
- */
- public static String getExtension( String fileName ) {
- // play it safe and get rid of any fragment id
- // that might be there
- int length=fileName.length();
-
- int newEnd = fileName.lastIndexOf('#');
- if( newEnd== -1 ) newEnd=length;
- // Instead of creating a new string.
- // if (i != -1) {
- // fileName = fileName.substring(0, i);
- // }
- int i = fileName.lastIndexOf('.', newEnd );
- if (i != -1) {
- return fileName.substring(i + 1, newEnd );
- } else {
- // no extension, no content type
- return null;
- }
- }
-
- public String getContentTypeFor(String fileName) {
- String extn=getExtension( fileName );
- if (extn!=null) {
- return getContentType(extn);
- } else {
- // no extension, no content type
- return null;
- }
- }
-
-}
+++ /dev/null
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.tomcat.servlets.util;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.StringTokenizer;
-
-/**
- * Utils to process HTTP/1.1 ranges. Used by default servlet, could
- * be used by any servlet that needs to deal with ranges.
- *
- * It is very good to support ranges if you have large content. In most
- * cases supporting one range is enough - getting multiple ranges doesn't
- * seem very common, and it's complex (multipart response).
- *
- * @author Costin Manolache
- * @author Remy Maucherat
- * @author - see DefaultServlet in Catalin for other contributors
- */
-public class Range {
-
- public long start;
- public long end;
- public long length;
-
- /**
- * Validate range.
- */
- public boolean validate() {
- if (end >= length)
- end = length - 1;
- return ( (start >= 0) && (end >= 0) && (start <= end)
- && (length > 0) );
- }
-
- public void recycle() {
- start = 0;
- end = 0;
- length = 0;
- }
-
- /** Parse ranges.
- *
- * @return null if the range is invalid or can't be parsed
- */
- public static ArrayList parseRanges(long fileLength,
- String rangeHeader) throws IOException {
- ArrayList result = new ArrayList();
- StringTokenizer commaTokenizer = new StringTokenizer(rangeHeader, ",");
-
- // Parsing the range list
- while (commaTokenizer.hasMoreTokens()) {
- String rangeDefinition = commaTokenizer.nextToken().trim();
-
- Range currentRange = new Range();
- currentRange.length = fileLength;
-
- int dashPos = rangeDefinition.indexOf('-');
-
- if (dashPos == -1) {
- return null;
- }
-
- if (dashPos == 0) {
- try {
- long offset = Long.parseLong(rangeDefinition);
- currentRange.start = fileLength + offset;
- currentRange.end = fileLength - 1;
- } catch (NumberFormatException e) {
- return null;
- }
- } else {
-
- try {
- currentRange.start = Long.parseLong
- (rangeDefinition.substring(0, dashPos));
- if (dashPos < rangeDefinition.length() - 1)
- currentRange.end = Long.parseLong
- (rangeDefinition.substring
- (dashPos + 1, rangeDefinition.length()));
- else
- currentRange.end = fileLength - 1;
- } catch (NumberFormatException e) {
- return null;
- }
-
- }
- if (!currentRange.validate()) {
- return null;
- }
- result.add(currentRange);
- }
- return result;
- }
-
-
- /**
- * Parse the Content-Range header. Used with PUT or in response.
- *
- * @return Range
- */
- public static Range parseContentRange(String rangeHeader)
- throws IOException {
- if (rangeHeader == null)
- return null;
-
- // bytes is the only range unit supported
- if (!rangeHeader.startsWith("bytes")) {
- return null;
- }
-
- rangeHeader = rangeHeader.substring(6).trim();
-
- int dashPos = rangeHeader.indexOf('-');
- int slashPos = rangeHeader.indexOf('/');
-
- if (dashPos == -1) {
- return null;
- }
-
- if (slashPos == -1) {
- return null;
- }
-
- Range range = new Range();
-
- try {
- range.start = Long.parseLong(rangeHeader.substring(0, dashPos));
- range.end =
- Long.parseLong(rangeHeader.substring(dashPos + 1, slashPos));
- range.length = Long.parseLong
- (rangeHeader.substring(slashPos + 1, rangeHeader.length()));
- } catch (NumberFormatException e) {
- return null;
- }
-
- if (!range.validate()) {
- return null;
- }
-
- return range;
- }
-
-}
\ No newline at end of file
}
- /**
- * Decode and return the specified URL-encoded String.
- * When the byte array is converted to a string, the system default
- * character encoding is used... This may be different than some other
- * servers.
- *
- * @param str The url-encoded string
- *
- * @exception IllegalArgumentException if a '%' character is not followed
- * by a valid 2-digit hexadecimal number
- */
- public static String URLDecode(String str) {
-
- return URLDecode(str, null);
-
- }
-
-
- /**
- * Decode and return the specified URL-encoded String.
- *
- * @param str The url-encoded string
- * @param enc The encoding to use; if null, the default encoding is used
- * @exception IllegalArgumentException if a '%' character is not followed
- * by a valid 2-digit hexadecimal number
- */
- public static String URLDecode(String str, String enc) {
-
- if (str == null)
- return (null);
-
- // use the specified encoding to extract bytes out of the
- // given string so that the encoding is not lost. If an
- // encoding is not specified, let it use platform default
- byte[] bytes = null;
- try {
- if (enc == null) {
- bytes = str.getBytes();
- } else {
- bytes = str.getBytes(enc);
- }
- } catch (UnsupportedEncodingException uee) {}
- return URLDecode(bytes, enc);
-
- }
-
-
- /**
- * Decode and return the specified URL-encoded byte array.
- *
- * @param bytes The url-encoded byte array
- * @exception IllegalArgumentException if a '%' character is not followed
- * by a valid 2-digit hexadecimal number
- */
- public static String URLDecode(byte[] bytes) {
- return URLDecode(bytes, null);
- }
+
/**
* Decode and return the specified URL-encoded byte array.
*
* @param bytes The url-encoded byte array
- * @param enc The encoding to use; if null, the default encoding is used
* @exception IllegalArgumentException if a '%' character is not followed
* by a valid 2-digit hexadecimal number
*/
- public static String URLDecode(byte[] bytes, String enc) {
+// public static String URLDecode(byte[] bytes) {
+// return URLDecode(bytes, null);
+// }
- if (bytes == null)
- return (null);
-
- int len = bytes.length;
- int ix = 0;
- int ox = 0;
- while (ix < len) {
- byte b = bytes[ix++]; // Get byte to test
- if (b == '+') {
- b = (byte)' ';
- } else if (b == '%') {
- b = (byte) ((convertHexDigit(bytes[ix++]) << 4)
- + convertHexDigit(bytes[ix++]));
- }
- bytes[ox++] = b;
- }
- if (enc != null) {
- try {
- return new String(bytes, 0, ox, enc);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- return new String(bytes, 0, ox);
- }
/**
+++ /dev/null
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.tomcat.servlets.util;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.util.BitSet;
-
-/**
- *
- * This class is very similar to the java.net.URLEncoder class.
- *
- * Unfortunately, with java.net.URLEncoder there is no way to specify to the
- * java.net.URLEncoder which characters should NOT be encoded.
- *
- * This code was moved from DefaultServlet.java
- *
- * @author Craig R. McClanahan
- * @author Remy Maucherat
- */
-public class URLEncoder {
- protected static final char[] hexadecimal =
- {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
- 'A', 'B', 'C', 'D', 'E', 'F'};
-
- //Array containing the safe characters set.
- protected BitSet safeCharacters = new BitSet(256);
-
- public URLEncoder() {
- for (char i = 'a'; i <= 'z'; i++) {
- addSafeCharacter(i);
- }
- for (char i = 'A'; i <= 'Z'; i++) {
- addSafeCharacter(i);
- }
- for (char i = '0'; i <= '9'; i++) {
- addSafeCharacter(i);
- }
- }
-
- public void addSafeCharacter( char c ) {
- safeCharacters.set( c );
- }
-
- public String encode( String path ) {
- int maxBytesPerChar = 10;
- int caseDiff = ('a' - 'A');
- StringBuffer rewrittenPath = new StringBuffer(path.length());
- ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar);
- OutputStreamWriter writer = null;
- try {
- writer = new OutputStreamWriter(buf, "UTF8");
- } catch (Exception e) {
- e.printStackTrace();
- writer = new OutputStreamWriter(buf);
- }
-
- for (int i = 0; i < path.length(); i++) {
- int c = (int) path.charAt(i);
- if (safeCharacters.get(c)) {
- rewrittenPath.append((char)c);
- } else {
- // convert to external encoding before hex conversion
- try {
- writer.write((char)c);
- writer.flush();
- } catch(IOException e) {
- buf.reset();
- continue;
- }
- byte[] ba = buf.toByteArray();
- for (int j = 0; j < ba.length; j++) {
- // Converting each byte in the buffer
- byte toEncode = ba[j];
- rewrittenPath.append('%');
- int low = (int) (toEncode & 0x0f);
- int high = (int) ((toEncode & 0xf0) >> 4);
- rewrittenPath.append(hexadecimal[high]);
- rewrittenPath.append(hexadecimal[low]);
- }
- buf.reset();
- }
- }
- return rewrittenPath.toString();
- }
-}
+++ /dev/null
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.tomcat.servlets.util;
-
-public class UrlUtils {
-
- /** Used by webdav.
- *
- * Return a context-relative path, beginning with a "/", that represents
- * the canonical version of the specified path after ".." and "." elements
- * are resolved out. If the specified path attempts to go outside the
- * boundaries of the current context (i.e. too many ".." path elements
- * are present), return <code>null</code> instead.
- *
- * @param path Path to be normalized
- */
- public static String normalize(String path) {
-
- if (path == null)
- return null;
-
- // Create a place for the normalized path
- String normalized = path;
-
- if (normalized.equals("/."))
- return "/";
-
- // Normalize the slashes and add leading slash if necessary
- if (normalized.indexOf('\\') >= 0)
- normalized = normalized.replace('\\', '/');
-
- if (!normalized.startsWith("/"))
- normalized = "/" + normalized;
-
- // Resolve occurrences of "//" in the normalized path
- while (true) {
- int index = normalized.indexOf("//");
- if (index < 0)
- break;
- normalized = normalized.substring(0, index) +
- normalized.substring(index + 1);
- }
-
- // Resolve occurrences of "/./" in the normalized path
- while (true) {
- int index = normalized.indexOf("/./");
- if (index < 0)
- break;
- normalized = normalized.substring(0, index) +
- normalized.substring(index + 2);
- }
-
- // Resolve occurrences of "/../" in the normalized path
- while (true) {
- int index = normalized.indexOf("/../");
- if (index < 0)
- break;
- if (index == 0)
- return (null); // Trying to go outside our context
- int index2 = normalized.lastIndexOf('/', index - 1);
- normalized = normalized.substring(0, index2) +
- normalized.substring(index + 3);
- }
-
- // Return the normalized path that we have completed
- return (normalized);
- }
-
-}
tomcat.setPort(8885);
tomcat.setBaseDir("../../output/build/webapps");
- tomcat.addWebapp("/examples", "examples");
tomcat.addWebapp("/", "ROOT");
public void testSimple() throws IOException {
HttpConnector clientCon = DefaultHttpConnector.get();
HttpChannel ch = clientCon.get("localhost", 8885);
- ch.getRequest().setRequestURI("/examples/servlets/servlet/HelloWorldExample");
+ ch.getRequest().setRequestURI("/index.html");
ch.getRequest().send();
BBuffer res = ch.readAll(null, 0);
- assertTrue(res.toString().indexOf("<title>Hello World!</title>") >= 0);
+ assertTrue(res.toString(), res.toString().indexOf("<title>Apache Tomcat</title>") >= 0);
}
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
-import org.apache.tomcat.integration.jmx.JMXProxyServlet;
-import org.apache.tomcat.integration.jmx.JmxObjectManagerSpi;
import org.apache.tomcat.integration.jmx.UJmxHandler;
import org.apache.tomcat.integration.jmx.UJmxObjectManagerSpi;
import org.apache.tomcat.integration.simple.Main;
import org.apache.tomcat.lite.http.services.EchoCallback;
import org.apache.tomcat.lite.http.services.SleepCallback;
import org.apache.tomcat.lite.io.BBuffer;
+import org.apache.tomcat.lite.io.IOConnector;
import org.apache.tomcat.lite.io.SocketConnector;
import org.apache.tomcat.lite.io.SslConnector;
import org.apache.tomcat.lite.proxy.HttpProxyService;
import org.apache.tomcat.lite.proxy.StaticContentService;
import org.apache.tomcat.lite.service.IOStatus;
-import org.apache.tomcat.lite.servlet.ServletConfigImpl;
-import org.apache.tomcat.util.buf.ByteChunk;
/**
* Server with lost of test servlets.
private SimpleObjectManager om;
+ private boolean init = false;
+
private SocketConnector serverCon = new SocketConnector();
private HttpConnector testClient = DefaultHttpConnector.get();
private HttpConnector testServer = new HttpConnector(serverCon);
private HttpConnector testProxy = new HttpConnector(serverCon);
-
+ private HttpConnector sslServer;
+
private HttpProxyService proxy;
UJmxObjectManagerSpi jmx = new UJmxObjectManagerSpi();
+
+ private IOConnector sslCon;
public static TestMain shared() {
if (defaultServer == null) {
});
d.addWrapper(mCtx, "/ujmx", new UJmxHandler(jmx));
- d.addWrapper(mCtx, "/jmx",
- new ServletConfigImpl(new JMXProxyServlet()));
}
public void run() {
return 8443;
}
- protected void startAll(int basePort) throws IOException {
+ protected synchronized void startAll(int basePort) throws IOException {
int port = basePort + 903;
- if (proxy == null) {
-
+ if (!init) {
+ init = true;
+
proxy = new HttpProxyService()
.withHttpClient(testClient);
testProxy.setPort(port);
e.printStackTrace();
}
- SslConnector sslCon = new SslConnector()
+ sslCon = new SslConnector()
.setKeysResource("org/apache/tomcat/lite/http/test.keystore",
"changeit");
- HttpConnector sslServer = new HttpConnector(sslCon);
+ sslServer = new HttpConnector(sslCon);
initTestCallback(sslServer.getDispatcher());
sslServer.setPort(basePort + 443);
sslServer.start();
+
+// System.setProperty("javax.net.debug", "ssl");
-// testProxy.setDebugHttp(true);
-// testProxy.setDebug(true);
-// testClient.setDebug(true);
-// testClient.setDebugHttp(true);
+// Logger.getLogger("SSL").setLevel(Level.FINEST);
+// testProxy.setDebugHttp(true);
+// testProxy.setDebug(true);
+// testClient.setDebug(true);
+// testClient.setDebugHttp(true);
// testServer.setDebugHttp(true);
// testServer.setDebug(true);
// sslServer.setDebug(true);
public void bindConnector(HttpConnector con, final String base) {
om.bind("HttpConnector-" + base, con);
om.bind("HttpConnectionPool-" + base, con.cpool);
- SocketConnector sc = (SocketConnector) con.getIOConnector();
- om.bind("NioThread-" + base, sc.getSelector());
-
+ IOConnector io = con.getIOConnector();
+ int ioLevel = 0;
+ while (io != null) {
+ om.bind("IOConnector-" + (ioLevel++) + "-" + base, io);
+ if (io instanceof SocketConnector) {
+ om.bind("NioThread-" + base,
+ ((SocketConnector) io).getSelector());
+
+ }
+ io = io.getNet();
+ }
con.cpool.setEvents(new HttpConnectionPool.HttpConnectionPoolEvents() {
@Override
if (om == null) {
om = new SimpleObjectManager();
}
- // All objects visible in JMX via util.registry
- // ( optional dependency )
- om.register(new JmxObjectManagerSpi());
om.register(jmx);
-
+ // Additional settings, via spring-like config file
om.loadResource(cfgFile);
+
+ // initialization - using runnables
String run = (String) om.getProperty("RUN");
String[] runNames = run == null ? new String[] {} : run.split(",");
for (String name: runNames) {
bindConnector(testServer, "TestServer");
bindConnector(testClient, "Client");
bindConnector(testProxy, "Proxy");
+ bindConnector(sslServer, "Https");
}
return out;
}
- public static ByteChunk getUrl(String path) throws IOException {
- ByteChunk out = new ByteChunk();
+ public static BBuffer getUrl(String path) throws IOException {
+ BBuffer out = BBuffer.allocate();
getUrl(path, out);
return out;
}
public static HttpURLConnection getUrl(String path,
- ByteChunk out) throws IOException {
+ BBuffer out) throws IOException {
URL url = new URL(path);
HttpURLConnection connection =
(HttpURLConnection) url.openConnection();
package org.apache.tomcat.lite.http;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
import junit.framework.TestCase;
-import org.apache.commons.codec.binary.Base64;
import org.apache.tomcat.lite.TestMain;
import org.apache.tomcat.lite.io.BBuffer;
import org.apache.tomcat.lite.io.SslConnector;
-import org.apache.tomcat.util.buf.ByteChunk;
+import org.apache.tomcat.lite.util.Base64;
public class HttpsTest extends TestCase {
static int port = 8443;
final HttpConnector httpClient = TestMain.shared().getClient();
- public void setUp() {
- Logger.getLogger("SSL").setLevel(Level.FINEST);
- }
-
public void testSimpleClient() throws Exception {
checkResponse(httpClient);
}
-
public void testSimpleServer() throws Exception {
- ByteChunk res = TestMain.getUrl("https://localhost:8443/hello");
+ BBuffer res = TestMain.getUrl("https://localhost:8443/hello");
assertTrue(res.toString().indexOf("Hello") >= 0);
}
HttpRequest ch = httpCon.request("localhost", port).setSecure(true);
ch.setRequestURI("/hello");
- ch.setProtocol("HTTP/1.0");
+ ch.setProtocol("HTTP/1.0"); // to force close
ch.send();
BBuffer res = ch.readAll();
}
public void testSimpleClient20() throws Exception {
- for (int i = 0; i < 20; i++) {
+ for (int i = 0; i < 10; i++) {
checkResponse(httpClient);
}
}
TestMain.shared().initTestCallback(con.getDispatcher());
con.start();
- ByteChunk res = TestMain.getUrl("https://localhost:8444" +
+ BBuffer res = TestMain.getUrl("https://localhost:8444" +
"/hello");
assertTrue(res.toString().indexOf("Hello") >= 0);
// DefaultHttpConnector.get().setDebugHttp(true);
TestMain.getTestServer();
- httpClient = DefaultHttpConnector.get().request("localhost", clientPort);
+ httpClient = DefaultHttpConnector.get().request("localhost",
+ clientPort);
bodyRecvBuffer.recycle();
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import junit.framework.TestCase;
import org.apache.tomcat.lite.TestMain;
import org.apache.tomcat.lite.http.HttpConnectionPool.RemoteServer;
import org.apache.tomcat.lite.io.IOBuffer;
+import org.apache.tomcat.lite.io.SocketConnector;
public class SpdyTest extends TestCase {
HttpConnector http11Con = TestMain.shared().getClient();
- static HttpConnector spdyCon = DefaultHttpConnector.get();
+ static HttpConnector spdyCon =
+ new HttpConnector(new SocketConnector());
+
+ static HttpConnector spdyConSsl =
+ new HttpConnector(new SocketConnector());
HttpConnector memSpdyCon = new HttpConnector(null);
HttpRequest req =
spdyCon.request("http://localhost:8802/echo/test1");
+ // Force SPDY - no negotiation
+ req.setProtocol("SPDY/1.0");
+
+ HttpResponse res = req.waitResponse();
+
+ assertEquals(200, res.getStatus());
+ //assertEquals("", res.getHeader(""));
+
+ BufferedReader reader = res.getReader();
+ String line1 = reader.readLine();
+ //assertEquals("", line1);
+ }
+
+ public void testSslClient() throws IOException {
+
+ HttpRequest req =
+ spdyConSsl.request("http://localhost:8443/echo/test1");
+ // Enable SSL for the connection.
+ // TODO: this must be done on the first request, all will be
+ // encrypted.
+ req.setSecure(true);
+ // Force SPDY - no negotiation
+ req.setProtocol("SPDY/1.0");
+
HttpResponse res = req.waitResponse();
assertEquals(200, res.getStatus());
String line1 = reader.readLine();
//assertEquals("", line1);
}
+
// Initial frame generated by Chrome
public void testParse() throws IOException {
InputStream is =
- getClass().getClassLoader().getResourceAsStream("org/apache/tomcat/lite/http/spdyreq0");
+ getClass().getClassLoader().getResourceAsStream("org/apache/tomcat/lite/http/spdyreq0.bin");
IOBuffer iob = new IOBuffer();
iob.append(is);
// Initial frame generated by Chrome
public void testParseCompressed() throws IOException {
InputStream is =
- getClass().getClassLoader().getResourceAsStream("org/apache/tomcat/lite/http/spdyreqCompressed");
+ getClass().getClassLoader().getResourceAsStream("org/apache/tomcat/lite/http/spdyreqCompressed.bin");
IOBuffer iob = new IOBuffer();
iob.append(is);
package org.apache.tomcat.lite.load;
-import java.io.File;
import java.io.IOException;
-import java.lang.management.ManagementFactory;
import java.net.HttpURLConnection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
-import javax.management.InstanceNotFoundException;
-import javax.management.MBeanException;
-import javax.management.MBeanServer;
-import javax.management.MalformedObjectNameException;
-import javax.management.ObjectName;
-import javax.management.ReflectionException;
-
import junit.framework.TestCase;
import org.apache.tomcat.lite.TestMain;
import org.apache.tomcat.lite.http.HttpConnector;
import org.apache.tomcat.lite.http.HttpRequest;
import org.apache.tomcat.lite.http.HttpChannel.RequestCompleted;
+import org.apache.tomcat.lite.io.BBuffer;
import org.apache.tomcat.lite.io.SocketConnector;
-import org.apache.tomcat.util.buf.ByteChunk;
/*
Notes on memory use ( from heap dumps ):
new HttpConnector(new SocketConnector());
ThreadRunner tr;
- static MBeanServer server;
- static boolean dumpHeap = false;
+ static boolean dumpHeap = true;
AtomicInteger ok = new AtomicInteger();
- Object lock = new Object();
int reqCnt;
Map<HttpRequest, HttpRequest> active = new HashMap();
}
public void test1000Async() throws Exception {
- try {
- asyncRequest(10, 100, false, clientCon);
- } finally {
- dumpHeap("heapAsync.bin");
- }
+// try {
+ asyncRequest(10, 100, false, false, clientCon, "AsyncHttp");
+// } finally {
+// dumpHeap("heapAsync.bin");
+// }
}
public void test10000Async() throws Exception {
- try {
- asyncRequest(20, 500, false, clientCon);
- } finally {
- dumpHeap("heapAsyncBig.bin");
- }
+ asyncRequest(20, 500, false, false, clientCon, "AsyncHttp");
}
- public void test1000AsyncSpdy() throws Exception {
- try {
- asyncRequest(10, 100, true, spdyClient);
- } finally {
- dumpHeap("heapSpdy1000.bin");
- }
+ public void test1000AsyncSsl() throws Exception {
+ asyncRequest(20, 50, false, true, clientCon, "AsyncHttpSsl");
+ }
+
+ public void test10000AsyncSsl() throws Exception {
+ asyncRequest(20, 500, false, true, clientCon, "AsyncHttpSsl");
+ }
+
+ public void test1000AsyncSpdy() throws Exception {
+ asyncRequest(10, 100, true, false, spdyClient, "AsyncSpdy");
}
public void test10000AsyncSpdy() throws Exception {
- try {
- asyncRequest(20, 500, true, spdyClient);
- } finally {
- dumpHeap("heapSpdy10000.bin");
- }
+ asyncRequest(20, 500, true, false, spdyClient, "AsyncSpdy");
}
public void test1000AsyncSpdyComp() throws Exception {
- try {
- asyncRequest(10, 100, true, spdyClientCompress);
- } finally {
- dumpHeap("heapSpdy1000Comp.bin");
- }
-
+ asyncRequest(10, 100, true, false, spdyClientCompress, "AsyncSpdyComp");
}
public void test10000AsyncSpdyComp() throws Exception {
- try {
- asyncRequest(20, 500, true, spdyClientCompress);
- } finally {
- dumpHeap("heapSpdy10000.bin");
- }
+ asyncRequest(20, 500, true, false, spdyClientCompress, "AsyncSpdyComp");
}
- public void asyncRequest(int thr, int perthr,
- final boolean spdy, final HttpConnector clientCon) throws Exception {
+ public void test1000AsyncSpdySsl() throws Exception {
+ asyncRequest(10, 100, true, true, spdyClient, "AsyncSpdySsl");
+ }
+
+ public void test1000AsyncSpdyCompSsl() throws Exception {
+ asyncRequest(10, 100, true, true, spdyClientCompress, "AsyncSpdyCompSsl");
+ }
+
+ public void test10000AsyncSpdyCompSsl() throws Exception {
+ asyncRequest(20, 500, true, true, spdyClientCompress, "AsyncSpdyCompSsl");
+ }
+
+ Object thrlock = new Object();
+ Object lock = new Object();
+
+ public void asyncRequest(final int thr, int perthr,
+ final boolean spdy, final boolean ssl,
+ final HttpConnector clientCon, String test) throws Exception {
+ clientCon.getConnectionPool().clear();
reqCnt = thr * perthr;
long t0 = System.currentTimeMillis();
+
tr = new ThreadRunner(thr, perthr) {
public void makeRequest(int i) throws Exception {
- HttpRequest cstate = clientCon.request("localhost", 8802);
+ HttpRequest cstate = clientCon.request("localhost",
+ ssl ? 8443 : 8802);
synchronized (active) {
active.put(cstate, cstate);
}
// a negotiation.
cstate.setProtocol("SPDY/1.0");
}
+ if (ssl) {
+ cstate.setSecure(true);
+ }
cstate.requestURI().set("/hello");
cstate.setCompletedCallback(reqCallback);
// no body
cstate.getBody().close();
- // Send the request, wait response
- Thread.currentThread().sleep(20);
+
cstate.send();
+
+ while (active.size() >= thr) {
+ synchronized(thrlock) {
+ thrlock.wait();
+ }
+ }
}
};
tr.run();
- assertEquals(0, tr.errors.get());
synchronized (lock) {
if (ok.get() < reqCnt) {
lock.wait(reqCnt * 100);
}
}
+ long time = (System.currentTimeMillis() - t0);
+
+ System.err.println("====== " + test +
+ " threads: " + thr + ", req: " +
+ reqCnt + ", sendTime" + tr.time +
+ ", time: " + time +
+ ", connections: " + clientCon.getConnectionPool().getSocketCount() +
+ ", avg: " + (time / reqCnt));
+
assertEquals(reqCnt, ok.get());
- System.err.println(reqCnt + " Async requests: " + (System.currentTimeMillis() - t0));
+ assertEquals(0, tr.errors.get());
}
+
+ RequestCompleted reqCallback = new RequestCompleted() {
+ @Override
+ public void handle(HttpChannel data, Object extraData)
+ throws IOException {
+ String out = data.getIn().copyAll(null).toString();
+ if (200 != data.getResponse().getStatus()) {
+ System.err.println("Wrong status");
+ tr.errors.incrementAndGet();
+ } else if (!"Hello world".equals(out)) {
+ tr.errors.incrementAndGet();
+ System.err.println("bad result " + out);
+ }
+ synchronized (active) {
+ active.remove(data.getRequest());
+ }
+ synchronized (thrlock) {
+ thrlock.notify();
+ }
+ data.release();
+ int okres = ok.incrementAndGet();
+ if (okres >= reqCnt) {
+ synchronized (lock) {
+ lock.notify();
+ }
+ }
+ }
+ };
+
+
public void testURLRequest1000() throws Exception {
- urlRequest(10, 100);
+ urlRequest(10, 100, false, "HttpURLConnection");
}
public void xtestURLRequest10000() throws Exception {
- urlRequest(20, 500);
+ urlRequest(20, 500, false, "HttpURLConnection");
+
+ }
+
+ // I can't seem to get 1000 requests to all complete...
+ public void xtestURLRequestSsl100() throws Exception {
+ urlRequest(10, 10, true, "HttpURLConnectionSSL");
+ }
+
+ public void xtestURLRequestSsl10000() throws Exception {
+ urlRequest(20, 500, true, "HttpURLConnectionSSL");
}
/**
* HttpURLConnection client against lite.http server.
*/
- public void urlRequest(int thr, int cnt) throws Exception {
+ public void urlRequest(int thr, int cnt, final boolean ssl, String test)
+ throws Exception {
long t0 = System.currentTimeMillis();
public void makeRequest(int i) throws Exception {
try {
- ByteChunk out = new ByteChunk();
- HttpURLConnection con = TestMain.getUrl("http://localhost:8802/hello", out);
+ BBuffer out = BBuffer.allocate();
+ String url = ssl ? "https://localhost:8443/hello" :
+ "http://localhost:8802/hello";
+ HttpURLConnection con =
+ TestMain.getUrl(url, out);
if (con.getResponseCode() != 200) {
errors.incrementAndGet();
}
};
tr.run();
assertEquals(0, tr.errors.get());
+ long time = (System.currentTimeMillis() - t0);
- System.err.println(thr + " threads, " + (thr * cnt) + " total blocking URL requests: " +
- (System.currentTimeMillis() - t0));
-
- //assertEquals(testServer., actual)
+ System.err.println("====== " + test + " threads: " + thr + ", req: " +
+ (thr * cnt) + ", time: " + time + ", avg: " +
+ (time / (thr * cnt)));
} finally {
- dumpHeap("heapURLReq.bin");
+ //dumpHeap("heapURLReq.bin");
}
}
// TODO: move to a servlet
- private void dumpHeap(String file) throws InstanceNotFoundException,
- MBeanException, ReflectionException, MalformedObjectNameException {
- if (!dumpHeap) {
- return;
- }
- if (server == null) {
- server = ManagementFactory.getPlatformMBeanServer();
-
- }
- File f1 = new java.io.File(file);
- if (f1.exists()) {
- f1.delete();
- }
- server.invoke(new ObjectName("com.sun.management:type=HotSpotDiagnostic"),
- "dumpHeap",
- new Object[] {file, Boolean.FALSE /* live */},
- new String[] {String.class.getName(), "boolean"});
- }
-
-
- RequestCompleted reqCallback = new RequestCompleted() {
- @Override
- public void handle(HttpChannel data, Object extraData)
- throws IOException {
- String out = data.getIn().copyAll(null).toString();
- if (200 != data.getResponse().getStatus()) {
- System.err.println("Wrong status");
- tr.errors.incrementAndGet();
- }
- if (!"Hello world".equals(out)) {
- tr.errors.incrementAndGet();
- System.err.println("bad result " + out);
- }
- synchronized (active) {
- active.remove(data.getRequest());
- }
- data.release();
- int okres = ok.incrementAndGet();
- if (okres >= reqCnt) {
- synchronized (lock) {
- lock.notify();
- }
- }
- }
- };
}
mappingData.recycle();
mapper.map(host, uri, mappingData);
}
- System.out.println("Elapsed:" + (System.currentTimeMillis() - time));
+ // TODO: asserts
+ //System.out.println("Elapsed:" + (System.currentTimeMillis() - time));
}
}
}
long t1 = System.currentTimeMillis();
time = t1 - t0;
- System.err.println("TimeNB: " + (t1 - t0) + " " + res);
}
public void makeRequests(int cnt) {
-RUN=Log,Socks,TomcatLite
+RUN=JMX,Log,Socks,TomcatLite,JMXHandler
+
+JMX.(class)=org.apache.tomcat.integration.jmx.JmxObjectManagerSpi
+JMXHandler.(class)=org.apache.tomcat.integration.jmx.JmxHandler
Log.(class)=org.apache.tomcat.lite.service.LogConfig
Log.debug=org.apache.tomcat.lite.http.HttpConnector
--- /dev/null
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tomcat.lite.util;
+
+import junit.framework.TestCase;
+
+public class UEncoderTest extends TestCase {
+ URLEncoder enc=new URLEncoder();
+
+ /*
+ *
+ * Test method for 'org.apache.tomcat.util.buf.UEncoder.encodeURL(String)'
+ * TODO: find the relevant rfc and apache tests and add more
+ */
+ public void testEncodeURL() {
+
+ String eurl1=enc.encodeURL("test");
+ assertEquals("test", eurl1);
+
+ eurl1=enc.encodeURL("/test");
+ assertEquals("/test", eurl1);
+
+ // safe ranges
+ eurl1=enc.encodeURL("test$-_.");
+ assertEquals("test$-_.", eurl1);
+
+ eurl1=enc.encodeURL("test$-_.!*'(),");
+ assertEquals("test$-_.!*'(),", eurl1);
+
+ eurl1=enc.encodeURL("//test");
+ assertEquals("//test", eurl1);
+
+
+ }
+
+}
+++ /dev/null
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.tomcat.util.buf;
-
-import junit.framework.TestCase;
-
-public class UEncoderTest extends TestCase {
- UEncoder enc=new UEncoder();
-
- /*
- *
- * Test method for 'org.apache.tomcat.util.buf.UEncoder.encodeURL(String)'
- * TODO: find the relevant rfc and apache tests and add more
- */
- public void testEncodeURL() {
-
- String eurl1=enc.encodeURL("test");
- assertEquals("test", eurl1);
-
- eurl1=enc.encodeURL("/test");
- assertEquals("%2ftest", eurl1);
-
- // safe ranges
- eurl1=enc.encodeURL("test$-_.");
- assertEquals("test$-_.", eurl1);
-
- eurl1=enc.encodeURL("test$-_.!*'(),");
- assertEquals("test$-_.!*'(),", eurl1);
-
- eurl1=enc.encodeURL("//test");
- assertEquals("%2f%2ftest", eurl1);
-
-
- }
-
-}