Merge branch 'master' into release

This commit is contained in:
Jesse McConnell 2011-12-22 12:50:58 -06:00
commit 772cabf893
119 changed files with 6075 additions and 1363 deletions

View File

@ -0,0 +1,88 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<groupId>org.eclipse.jetty.aggregate</groupId>
<artifactId>jetty-aggregate-project</artifactId>
<version>7.6.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jetty-websocket</artifactId>
<name>Jetty :: Aggregate :: Websocket</name>
<build>
<sourceDirectory>${project.build.directory}/sources</sourceDirectory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>unpack-dependencies</id>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<includes>META-INF/**,org/eclipse/**</includes>
<excludes>**/MANIFEST.MF,javax/**,about.html</excludes>
<outputDirectory>${project.build.directory}/classes</outputDirectory>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
</configuration>
</execution>
<execution>
<id>unpack-source</id>
<phase>generate-sources</phase>
<goals>
<goal>unpack-dependencies</goal>
</goals>
<configuration>
<classifier>sources</classifier>
<includes>**/*</includes>
<excludes>META-INF/**</excludes>
<includeGroupIds>org.eclipse.jetty</includeGroupIds>
<outputDirectory>${project.build.directory}/sources</outputDirectory>
<overWriteReleases>true</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins
</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<id>package</id>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<archive>
<manifest>
</manifest>
<manifestEntries>
<mode>development</mode>
<url>http://eclipse.org/jetty</url>
<Built-By>${user.name}</Built-By>
<package>org.eclipse.jetty</package>
<Bundle-License>http://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk/NOTICE.txt</Bundle-License>
<Bundle-Name>Jetty HTTP Server</Bundle-Name>
</manifestEntries>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-websocket</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -35,6 +35,7 @@
<module>jetty-client</module>
<module>jetty-servlet</module>
<module>jetty-webapp</module>
<module>jetty-websocket</module>
<module>jetty-plus</module>
<module>jetty-all-server</module>
<module>jetty-all</module>

View File

@ -454,7 +454,10 @@ public class AnnotationParser
className = className.replace('.', '/')+".class";
URL resource = Loader.getResource(this.getClass(), className, false);
if (resource!= null)
scanClass(resource.openStream());
{
Resource r = Resource.newResource(resource);
scanClass(r.getInputStream());
}
}
}
}
@ -472,7 +475,10 @@ public class AnnotationParser
String nameAsResource = cz.getName().replace('.', '/')+".class";
URL resource = Loader.getResource(this.getClass(), nameAsResource, false);
if (resource!= null)
scanClass(resource.openStream());
{
Resource r = Resource.newResource(resource);
scanClass(r.getInputStream());
}
}
}
if (visitSuperClasses)
@ -501,7 +507,10 @@ public class AnnotationParser
s = s.replace('.', '/')+".class";
URL resource = Loader.getResource(this.getClass(), s, false);
if (resource!= null)
scanClass(resource.openStream());
{
Resource r = Resource.newResource(resource);
scanClass(r.getInputStream());
}
}
}
}
@ -525,7 +534,10 @@ public class AnnotationParser
if (name.endsWith(".class"))
{
if ((resolver == null)|| (!resolver.isExcluded(name) && (!isParsed(name) || resolver.shouldOverride(name))))
scanClass(res.getURL().openStream());
{
Resource r = Resource.newResource(res.getURL());
scanClass(r.getInputStream());
}
}
}

View File

@ -355,9 +355,8 @@ public abstract class AbstractHttpConnection extends AbstractConnection implemen
@Override
public String toString()
{
return String.format("%s@%x//%s,g=%s,p=%s",
getClass().getSimpleName(),
hashCode(),
return String.format("%s %s g=%s p=%s",
super.toString(),
_destination == null ? "?.?.?.?:??" : _destination.getAddress(),
_generator,
_parser);

View File

@ -43,6 +43,9 @@ public class Address
public Address(String host, int port)
{
if (host == null)
throw new IllegalArgumentException("Host is null");
this.host = host.trim();
this.port = port;
}

View File

@ -439,7 +439,9 @@ public class HttpDestination implements Dumpable
public void returnIdleConnection(AbstractHttpConnection connection)
{
connection.onIdleExpired();
// TODO work out the real idle time;
long idleForMs=connection!=null&&connection.getEndPoint()!=null?connection.getEndPoint().getMaxIdleTime():-1;
connection.onIdleExpired(idleForMs);
boolean startConnection = false;
synchronized (this)

View File

@ -437,9 +437,9 @@ class SelectConnector extends AbstractLifeCycle implements HttpClient.Connector,
_endp.setMaxIdleTime(timeMs);
}
public void onIdleExpired()
public void onIdleExpired(long idleForMs)
{
_endp.onIdleExpired();
_endp.onIdleExpired(idleForMs);
}
public void setCheckForIdle(boolean check)

View File

@ -1,8 +1,5 @@
package org.eclipse.jetty.client;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.lessThan;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
@ -17,19 +14,21 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSocket;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpParser;
import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.Buffers;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
@ -40,6 +39,7 @@ import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.OS;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.After;
import org.junit.Assert;
@ -48,10 +48,16 @@ import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.lessThan;
public class SslBytesServerTest extends SslBytesTest
{
private final AtomicInteger sslHandles = new AtomicInteger();
private final AtomicInteger sslFlushes = new AtomicInteger();
private final AtomicInteger httpParses = new AtomicInteger();
private final AtomicReference<EndPoint> serverEndPoint = new AtomicReference<EndPoint>();
private final int idleTimeout = 2000;
private ExecutorService threadPool;
private Server server;
private SSLContext sslContext;
@ -68,6 +74,7 @@ public class SslBytesServerTest extends SslBytesTest
@Override
protected SslConnection newSslConnection(AsyncEndPoint endPoint, SSLEngine engine)
{
serverEndPoint.set(endPoint);
return new SslConnection(engine, endPoint)
{
@Override
@ -76,6 +83,20 @@ public class SslBytesServerTest extends SslBytesTest
sslHandles.incrementAndGet();
return super.handle();
}
@Override
protected SslEndPoint newSslEndPoint()
{
return new SslEndPoint()
{
@Override
public int flush(Buffer buffer) throws IOException
{
sslFlushes.incrementAndGet();
return super.flush(buffer);
}
};
}
};
}
@ -100,6 +121,7 @@ public class SslBytesServerTest extends SslBytesTest
};
}
};
connector.setMaxIdleTime(idleTimeout);
// connector.setPort(5870);
connector.setPort(0);
@ -119,9 +141,15 @@ public class SslBytesServerTest extends SslBytesTest
if (contentLength != null)
{
int length = Integer.parseInt(contentLength);
ServletInputStream input = request.getInputStream();
ServletInputStream input = httpRequest.getInputStream();
ServletOutputStream output = httpResponse.getOutputStream();
byte[] buffer = new byte[32 * 1024];
for (int i = 0; i < length; ++i)
input.read();
{
int read = input.read(buffer);
if ("/echo".equals(target))
output.write(buffer, 0, read);
}
}
}
});
@ -199,6 +227,7 @@ public class SslBytesServerTest extends SslBytesTest
// Check that we did not spin
Assert.assertThat(sslHandles.get(), lessThan(20));
Assert.assertThat(sslFlushes.get(), lessThan(20));
Assert.assertThat(httpParses.get(), lessThan(50));
closeClient(client);
@ -225,10 +254,8 @@ public class SslBytesServerTest extends SslBytesTest
System.arraycopy(bytes, 0, chunk1, 0, chunk1.length);
byte[] chunk2 = new byte[bytes.length - chunk1.length];
System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length);
proxy.flushToServer(chunk1);
TimeUnit.MILLISECONDS.sleep(100);
proxy.flushToServer(chunk2);
TimeUnit.MILLISECONDS.sleep(100);
proxy.flushToServer(100, chunk1);
proxy.flushToServer(100, chunk2);
// Server Hello + Certificate + Server Done
record = proxy.readFromServer();
@ -241,10 +268,8 @@ public class SslBytesServerTest extends SslBytesTest
System.arraycopy(bytes, 0, chunk1, 0, chunk1.length);
chunk2 = new byte[bytes.length - chunk1.length];
System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length);
proxy.flushToServer(chunk1);
TimeUnit.MILLISECONDS.sleep(100);
proxy.flushToServer(chunk2);
TimeUnit.MILLISECONDS.sleep(100);
proxy.flushToServer(100, chunk1);
proxy.flushToServer(100, chunk2);
// Change Cipher Spec
record = proxy.readFromClient();
@ -253,10 +278,8 @@ public class SslBytesServerTest extends SslBytesTest
System.arraycopy(bytes, 0, chunk1, 0, chunk1.length);
chunk2 = new byte[bytes.length - chunk1.length];
System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length);
proxy.flushToServer(chunk1);
TimeUnit.MILLISECONDS.sleep(100);
proxy.flushToServer(chunk2);
TimeUnit.MILLISECONDS.sleep(100);
proxy.flushToServer(100, chunk1);
proxy.flushToServer(100, chunk2);
// Client Done
record = proxy.readFromClient();
@ -265,10 +288,8 @@ public class SslBytesServerTest extends SslBytesTest
System.arraycopy(bytes, 0, chunk1, 0, chunk1.length);
chunk2 = new byte[bytes.length - chunk1.length];
System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length);
proxy.flushToServer(chunk1);
TimeUnit.MILLISECONDS.sleep(100);
proxy.flushToServer(chunk2);
TimeUnit.MILLISECONDS.sleep(100);
proxy.flushToServer(100, chunk1);
proxy.flushToServer(100, chunk2);
// Change Cipher Spec
record = proxy.readFromServer();
@ -284,6 +305,7 @@ public class SslBytesServerTest extends SslBytesTest
// Check that we did not spin
Assert.assertThat(sslHandles.get(), lessThan(20));
Assert.assertThat(sslFlushes.get(), lessThan(20));
Assert.assertThat(httpParses.get(), lessThan(50));
client.close();
@ -295,10 +317,8 @@ public class SslBytesServerTest extends SslBytesTest
System.arraycopy(bytes, 0, chunk1, 0, chunk1.length);
chunk2 = new byte[bytes.length - chunk1.length];
System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length);
proxy.flushToServer(chunk1);
TimeUnit.MILLISECONDS.sleep(100);
proxy.flushToServer(chunk2);
TimeUnit.MILLISECONDS.sleep(100);
proxy.flushToServer(100, chunk1);
proxy.flushToServer(100, chunk2);
// Socket close
record = proxy.readFromClient();
Assert.assertNull(String.valueOf(record), record);
@ -358,6 +378,7 @@ public class SslBytesServerTest extends SslBytesTest
// Check that we did not spin
Assert.assertThat(sslHandles.get(), lessThan(20));
Assert.assertThat(sslFlushes.get(), lessThan(20));
Assert.assertThat(httpParses.get(), lessThan(50));
closeClient(client);
@ -380,10 +401,7 @@ public class SslBytesServerTest extends SslBytesTest
// Client Hello
TLSRecord record = proxy.readFromClient();
for (byte b : record.getBytes())
{
proxy.flushToServer(b);
TimeUnit.MILLISECONDS.sleep(50);
}
proxy.flushToServer(50, b);
// Server Hello + Certificate + Server Done
record = proxy.readFromServer();
@ -392,26 +410,17 @@ public class SslBytesServerTest extends SslBytesTest
// Client Key Exchange
record = proxy.readFromClient();
for (byte b : record.getBytes())
{
proxy.flushToServer(b);
TimeUnit.MILLISECONDS.sleep(50);
}
proxy.flushToServer(50, b);
// Change Cipher Spec
record = proxy.readFromClient();
for (byte b : record.getBytes())
{
proxy.flushToServer(b);
TimeUnit.MILLISECONDS.sleep(50);
}
proxy.flushToServer(50, b);
// Client Done
record = proxy.readFromClient();
for (byte b : record.getBytes())
{
proxy.flushToServer(b);
TimeUnit.MILLISECONDS.sleep(50);
}
proxy.flushToServer(50, b);
// Change Cipher Spec
record = proxy.readFromServer();
@ -440,10 +449,7 @@ public class SslBytesServerTest extends SslBytesTest
// Application data
record = proxy.readFromClient();
for (byte b : record.getBytes())
{
proxy.flushToServer(b);
TimeUnit.MILLISECONDS.sleep(50);
}
proxy.flushToServer(50, b);
Assert.assertNull(request.get(5, TimeUnit.SECONDS));
// Application data
@ -463,6 +469,7 @@ public class SslBytesServerTest extends SslBytesTest
// Check that we did not spin
Assert.assertThat(sslHandles.get(), lessThan(750));
Assert.assertThat(sslFlushes.get(), lessThan(750));
Assert.assertThat(httpParses.get(), lessThan(150));
client.close();
@ -470,10 +477,7 @@ public class SslBytesServerTest extends SslBytesTest
// Close Alert
record = proxy.readFromClient();
for (byte b : record.getBytes())
{
proxy.flushToServer(b);
TimeUnit.MILLISECONDS.sleep(50);
}
proxy.flushToServer(50, b);
// Socket close
record = proxy.readFromClient();
Assert.assertNull(String.valueOf(record), record);
@ -565,6 +569,21 @@ public class SslBytesServerTest extends SslBytesTest
@Test
public void testRequestWithCloseAlert() throws Exception
{
if ( !OS.IS_LINUX )
{
// currently we are ignoring this test on anything other then linux
//http://tools.ietf.org/html/rfc2246#section-7.2.1
// TODO (react to this portion which seems to allow win/mac behavior)
//It is required that the other party respond with a close_notify alert of its own
//and close down the connection immediately, discarding any pending writes. It is not
//required for the initiator of the close to wait for the responding
//close_notify alert before closing the read side of the connection.
return;
}
final SSLSocket client = newClient();
SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
@ -587,6 +606,7 @@ public class SslBytesServerTest extends SslBytesTest
// Application data
TLSRecord record = proxy.readFromClient();
Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType());
proxy.flushToServer(record);
Assert.assertNull(request.get(5, TimeUnit.SECONDS));
@ -594,6 +614,7 @@ public class SslBytesServerTest extends SslBytesTest
// Close Alert
record = proxy.readFromClient();
Assert.assertEquals(TLSRecord.Type.ALERT, record.getType());
proxy.flushToServer(record);
// Do not close the raw socket yet
@ -614,6 +635,7 @@ public class SslBytesServerTest extends SslBytesTest
// Check that we did not spin
Assert.assertThat(sslHandles.get(), lessThan(20));
Assert.assertThat(sslFlushes.get(), lessThan(20));
Assert.assertThat(httpParses.get(), lessThan(50));
// Socket close
@ -652,6 +674,7 @@ public class SslBytesServerTest extends SslBytesTest
// Application data
TLSRecord record = proxy.readFromClient();
Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType());
proxy.flushToServer(record);
Assert.assertNull(request.get(5, TimeUnit.SECONDS));
@ -670,6 +693,64 @@ public class SslBytesServerTest extends SslBytesTest
// Check that we did not spin
Assert.assertThat(sslHandles.get(), lessThan(20));
Assert.assertThat(sslFlushes.get(), lessThan(20));
Assert.assertThat(httpParses.get(), lessThan(50));
client.close();
}
@Test
public void testRequestWithBigContentWriteBlockedAndResetException() throws Exception
{
final SSLSocket client = newClient();
SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
client.startHandshake();
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
byte[] data = new byte[128 * 1024];
Arrays.fill(data, (byte)'X');
final String content = new String(data, "UTF-8");
Future<Object> request = threadPool.submit(new Callable<Object>()
{
public Object call() throws Exception
{
OutputStream clientOutput = client.getOutputStream();
clientOutput.write(("" +
"GET /echo HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Content-Length: " + content.length() + "\r\n" +
"\r\n" +
content).getBytes("UTF-8"));
clientOutput.flush();
return null;
}
});
// Nine TLSRecords will be generated for the request
for (int i = 0; i < 9; ++i)
{
// Application data
TLSRecord record = proxy.readFromClient();
Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType());
proxy.flushToServer(record, 0);
}
Assert.assertNull(request.get(5, TimeUnit.SECONDS));
// We asked the server to echo back the data we sent
// but we do not read it, thus causing a write interest
// on the server.
// However, we then simulate that the client resets the
// connection, and this will cause an exception in the
// server that is trying to write the data
proxy.resetServer();
// Wait a while to detect spinning
TimeUnit.SECONDS.sleep(1);
Assert.assertThat(sslHandles.get(), lessThan(20));
Assert.assertThat(sslFlushes.get(), lessThan(20));
Assert.assertThat(httpParses.get(), lessThan(50));
client.close();
@ -678,6 +759,20 @@ public class SslBytesServerTest extends SslBytesTest
@Test
public void testRequestWithCloseAlertWithSplitBoundary() throws Exception
{
if ( !OS.IS_LINUX )
{
// currently we are ignoring this test on anything other then linux
//http://tools.ietf.org/html/rfc2246#section-7.2.1
// TODO (react to this portion which seems to allow win/mac behavior)
//It is required that the other party respond with a close_notify alert of its own
//and close down the connection immediately, discarding any pending writes. It is not
//required for the initiator of the close to wait for the responding
//close_notify alert before closing the read side of the connection.
return;
}
final SSLSocket client = newClient();
SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
@ -713,13 +808,11 @@ public class SslBytesServerTest extends SslBytesTest
byte[] bytes = new byte[dataBytes.length + closeBytes.length / 2];
System.arraycopy(dataBytes, 0, bytes, 0, dataBytes.length);
System.arraycopy(closeBytes, 0, bytes, dataBytes.length, closeBytes.length / 2);
proxy.flushToServer(bytes);
TimeUnit.MILLISECONDS.sleep(100);
proxy.flushToServer(100, bytes);
bytes = new byte[closeBytes.length - closeBytes.length / 2];
System.arraycopy(closeBytes, closeBytes.length / 2, bytes, 0, bytes.length);
proxy.flushToServer(bytes);
proxy.flushToServer(100, bytes);
// Do not close the raw socket yet
@ -739,6 +832,7 @@ public class SslBytesServerTest extends SslBytesTest
// Check that we did not spin
Assert.assertThat(sslHandles.get(), lessThan(20));
Assert.assertThat(sslFlushes.get(), lessThan(20));
Assert.assertThat(httpParses.get(), lessThan(50));
// Socket close
@ -785,13 +879,11 @@ public class SslBytesServerTest extends SslBytesTest
Assert.assertNull(request.get(5, TimeUnit.SECONDS));
byte[] chunk1 = new byte[2 * record.getBytes().length / 3];
System.arraycopy(record.getBytes(), 0, chunk1, 0, chunk1.length);
proxy.flushToServer(chunk1);
TimeUnit.MILLISECONDS.sleep(100);
proxy.flushToServer(100, chunk1);
byte[] chunk2 = new byte[record.getBytes().length - chunk1.length];
System.arraycopy(record.getBytes(), chunk1.length, chunk2, 0, chunk2.length);
proxy.flushToServer(chunk2);
proxy.flushToServer(100, chunk2);
record = proxy.readFromServer();
Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType());
@ -809,6 +901,7 @@ public class SslBytesServerTest extends SslBytesTest
// Check that we did not spin
Assert.assertThat(sslHandles.get(), lessThan(20));
Assert.assertThat(sslFlushes.get(), lessThan(20));
Assert.assertThat(httpParses.get(), lessThan(50));
closeClient(client);
@ -855,14 +948,13 @@ public class SslBytesServerTest extends SslBytesTest
System.arraycopy(bytes, 0, chunk1, 0, chunk1.length);
byte[] chunk2 = new byte[bytes.length - chunk1.length];
System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length);
proxy.flushToServer(chunk1);
TimeUnit.MILLISECONDS.sleep(100);
proxy.flushToServer(chunk2);
TimeUnit.MILLISECONDS.sleep(100);
proxy.flushToServer(100, chunk1);
proxy.flushToServer(100, chunk2);
}
// Check that we did not spin
Assert.assertThat(sslHandles.get(), lessThan(20));
Assert.assertThat(sslFlushes.get(), lessThan(20));
Assert.assertThat(httpParses.get(), lessThan(150));
Assert.assertNull(request.get(5, TimeUnit.SECONDS));
@ -883,6 +975,7 @@ public class SslBytesServerTest extends SslBytesTest
// Check that we did not spin
Assert.assertThat(sslHandles.get(), lessThan(20));
Assert.assertThat(sslFlushes.get(), lessThan(20));
Assert.assertThat(httpParses.get(), lessThan(150));
closeClient(client);
@ -1013,6 +1106,7 @@ public class SslBytesServerTest extends SslBytesTest
// Check that we did not spin
Assert.assertThat(sslHandles.get(), lessThan(20));
Assert.assertThat(sslFlushes.get(), lessThan(20));
Assert.assertThat(httpParses.get(), lessThan(50));
closeClient(client);
@ -1068,10 +1162,8 @@ public class SslBytesServerTest extends SslBytesTest
System.arraycopy(bytes, 0, chunk1, 0, chunk1.length);
byte[] chunk2 = new byte[bytes.length - chunk1.length];
System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length);
proxy.flushToServer(chunk1);
TimeUnit.MILLISECONDS.sleep(100);
proxy.flushToServer(chunk2);
TimeUnit.MILLISECONDS.sleep(100);
proxy.flushToServer(100, chunk1);
proxy.flushToServer(100, chunk2);
// Renegotiation Handshake
record = proxy.readFromServer();
@ -1108,10 +1200,8 @@ public class SslBytesServerTest extends SslBytesTest
System.arraycopy(bytes, 0, chunk1, 0, chunk1.length);
chunk2 = new byte[bytes.length - chunk1.length];
System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length);
proxy.flushToServer(chunk1);
TimeUnit.MILLISECONDS.sleep(100);
proxy.flushToServer(chunk2);
TimeUnit.MILLISECONDS.sleep(100);
proxy.flushToServer(100, chunk1);
proxy.flushToServer(100, chunk2);
// Renegotiation Handshake
record = proxy.readFromClient();
@ -1121,8 +1211,7 @@ public class SslBytesServerTest extends SslBytesTest
System.arraycopy(bytes, 0, chunk1, 0, chunk1.length);
chunk2 = new byte[bytes.length - chunk1.length];
System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length);
proxy.flushToServer(chunk1);
TimeUnit.MILLISECONDS.sleep(100);
proxy.flushToServer(100, chunk1);
// Do not write the second chunk now, but merge it with content, see below
Assert.assertNull(renegotiation.get(5, TimeUnit.SECONDS));
@ -1146,7 +1235,7 @@ public class SslBytesServerTest extends SslBytesTest
byte[] mergedBytes = new byte[chunk2.length + dataBytes.length];
System.arraycopy(chunk2, 0, mergedBytes, 0, chunk2.length);
System.arraycopy(dataBytes, 0, mergedBytes, chunk2.length, dataBytes.length);
proxy.flushToServer(mergedBytes);
proxy.flushToServer(100, mergedBytes);
// Write the remaining 2 TLS records
for (int i = 0; i < 2; ++i)
{
@ -1176,11 +1265,66 @@ public class SslBytesServerTest extends SslBytesTest
// Check that we did not spin
Assert.assertThat(sslHandles.get(), lessThan(20));
Assert.assertThat(sslFlushes.get(), lessThan(20));
Assert.assertThat(httpParses.get(), lessThan(100));
closeClient(client);
}
@Test
public void testServerShutdownOutputClientDoesNotCloseServerCloses() throws Exception
{
final SSLSocket client = newClient();
final OutputStream clientOutput = client.getOutputStream();
SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow();
client.startHandshake();
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
byte[] data = new byte[3 * 1024];
Arrays.fill(data, (byte)'Y');
String content = new String(data, "UTF-8");
automaticProxyFlow = proxy.startAutomaticFlow();
clientOutput.write(("" +
"POST / HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Content-Type: text/plain\r\n" +
"Content-Length: " + content.length() + "\r\n" +
"Connection: close\r\n" +
"\r\n" +
content).getBytes("UTF-8"));
clientOutput.flush();
BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8"));
String line = reader.readLine();
Assert.assertNotNull(line);
Assert.assertTrue(line.startsWith("HTTP/1.1 200 "));
while ((line = reader.readLine()) != null)
{
if (line.trim().length() == 0)
break;
}
Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS));
// Check client is at EOF
Assert.assertEquals(-1,client.getInputStream().read());
// Client should close the socket, but let's hold it open.
// Check that we did not spin
TimeUnit.MILLISECONDS.sleep(100);
Assert.assertThat(sslHandles.get(), lessThan(20));
Assert.assertThat(sslFlushes.get(), lessThan(20));
Assert.assertThat(httpParses.get(), lessThan(50));
// The server has shutdown the output since the client sent a Connection: close
// but the client does not close, so the server must idle timeout the endPoint.
TimeUnit.MILLISECONDS.sleep(idleTimeout + idleTimeout/2);
Assert.assertFalse(serverEndPoint.get().isOpen());
}
private void assumeJavaVersionSupportsTLSRenegotiations()
{
// Due to a security bug, TLS renegotiations were disabled in JDK 1.6.0_19-21
@ -1203,7 +1347,7 @@ public class SslBytesServerTest extends SslBytesTest
return client;
}
private void closeClient(SSLSocket client) throws IOException
private void closeClient(SSLSocket client) throws Exception
{
client.close();
@ -1223,5 +1367,4 @@ public class SslBytesServerTest extends SslBytesTest
Assert.assertNull(String.valueOf(record), record);
proxy.flushToClient(record);
}
}

View File

@ -199,7 +199,12 @@ public abstract class SslBytesTest
return new TLSRecord(type, bytes);
}
public void flushToServer(TLSRecord record) throws IOException
public void flushToServer(TLSRecord record) throws Exception
{
flushToServer(record, 100);
}
public void flushToServer(TLSRecord record, long sleep) throws Exception
{
if (record == null)
{
@ -212,20 +217,22 @@ public abstract class SslBytesTest
}
else
{
flush(server, record.getBytes());
flush(sleep, server, record.getBytes());
}
}
public void flushToServer(byte... bytes) throws IOException
public void flushToServer(long sleep, byte... bytes) throws Exception
{
flush(server, bytes);
flush(sleep, server, bytes);
}
private void flush(Socket socket, byte... bytes) throws IOException
private void flush(long sleep, Socket socket, byte... bytes) throws Exception
{
OutputStream output = socket.getOutputStream();
output.write(bytes);
output.flush();
if (sleep > 0)
TimeUnit.MILLISECONDS.sleep(sleep);
}
public TLSRecord readFromServer() throws IOException
@ -235,7 +242,7 @@ public abstract class SslBytesTest
return record;
}
public void flushToClient(TLSRecord record) throws IOException
public void flushToClient(TLSRecord record) throws Exception
{
if (record == null)
{
@ -248,7 +255,7 @@ public abstract class SslBytesTest
}
else
{
flush(client, record.getBytes());
flush(0, client, record.getBytes());
}
}
@ -266,7 +273,7 @@ public abstract class SslBytesTest
{
while (true)
{
flushToServer(readFromClient());
flushToServer(readFromClient(), 0);
}
}
catch (InterruptedIOException x)
@ -313,6 +320,15 @@ public abstract class SslBytesTest
return latch.await(time, unit);
}
public void resetServer() throws IOException
{
// Calling setSoLinger(true, 0) causes close()
// to send a RST instead of a FIN, causing an
// exception to be thrown on the other end
server.setSoLinger(true, 0);
server.close();
}
public class AutomaticFlow
{
private final CountDownLatch stopLatch;

View File

@ -0,0 +1,420 @@
package org.eclipse.jetty.client;
import static org.hamcrest.Matchers.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.nio.channels.SocketChannel;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpParser;
import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.io.Buffers;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.nio.AsyncConnection;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.toolchain.test.IO;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
public class TimeoutTest
{
private static final Logger logger = Log.getLogger(TimeoutTest.class);
private final AtomicInteger httpParses = new AtomicInteger();
private final AtomicInteger httpRequests = new AtomicInteger();
private ExecutorService threadPool;
private Server server;
private int serverPort;
private final AtomicReference<EndPoint> serverEndPoint = new AtomicReference<EndPoint>();
@Before
public void init() throws Exception
{
threadPool = Executors.newCachedThreadPool();
server = new Server();
SelectChannelConnector connector = new SelectChannelConnector()
{
@Override
protected AsyncConnection newConnection(SocketChannel channel, final AsyncEndPoint endPoint)
{
serverEndPoint.set(endPoint);
return new org.eclipse.jetty.server.AsyncHttpConnection(this,endPoint,getServer())
{
@Override
protected HttpParser newHttpParser(Buffers requestBuffers, EndPoint endPoint, HttpParser.EventHandler requestHandler)
{
return new HttpParser(requestBuffers,endPoint,requestHandler)
{
@Override
public int parseNext() throws IOException
{
httpParses.incrementAndGet();
return super.parseNext();
}
};
}
};
}
};
connector.setMaxIdleTime(2000);
// connector.setPort(5870);
connector.setPort(0);
server.addConnector(connector);
server.setHandler(new AbstractHandler()
{
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException,
ServletException
{
httpRequests.incrementAndGet();
request.setHandled(true);
String contentLength = request.getHeader("Content-Length");
if (contentLength != null)
{
int length = Integer.parseInt(contentLength);
ServletInputStream input = request.getInputStream();
for (int i = 0; i < length; ++i)
input.read();
}
}
});
server.start();
serverPort = connector.getLocalPort();
httpRequests.set(0);
logger.debug(" => :{}",serverPort);
}
@After
public void destroy() throws Exception
{
if (server != null)
server.stop();
if (threadPool != null)
threadPool.shutdownNow();
}
private Socket newClient() throws IOException, InterruptedException
{
Socket client = new Socket("localhost",serverPort);
return client;
}
/**
* Test that performs a normal http POST request, with connection:close.
* Check that shutdownOutput is sufficient to close the server connection.
*/
@Test
public void testServerCloseClientDoesClose() throws Exception
{
// Log.getLogger("").setDebugEnabled(true);
final Socket client = newClient();
final OutputStream clientOutput = client.getOutputStream();
byte[] data = new byte[3 * 1024];
Arrays.fill(data,(byte)'Y');
String content = new String(data,"UTF-8");
// The request section
StringBuilder req = new StringBuilder();
req.append("POST / HTTP/1.1\r\n");
req.append("Host: localhost\r\n");
req.append("Content-Type: text/plain\r\n");
req.append("Content-Length: ").append(content.length()).append("\r\n");
req.append("Connection: close\r\n");
req.append("\r\n");
// and now, the POST content section.
req.append(content);
// Send request to server
clientOutput.write(req.toString().getBytes("UTF-8"));
clientOutput.flush();
InputStream in = null;
InputStreamReader isr = null;
BufferedReader reader = null;
try
{
in = client.getInputStream();
isr = new InputStreamReader(in);
reader = new BufferedReader(isr);
// Read the response header
String line = reader.readLine();
Assert.assertNotNull(line);
Assert.assertThat(line,startsWith("HTTP/1.1 200 "));
while ((line = reader.readLine()) != null)
{
if (line.trim().length() == 0)
{
break;
}
}
Assert.assertEquals("one request handled",1,httpRequests.get());
Assert.assertEquals("EOF received",-1,client.getInputStream().read());
// shutdown the output
client.shutdownOutput();
// Check that we did not spin
int httpParseCount = httpParses.get();
Assert.assertThat(httpParseCount,lessThan(50));
// Try to write another request (to prove that stream is closed)
try
{
clientOutput.write(req.toString().getBytes("UTF-8"));
clientOutput.flush();
Assert.fail("Should not have been able to send a second POST request (connection: close)");
}
catch(SocketException e)
{
}
Assert.assertEquals("one request handled",1,httpRequests.get());
}
finally
{
IO.close(reader);
IO.close(isr);
IO.close(in);
closeClient(client);
}
}
/**
* Test that performs a seemingly normal http POST request, but with
* a client that issues "connection: close", and then attempts to
* write a second POST request.
* <p>
* The connection should be closed by the server
*/
@Test
public void testServerCloseClientMoreDataSent() throws Exception
{
// Log.getLogger("").setDebugEnabled(true);
final Socket client = newClient();
final OutputStream clientOutput = client.getOutputStream();
byte[] data = new byte[3 * 1024];
Arrays.fill(data,(byte)'Y');
String content = new String(data,"UTF-8");
// The request section
StringBuilder req = new StringBuilder();
req.append("POST / HTTP/1.1\r\n");
req.append("Host: localhost\r\n");
req.append("Content-Type: text/plain\r\n");
req.append("Content-Length: ").append(content.length()).append("\r\n");
req.append("Connection: close\r\n");
req.append("\r\n");
// and now, the POST content section.
req.append(content);
// Send request to server
clientOutput.write(req.toString().getBytes("UTF-8"));
clientOutput.flush();
InputStream in = null;
InputStreamReader isr = null;
BufferedReader reader = null;
try
{
in = client.getInputStream();
isr = new InputStreamReader(in);
reader = new BufferedReader(isr);
// Read the response header
String line = reader.readLine();
Assert.assertNotNull(line);
Assert.assertThat(line,startsWith("HTTP/1.1 200 "));
while ((line = reader.readLine()) != null)
{
if (line.trim().length() == 0)
{
break;
}
}
Assert.assertEquals("EOF received",-1,client.getInputStream().read());
Assert.assertEquals("one request handled",1,httpRequests.get());
// Don't shutdown the output
// client.shutdownOutput();
// server side seeking EOF
Assert.assertTrue("is open",serverEndPoint.get().isOpen());
Assert.assertTrue("close sent",serverEndPoint.get().isOutputShutdown());
Assert.assertFalse("close not received",serverEndPoint.get().isInputShutdown());
// Check that we did not spin
TimeUnit.SECONDS.sleep(1);
int httpParseCount = httpParses.get();
Assert.assertThat(httpParseCount,lessThan(50));
// Write another request (which is ignored as the stream is closing), which causes real close.
clientOutput.write(req.toString().getBytes("UTF-8"));
clientOutput.flush();
// Check that we did not spin
TimeUnit.SECONDS.sleep(1);
httpParseCount = httpParses.get();
Assert.assertThat(httpParseCount,lessThan(50));
// server side is closed
Assert.assertFalse("is open",serverEndPoint.get().isOpen());
Assert.assertTrue("close sent",serverEndPoint.get().isOutputShutdown());
Assert.assertTrue("close not received",serverEndPoint.get().isInputShutdown());
Assert.assertEquals("one request handled",1,httpRequests.get());
}
finally
{
IO.close(reader);
IO.close(isr);
IO.close(in);
closeClient(client);
}
}
/**
* Test that performs a seemingly normal http POST request, but with
* a client that issues "connection: close", and then does not close
* the connection after reading the response.
* <p>
* The connection should be closed by the server after a timeout.
*/
@Test
public void testServerCloseClientDoesNotClose() throws Exception
{
// Log.getLogger("").setDebugEnabled(true);
final Socket client = newClient();
final OutputStream clientOutput = client.getOutputStream();
byte[] data = new byte[3 * 1024];
Arrays.fill(data,(byte)'Y');
String content = new String(data,"UTF-8");
// The request section
StringBuilder req = new StringBuilder();
req.append("POST / HTTP/1.1\r\n");
req.append("Host: localhost\r\n");
req.append("Content-Type: text/plain\r\n");
req.append("Content-Length: ").append(content.length()).append("\r\n");
req.append("Connection: close\r\n");
req.append("\r\n");
// and now, the POST content section.
req.append(content);
// Send request to server
clientOutput.write(req.toString().getBytes("UTF-8"));
clientOutput.flush();
InputStream in = null;
InputStreamReader isr = null;
BufferedReader reader = null;
try
{
in = client.getInputStream();
isr = new InputStreamReader(in);
reader = new BufferedReader(isr);
// Read the response header
String line = reader.readLine();
Assert.assertNotNull(line);
Assert.assertThat(line,startsWith("HTTP/1.1 200 "));
while ((line = reader.readLine()) != null)
{
if (line.trim().length() == 0)
{
break;
}
}
Assert.assertEquals("EOF received",-1,client.getInputStream().read());
Assert.assertEquals("one request handled",1,httpRequests.get());
// Don't shutdown the output
// client.shutdownOutput();
// server side seeking EOF
Assert.assertTrue("is open",serverEndPoint.get().isOpen());
Assert.assertTrue("close sent",serverEndPoint.get().isOutputShutdown());
Assert.assertFalse("close not received",serverEndPoint.get().isInputShutdown());
// Wait for the server idle timeout
TimeUnit.SECONDS.sleep(3);
int httpParseCount = httpParses.get();
Assert.assertThat(httpParseCount,lessThan(50));
// server side is closed
Assert.assertFalse("is open",serverEndPoint.get().isOpen());
Assert.assertTrue("close sent",serverEndPoint.get().isOutputShutdown());
Assert.assertTrue("close not received",serverEndPoint.get().isInputShutdown());
Assert.assertEquals("one request handled",1,httpRequests.get());
// client will eventually get broken pipe if it keeps writing
try
{
for (int i=0;i<1000;i++)
{
clientOutput.write(req.toString().getBytes("UTF-8"));
clientOutput.flush();
}
Assert.fail("Client should have seen a broken pipe");
}
catch(IOException e)
{
// expected broken pipe
}
}
finally
{
IO.close(reader);
IO.close(isr);
IO.close(in);
closeClient(client);
}
}
private void closeClient(Socket client) throws IOException
{
client.close();
}
}

View File

@ -21,7 +21,7 @@
# -Dorg.apache.jasper.compiler.disablejsr199=true
# -Dcom.sun.management.jmxremote
# -Dorg.eclipse.jetty.util.log.IGNORED=true
# -Dorg.eclipse.jetty.util.log.stderr.DEBUG=true
# -Dorg.eclipse.jetty.LEVEL=DEBUG
# -Dorg.eclipse.jetty.util.log.stderr.SOURCE=true
# -Xmx2000m
# -Xmn512m

View File

@ -1,4 +1,5 @@
package org.eclipse.jetty.http.spi;
//========================================================================
//Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
@ -12,19 +13,25 @@ package org.eclipse.jetty.http.spi;
//You may elect to redistribute this code under either of these licenses.
//========================================================================
import com.sun.net.httpserver.Authenticator;
import com.sun.net.httpserver.Authenticator.Result;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpPrincipal;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.ContextHandler;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.ContextHandler;
import com.sun.net.httpserver.Authenticator;
import com.sun.net.httpserver.Authenticator.Result;
import com.sun.net.httpserver.BasicAuthenticator;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpPrincipal;
/**
* Jetty handler that bridges requests to {@link HttpHandler}.
@ -36,7 +43,6 @@ public class HttpSpiContextHandler extends ContextHandler
private HttpHandler _httpHandler;
public HttpSpiContextHandler(HttpContext httpContext, HttpHandler httpHandler)
{
this._httpContext = httpContext;
@ -46,9 +52,20 @@ public class HttpSpiContextHandler extends ContextHandler
@Override
public void doScope(String target, Request baseRequest, HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException
{
if (!target.startsWith(getContextPath())) return;
if (!target.startsWith(getContextPath()))
{
return;
}
JettyHttpExchange jettyHttpExchange = new JettyHttpExchange(_httpContext, req, resp);
HttpExchange jettyHttpExchange;
if (baseRequest.isSecure())
{
jettyHttpExchange = new JettyHttpsExchange(_httpContext,req,resp);
}
else
{
jettyHttpExchange = new JettyHttpExchange(_httpContext,req,resp);
}
// TODO: add filters processing
@ -56,53 +73,67 @@ public class HttpSpiContextHandler extends ContextHandler
{
Authenticator auth = _httpContext.getAuthenticator();
if (auth != null)
handleAuthentication(resp, jettyHttpExchange, auth);
{
handleAuthentication(resp,jettyHttpExchange,auth);
}
else
{
_httpHandler.handle(jettyHttpExchange);
}
}
catch(Exception ex)
catch (Exception ex)
{
PrintWriter writer = new PrintWriter(jettyHttpExchange.getResponseBody());
resp.setStatus(500);
writer.println("<h2>HTTP ERROR: 500</h2>");
writer.println("<pre>INTERNAL_SERVER_ERROR</pre>");
writer.println("<p>RequestURI=" + req.getRequestURI() + "</p>");
writer.println("<pre>");
ex.printStackTrace(writer);
writer.println("</pre>");
writer.println("<p><i><small><a href=\"http://jetty.mortbay.org\">Powered by jetty://</a></small></i></p>");
writer.close();
}
finally
{
baseRequest.setHandled(true);
}
}
private void handleAuthentication(HttpServletResponse resp, JettyHttpExchange jettyHttpExchange, Authenticator auth) throws IOException
private void handleAuthentication(HttpServletResponse resp, HttpExchange httpExchange, Authenticator auth) throws IOException
{
Result result = auth.authenticate(jettyHttpExchange);
Result result = auth.authenticate(httpExchange);
if (result instanceof Authenticator.Failure)
{
int rc = ((Authenticator.Failure)result).getResponseCode();
for (Map.Entry<String,List<String>> header : httpExchange.getResponseHeaders().entrySet())
{
for (String value : header.getValue())
resp.addHeader(header.getKey(),value);
}
resp.sendError(rc);
}
else if (result instanceof Authenticator.Retry)
{
int rc = ((Authenticator.Retry)result).getResponseCode();
resp.sendError(rc);
for (Map.Entry<String,List<String>> header : httpExchange.getResponseHeaders().entrySet())
{
for (String value : header.getValue())
resp.addHeader(header.getKey(),value);
}
resp.setStatus(rc);
resp.flushBuffer();
}
else if (result instanceof Authenticator.Success)
{
HttpPrincipal principal = ((Authenticator.Success)result).getPrincipal();
jettyHttpExchange.setPrincipal(principal);
_httpHandler.handle(jettyHttpExchange);
((JettyExchange)httpExchange).setPrincipal(principal);
_httpHandler.handle(httpExchange);
}
}

View File

@ -0,0 +1,28 @@
// ========================================================================
// Copyright (c) 2009-2009 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.http.spi;
import com.sun.net.httpserver.HttpPrincipal;
/* ------------------------------------------------------------ */
/**
*/
public interface JettyExchange
{
HttpPrincipal getPrincipal();
void setPrincipal(HttpPrincipal principal);
}

View File

@ -1,227 +1,252 @@
package org.eclipse.jetty.http.spi;
//========================================================================
//Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//All rights reserved. This program and the accompanying materials
//are made available under the terms of the Eclipse Public License v1.0
//and Apache License v2.0 which accompanies this distribution.
//The Eclipse Public License is available at
//http://www.eclipse.org/legal/epl-v10.html
//The Apache License v2.0 is available at
//http://www.opensource.org/licenses/apache2.0.php
//You may elect to redistribute this code under either of these licenses.
//========================================================================
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpPrincipal;
/**
* Jetty implementation of {@link com.sun.net.httpserver.HttpExchange}
*/
public class JettyHttpExchange extends HttpExchange
{
private HttpContext _httpContext;
private HttpServletRequest _req;
private HttpServletResponse _resp;
private Headers _responseHeaders = new Headers();
private int _responseCode = 0;
private InputStream _is;
private OutputStream _os;
private HttpPrincipal _httpPrincipal;
public JettyHttpExchange(HttpContext jaxWsContext , HttpServletRequest req,
HttpServletResponse resp)
{
this._httpContext = jaxWsContext;
this._req = req;
this._resp = resp;
try
{
this._is = req.getInputStream();
this._os = resp.getOutputStream();
}
catch (IOException ex)
{
throw new RuntimeException(ex);
}
}
@Override
public Headers getRequestHeaders()
{
Headers headers = new Headers();
Enumeration<?> en = _req.getHeaderNames();
while (en.hasMoreElements())
{
String name = (String) en.nextElement();
Enumeration<?> en2 = _req.getHeaders(name);
while (en2.hasMoreElements())
{
String value = (String) en2.nextElement();
headers.add(name, value);
}
}
return headers;
}
@Override
public Headers getResponseHeaders()
{
return _responseHeaders;
}
@Override
public URI getRequestURI()
{
try
{
String uriAsString = _req.getRequestURI();
if (_req.getQueryString() != null)
uriAsString += "?" + _req.getQueryString();
return new URI(uriAsString);
}
catch (URISyntaxException ex)
{
throw new RuntimeException(ex);
}
}
@Override
public String getRequestMethod()
{
return _req.getMethod();
}
@Override
public HttpContext getHttpContext()
{
return _httpContext;
}
@Override
public void close()
{
try
{
_resp.getOutputStream().close();
}
catch (IOException ex)
{
throw new RuntimeException(ex);
}
}
@Override
public InputStream getRequestBody()
{
return _is;
}
@Override
public OutputStream getResponseBody()
{
return _os;
}
@Override
public void sendResponseHeaders(int rCode, long responseLength)
throws IOException
{
this._responseCode = rCode;
for (Map.Entry<String, List<String>> stringListEntry : _responseHeaders.entrySet())
{
String name = stringListEntry.getKey();
List<String> values = stringListEntry.getValue();
for (String value : values)
{
_resp.setHeader(name, value);
}
}
if (responseLength > 0)
_resp.setHeader("content-length", "" + responseLength);
_resp.setStatus(rCode);
}
@Override
public InetSocketAddress getRemoteAddress()
{
return new InetSocketAddress(_req.getRemoteAddr(), _req.getRemotePort());
}
@Override
public int getResponseCode()
{
return _responseCode;
}
@Override
public InetSocketAddress getLocalAddress()
{
return new InetSocketAddress(_req.getLocalAddr(), _req.getLocalPort());
}
@Override
public String getProtocol()
{
return _req.getProtocol();
}
@Override
public Object getAttribute(String name)
{
return _req.getAttribute(name);
}
@Override
public void setAttribute(String name, Object value)
{
_req.setAttribute(name, value);
}
@Override
public void setStreams(InputStream i, OutputStream o)
{
_is = i;
_os = o;
}
@Override
public HttpPrincipal getPrincipal()
{
return _httpPrincipal;
}
public void setPrincipal(HttpPrincipal principal)
{
this._httpPrincipal = principal;
}
}
// ========================================================================
// Copyright (c) 2009-2009 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.http.spi;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpPrincipal;
/* ------------------------------------------------------------ */
/**
*/
public class JettyHttpExchange extends HttpExchange implements JettyExchange
{
private JettyHttpExchangeDelegate _delegate;
public JettyHttpExchange(HttpContext jaxWsContext, HttpServletRequest req, HttpServletResponse resp)
{
super();
_delegate = new JettyHttpExchangeDelegate(jaxWsContext,req,resp);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#hashCode()
*/
@Override
public int hashCode()
{
return _delegate.hashCode();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getRequestHeaders()
*/
@Override
public Headers getRequestHeaders()
{
return _delegate.getRequestHeaders();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getResponseHeaders()
*/
@Override
public Headers getResponseHeaders()
{
return _delegate.getResponseHeaders();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getRequestURI()
*/
@Override
public URI getRequestURI()
{
return _delegate.getRequestURI();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getRequestMethod()
*/
@Override
public String getRequestMethod()
{
return _delegate.getRequestMethod();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getHttpContext()
*/
@Override
public HttpContext getHttpContext()
{
return _delegate.getHttpContext();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#close()
*/
@Override
public void close()
{
_delegate.close();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj)
{
return _delegate.equals(obj);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getRequestBody()
*/
@Override
public InputStream getRequestBody()
{
return _delegate.getRequestBody();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getResponseBody()
*/
@Override
public OutputStream getResponseBody()
{
return _delegate.getResponseBody();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#sendResponseHeaders(int, long)
*/
@Override
public void sendResponseHeaders(int rCode, long responseLength) throws IOException
{
_delegate.sendResponseHeaders(rCode,responseLength);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getRemoteAddress()
*/
@Override
public InetSocketAddress getRemoteAddress()
{
return _delegate.getRemoteAddress();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getResponseCode()
*/
@Override
public int getResponseCode()
{
return _delegate.getResponseCode();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getLocalAddress()
*/
@Override
public InetSocketAddress getLocalAddress()
{
return _delegate.getLocalAddress();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getProtocol()
*/
@Override
public String getProtocol()
{
return _delegate.getProtocol();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getAttribute(java.lang.String)
*/
@Override
public Object getAttribute(String name)
{
return _delegate.getAttribute(name);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#setAttribute(java.lang.String, java.lang.Object)
*/
@Override
public void setAttribute(String name, Object value)
{
_delegate.setAttribute(name,value);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#setStreams(java.io.InputStream, java.io.OutputStream)
*/
@Override
public void setStreams(InputStream i, OutputStream o)
{
_delegate.setStreams(i,o);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#getPrincipal()
*/
@Override
public HttpPrincipal getPrincipal()
{
return _delegate.getPrincipal();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#setPrincipal(com.sun.net.httpserver.HttpPrincipal)
*/
public void setPrincipal(HttpPrincipal principal)
{
_delegate.setPrincipal(principal);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.http.spi.JettyExchange#toString()
*/
@Override
public String toString()
{
return _delegate.toString();
}
}

View File

@ -0,0 +1,228 @@
package org.eclipse.jetty.http.spi;
//========================================================================
//Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//All rights reserved. This program and the accompanying materials
//are made available under the terms of the Eclipse Public License v1.0
//and Apache License v2.0 which accompanies this distribution.
//The Eclipse Public License is available at
//http://www.eclipse.org/legal/epl-v10.html
//The Apache License v2.0 is available at
//http://www.opensource.org/licenses/apache2.0.php
//You may elect to redistribute this code under either of these licenses.
//========================================================================
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpPrincipal;
/**
* Jetty implementation of {@link com.sun.net.httpserver.HttpExchange}
*/
public class JettyHttpExchangeDelegate extends HttpExchange
{
private HttpContext _httpContext;
private HttpServletRequest _req;
private HttpServletResponse _resp;
private Headers _responseHeaders = new Headers();
private int _responseCode = 0;
private InputStream _is;
private OutputStream _os;
private HttpPrincipal _httpPrincipal;
JettyHttpExchangeDelegate(HttpContext jaxWsContext, HttpServletRequest req, HttpServletResponse resp)
{
this._httpContext = jaxWsContext;
this._req = req;
this._resp = resp;
try
{
this._is = req.getInputStream();
this._os = resp.getOutputStream();
}
catch (IOException ex)
{
throw new RuntimeException(ex);
}
}
@Override
public Headers getRequestHeaders()
{
Headers headers = new Headers();
Enumeration<?> en = _req.getHeaderNames();
while (en.hasMoreElements())
{
String name = (String)en.nextElement();
Enumeration<?> en2 = _req.getHeaders(name);
while (en2.hasMoreElements())
{
String value = (String)en2.nextElement();
headers.add(name,value);
}
}
return headers;
}
@Override
public Headers getResponseHeaders()
{
return _responseHeaders;
}
@Override
public URI getRequestURI()
{
try
{
String uriAsString = _req.getRequestURI();
if (_req.getQueryString() != null)
{
uriAsString += "?" + _req.getQueryString();
}
return new URI(uriAsString);
}
catch (URISyntaxException ex)
{
throw new RuntimeException(ex);
}
}
@Override
public String getRequestMethod()
{
return _req.getMethod();
}
@Override
public HttpContext getHttpContext()
{
return _httpContext;
}
@Override
public void close()
{
try
{
_resp.getOutputStream().close();
}
catch (IOException ex)
{
throw new RuntimeException(ex);
}
}
@Override
public InputStream getRequestBody()
{
return _is;
}
@Override
public OutputStream getResponseBody()
{
return _os;
}
@Override
public void sendResponseHeaders(int rCode, long responseLength) throws IOException
{
this._responseCode = rCode;
for (Map.Entry<String, List<String>> stringListEntry : _responseHeaders.entrySet())
{
String name = stringListEntry.getKey();
List<String> values = stringListEntry.getValue();
for (String value : values)
{
_resp.setHeader(name,value);
}
}
if (responseLength > 0)
{
_resp.setHeader("content-length","" + responseLength);
}
_resp.setStatus(rCode);
}
@Override
public InetSocketAddress getRemoteAddress()
{
return new InetSocketAddress(_req.getRemoteAddr(),_req.getRemotePort());
}
@Override
public int getResponseCode()
{
return _responseCode;
}
@Override
public InetSocketAddress getLocalAddress()
{
return new InetSocketAddress(_req.getLocalAddr(),_req.getLocalPort());
}
@Override
public String getProtocol()
{
return _req.getProtocol();
}
@Override
public Object getAttribute(String name)
{
return _req.getAttribute(name);
}
@Override
public void setAttribute(String name, Object value)
{
_req.setAttribute(name,value);
}
@Override
public void setStreams(InputStream i, OutputStream o)
{
_is = i;
_os = o;
}
@Override
public HttpPrincipal getPrincipal()
{
return _httpPrincipal;
}
public void setPrincipal(HttpPrincipal principal)
{
this._httpPrincipal = principal;
}
}

View File

@ -0,0 +1,179 @@
// ========================================================================
// Copyright (c) 2009-2009 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.http.spi;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.URI;
import javax.net.ssl.SSLSession;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpPrincipal;
import com.sun.net.httpserver.HttpsExchange;
/* ------------------------------------------------------------ */
/**
*/
public class JettyHttpsExchange extends HttpsExchange implements JettyExchange
{
private JettyHttpExchangeDelegate _delegate;
public JettyHttpsExchange(HttpContext jaxWsContext, HttpServletRequest req, HttpServletResponse resp)
{
super();
_delegate = new JettyHttpExchangeDelegate(jaxWsContext,req,resp);
}
@Override
public int hashCode()
{
return _delegate.hashCode();
}
@Override
public Headers getRequestHeaders()
{
return _delegate.getRequestHeaders();
}
@Override
public Headers getResponseHeaders()
{
return _delegate.getResponseHeaders();
}
@Override
public URI getRequestURI()
{
return _delegate.getRequestURI();
}
@Override
public String getRequestMethod()
{
return _delegate.getRequestMethod();
}
@Override
public HttpContext getHttpContext()
{
return _delegate.getHttpContext();
}
@Override
public void close()
{
_delegate.close();
}
@Override
public boolean equals(Object obj)
{
return _delegate.equals(obj);
}
@Override
public InputStream getRequestBody()
{
return _delegate.getRequestBody();
}
@Override
public OutputStream getResponseBody()
{
return _delegate.getResponseBody();
}
@Override
public void sendResponseHeaders(int rCode, long responseLength) throws IOException
{
_delegate.sendResponseHeaders(rCode,responseLength);
}
@Override
public InetSocketAddress getRemoteAddress()
{
return _delegate.getRemoteAddress();
}
@Override
public int getResponseCode()
{
return _delegate.getResponseCode();
}
@Override
public InetSocketAddress getLocalAddress()
{
return _delegate.getLocalAddress();
}
@Override
public String getProtocol()
{
return _delegate.getProtocol();
}
@Override
public Object getAttribute(String name)
{
return _delegate.getAttribute(name);
}
@Override
public void setAttribute(String name, Object value)
{
_delegate.setAttribute(name,value);
}
@Override
public void setStreams(InputStream i, OutputStream o)
{
_delegate.setStreams(i,o);
}
@Override
public HttpPrincipal getPrincipal()
{
return _delegate.getPrincipal();
}
public void setPrincipal(HttpPrincipal principal)
{
_delegate.setPrincipal(principal);
}
@Override
public String toString()
{
return _delegate.toString();
}
/* ------------------------------------------------------------ */
/**
* @see com.sun.net.httpserver.HttpsExchange#getSSLSession()
*/
@Override
public SSLSession getSSLSession()
{
return null;
}
}

View File

@ -0,0 +1,71 @@
package org.eclipse.jetty.http.spi;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import com.sun.net.httpserver.BasicAuthenticator;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class TestSPIServer
{
public static void main(String[] args) throws Exception
{
String host="localhost";
int port = 8080;
HttpServer server = new JettyHttpServerProvider().createHttpServer(new
InetSocketAddress(host, port), 10);
server.start();
final HttpContext httpContext = server.createContext("/",
new HttpHandler()
{
@Override
public void handle(HttpExchange exchange) throws IOException
{
Headers responseHeaders = exchange.getResponseHeaders();
responseHeaders.set("Content-Type","text/plain");
exchange.sendResponseHeaders(200,0);
OutputStream responseBody = exchange.getResponseBody();
Headers requestHeaders = exchange.getRequestHeaders();
Set<String> keySet = requestHeaders.keySet();
Iterator<String> iter = keySet.iterator();
while (iter.hasNext())
{
String key = iter.next();
List values = requestHeaders.get(key);
String s = key + " = " + values.toString() + "\n";
responseBody.write(s.getBytes());
}
responseBody.close();
}
});
httpContext.setAuthenticator(new BasicAuthenticator("Test")
{
@Override
public boolean checkCredentials(String username, String password)
{
if ("username".equals(username) && password.equals("password"))
return true;
return false;
}
});
Thread.sleep(10000000);
}
}

View File

@ -968,8 +968,25 @@ public class HttpParser implements Parser
}
case STATE_SEEKING_EOF:
{
// Skip all data
{
// Close if there is more data than CRLF
if (_buffer.length()>2)
{
_state=STATE_END;
_endp.close();
}
else
{
// or if the data is not white space
while (_buffer.length()>0)
if (!Character.isWhitespace(_buffer.get()))
{
_state=STATE_END;
_endp.close();
_buffer.clear();
}
}
_buffer.clear();
break;
}

View File

@ -510,6 +510,36 @@ public class HttpParserTest
assertTrue(messageCompleted);
}
@Test
public void testSeekEOF() throws Exception
{
StringEndPoint io=new StringEndPoint();
io.setInput(
"HTTP/1.1 200 OK\015\012"
+ "Content-Length: 0\015\012"
+ "Connection: close\015\012"
+ "\015\012"
+ "\015\012" // extra CRLF ignored
+ "HTTP/1.1 400 OK\015\012"); // extra data causes close
ByteArrayBuffer buffer= new ByteArrayBuffer(4096);
SimpleBuffers buffers=new SimpleBuffers(buffer,null);
Handler handler = new Handler();
HttpParser parser= new HttpParser(buffers,io, handler);
parser.parse();
assertEquals("HTTP/1.1", f0);
assertEquals("200", f1);
assertEquals("OK", f2);
assertEquals(null,_content);
assertTrue(headerCompleted);
assertTrue(messageCompleted);
}
private String _content;
private String f0;
private String f1;

View File

@ -35,11 +35,11 @@ public abstract class AbstractConnection implements Connection
return _endp;
}
public void onIdleExpired()
public void onIdleExpired(long idleForMs)
{
try
{
LOG.debug("onIdleExpired {} {}",this,_endp);
LOG.debug("onIdleExpired {}ms {} {}",idleForMs,this,_endp);
if (_endp.isInputShutdown() || _endp.isOutputShutdown())
_endp.close();
else
@ -62,12 +62,6 @@ public abstract class AbstractConnection implements Connection
public String toString()
{
return String.format("%s@%x//%s:%d<->%s:%d",
getClass().getSimpleName(),
hashCode(),
_endp.getLocalAddr(),
_endp.getLocalPort(),
_endp.getRemoteAddr(),
_endp.getRemotePort());
return String.format("%s@%x", getClass().getSimpleName(), hashCode());
}
}

View File

@ -35,8 +35,9 @@ public interface AsyncEndPoint extends ConnectedEndPoint
/** Callback when idle.
* <p>An endpoint is idle if there has been no IO activity for
* {@link #getMaxIdleTime()} and {@link #isCheckForIdle()} is true.
* @param idleForMs TODO
*/
public void onIdleExpired();
public void onIdleExpired(long idleForMs);
/* ------------------------------------------------------------ */
/** Set if the endpoint should be checked for idleness

View File

@ -54,6 +54,7 @@ public interface Connection
/**
* Called when the connection idle timeout expires
* @param idleForMs TODO
*/
void onIdleExpired();
void onIdleExpired(long idleForMs);
}

View File

@ -35,7 +35,8 @@ public class UncheckedPrintWriter extends PrintWriter
private static final Logger LOG = Log.getLogger(UncheckedPrintWriter.class);
private boolean _autoFlush = false;
private boolean _throwUnchecked=true;
private IOException _ioException;
private boolean _isClosed = false;
/* ------------------------------------------------------------ */
/**
@ -102,38 +103,45 @@ public class UncheckedPrintWriter extends PrintWriter
this(new BufferedWriter(new OutputStreamWriter(out)),autoFlush);
}
/* ------------------------------------------------------------ */
private void setError(Throwable th)
public boolean checkError()
{
setError();
if (_throwUnchecked)
throw new RuntimeIOException(th);
LOG.debug(th);
}
/* ------------------------------------------------------------ */
/** Are unchecked exceptions thrown.
* @return True if {@link RuntimeIOException}s are thrown
*/
public boolean isUncheckedPrintWriter()
{
return _throwUnchecked;
}
/* ------------------------------------------------------------ */
/** Set if unchecked exceptions are thrown
* @param uncheckedPrintWriter True if {@link RuntimeIOException}s are to be thrown
*/
public void setUncheckedPrintWriter(boolean uncheckedPrintWriter)
{
_throwUnchecked = uncheckedPrintWriter;
return _ioException!=null || super.checkError();
}
/* ------------------------------------------------------------ */
private void setError(Throwable th)
{
super.setError();
if (th instanceof IOException)
_ioException=(IOException)th;
else
{
_ioException=new IOException(String.valueOf(th));
_ioException.initCause(th);
}
LOG.debug(th);
}
@Override
protected void setError()
{
setError(new IOException());
}
/* ------------------------------------------------------------ */
/** Check to make sure that the stream has not been closed */
private void isOpen() throws IOException
{
if (super.out == null)
{
if (_ioException!=null)
throw new RuntimeIOException(_ioException);
if (_isClosed)
throw new IOException("Stream closed");
}
@ -170,6 +178,7 @@ public class UncheckedPrintWriter extends PrintWriter
synchronized (lock)
{
out.close();
_isClosed = true;
}
}
catch (IOException ex)
@ -248,7 +257,7 @@ public class UncheckedPrintWriter extends PrintWriter
*/
@Override
public void write(char buf[])
{
{
this.write(buf,0,buf.length);
}

View File

@ -262,35 +262,41 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
{
_idleTimestamp=check?System.currentTimeMillis():0;
}
/* ------------------------------------------------------------ */
public boolean isCheckForIdle()
{
return _idleTimestamp!=0;
}
/* ------------------------------------------------------------ */
protected void notIdle()
{
if (_idleTimestamp!=0)
_idleTimestamp=System.currentTimeMillis();
}
/* ------------------------------------------------------------ */
public void checkIdleTimestamp(long now)
{
long idleTimestamp=_idleTimestamp;
if (!getChannel().isOpen() || idleTimestamp!=0 && _maxIdleTime>0 && now>(idleTimestamp+_maxIdleTime))
if (idleTimestamp!=0 && _maxIdleTime>0)
{
onIdleExpired();
_idleTimestamp=now;
long idleForMs=now-idleTimestamp;
if (idleForMs>_maxIdleTime)
{
onIdleExpired(idleForMs);
_idleTimestamp=now;
}
}
}
/* ------------------------------------------------------------ */
public void onIdleExpired()
public void onIdleExpired(long idleForMs)
{
_connection.onIdleExpired();
_connection.onIdleExpired(idleForMs);
}
/* ------------------------------------------------------------ */
@ -704,24 +710,43 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
@Override
public String toString()
{
synchronized(this)
// Do NOT use synchronized (this)
// because it's very easy to deadlock when debugging is enabled.
// We do a best effort to print the right toString() and that's it.
SelectionKey key = _key;
String keyString = "";
if (key != null)
{
return String.format("SCEP@%x{%s->%s,d=%b,open=%b,ishut=%b,oshut=%b,rb=%b,wb=%b,w=%b,i=%d%s%s%s}",
hashCode(),
_socket.getRemoteSocketAddress(),
_socket.getLocalSocketAddress(),
_dispatched,
isOpen(),
isInputShutdown(),
isOutputShutdown(),
_readBlocked,
_writeBlocked,
_writable,
_interestOps,
_key != null && _key.isValid() ? "" : "!",
_key != null && _key.isValid() && _key.isReadable() ? "r" : "",
_key != null && _key.isValid() && _key.isWritable() ? "w" : "");
if (key.isValid())
{
if (key.isReadable())
keyString += "r";
if (key.isWritable())
keyString += "w";
}
else
{
keyString += "!";
}
}
else
{
keyString += "-";
}
return String.format("SCEP@%x{l(%s)<->r(%s),d=%b,open=%b,ishut=%b,oshut=%b,rb=%b,wb=%b,w=%b,i=%d%s}-{%s}",
hashCode(),
_socket.getRemoteSocketAddress(),
_socket.getLocalSocketAddress(),
_dispatched,
isOpen(),
isInputShutdown(),
isOutputShutdown(),
_readBlocked,
_writeBlocked,
_writable,
_interestOps,
keyString,
_connection);
}
/* ------------------------------------------------------------ */

View File

@ -16,7 +16,6 @@ package org.eclipse.jetty.io.nio;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
@ -51,7 +50,7 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
private final SSLEngine _engine;
private final SSLSession _session;
private AsyncConnection _connection;
private final SslEndPoint _sslEndPoint = new SslEndPoint();
private final SslEndPoint _sslEndPoint;
private int _allocations;
private SslBuffers _buffers;
private NIOBuffer _inbound;
@ -94,6 +93,13 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
_engine=engine;
_session=_engine.getSession();
_aEndp=(AsyncEndPoint)endp;
_sslEndPoint = newSslEndPoint();
}
/* ------------------------------------------------------------ */
protected SslEndPoint newSslEndPoint()
{
return new SslEndPoint();
}
/* ------------------------------------------------------------ */
@ -191,9 +197,10 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
_connection=next;
progress=true;
}
// TODO: consider moving here hasProgressed() - it's only used in SSL
}
LOG.debug("{} handle {} progress=",_session,this, progress);
LOG.debug("{} handle {} progress={}", _session, this, progress);
}
}
finally
@ -238,16 +245,20 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
/* ------------------------------------------------------------ */
@Override
public void onIdleExpired()
public void onIdleExpired(long idleForMs)
{
try
{
_sslEndPoint.shutdownOutput();
LOG.debug("onIdleExpired {}ms on {}",idleForMs,this);
if (_endp.isOutputShutdown())
_sslEndPoint.close();
else
_sslEndPoint.shutdownOutput();
}
catch (IOException e)
{
LOG.warn(e);
super.onIdleExpired();
super.onIdleExpired(idleForMs);
}
}
@ -307,12 +318,15 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
if (_outbound.hasContent() && (flushed=_endp.flush(_outbound))>0)
progress = true;
}
catch (Exception e)
catch (IOException e)
{
LOG.debug(e.toString());
LOG.ignore(e);
_endp.close();
throw e;
}
finally
{
LOG.debug("{} {} {} filled={}/{} flushed={}/{}",_session,this,_engine.getHandshakeStatus(),filled,_inbound.length(),flushed,_outbound.length());
}
LOG.debug("{} {} {} filled={}/{} flushed={}/{}",_session,this,_engine.getHandshakeStatus(),filled,_inbound.length(),flushed,_outbound.length());
// handle the current hand share status
switch(_engine.getHandshakeStatus())
@ -322,13 +336,13 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
case NOT_HANDSHAKING:
{
// Try wrapping some application data
if (toFlush.hasContent() && _outbound.space()>0 && wrap(toFlush))
progress=true;
// Try unwrapping some application data
if (toFill.space()>0 && _inbound.hasContent() && unwrap(toFill))
progress=true;
// Try wrapping some application data
if (toFlush.hasContent() && _outbound.space()>0 && wrap(toFlush))
progress=true;
}
break;
@ -381,13 +395,17 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
}
// pass on ishut/oshut state
if (!_inbound.hasContent() && _endp.isInputShutdown())
if (_endp.isOpen() && _endp.isInputShutdown() && !_inbound.hasContent())
_engine.closeInbound();
if (!_outbound.hasContent() && _engine.isOutboundDone())
if (_endp.isOpen() && _engine.isOutboundDone() && !_outbound.hasContent())
_endp.shutdownOutput();
some_progress|=progress;
}
if (toFill.hasContent())
_aEndp.asyncDispatch();
}
finally
{
@ -430,7 +448,7 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
}
catch(SSLException e)
{
LOG.warn(_endp+":",e);
LOG.warn(String.valueOf(_endp), e);
_endp.close();
throw e;
}
@ -506,10 +524,8 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
}
catch(SSLException e)
{
LOG.warn(_endp+":"+e);
LOG.debug(e);
if (_endp.isOpen())
_endp.close();
LOG.warn(String.valueOf(_endp), e);
_endp.close();
throw e;
}
finally
@ -568,13 +584,10 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
return _sslEndPoint;
}
/* ------------------------------------------------------------ */
public String toString()
{
Buffer i=_inbound;
Buffer o=_outbound;
Buffer u=_unwrapBuf;
return super.toString()+"|"+_engine.getHandshakeStatus()+" i/u/o="+(i==null?0:i.length())+"/"+(u==null?0:u.length())+"/"+(o==null?0:o.length());
return String.format("%s %s", super.toString(), _sslEndPoint);
}
/* ------------------------------------------------------------ */
@ -585,6 +598,11 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
{
return _engine;
}
public AsyncEndPoint getEndpoint()
{
return _aEndp;
}
public void shutdownOutput() throws IOException
{
@ -631,7 +649,7 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
public int fill(Buffer buffer) throws IOException
{
int size=buffer.length();
process(buffer,null);
process(buffer, null);
int filled=buffer.length()-size;
@ -691,7 +709,7 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
public void flush() throws IOException
{
process(null,null);
process(null, null);
}
public void asyncDispatch()
@ -704,9 +722,9 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
_aEndp.scheduleWrite();
}
public void onIdleExpired()
public void onIdleExpired(long idleForMs)
{
_aEndp.onIdleExpired();
_aEndp.onIdleExpired(idleForMs);
}
public void setCheckForIdle(boolean check)
@ -796,20 +814,21 @@ public class SslConnection extends AbstractConnection implements AsyncConnection
public String toString()
{
int i;
int o;
int u;
synchronized(SslConnection.this)
{
i=_inbound==null?-1:_inbound.length();
o=_outbound==null?-1:_outbound.length();
u=_unwrapBuf==null?-1:_unwrapBuf.length();
}
return String.format("SSL:%s %s i/u/o=%d/%d/%d ishut=%b oshut=%b",
_endp,
// Do NOT use synchronized (SslConnection.this)
// because it's very easy to deadlock when debugging is enabled.
// We do a best effort to print the right toString() and that's it.
Buffer inbound = _inbound;
Buffer outbound = _outbound;
Buffer unwrap = _unwrapBuf;
int i = inbound == null? -1 : inbound.length();
int o = outbound == null ? -1 : outbound.length();
int u = unwrap == null ? -1 : unwrap.length();
return String.format("SSL %s i/o/u=%d/%d/%d ishut=%b oshut=%b {%s}",
_engine.getHandshakeStatus(),
i, u, o,
_ishut, _oshut);
i, o, u,
_ishut, _oshut,
_connection);
}
}
}

View File

@ -20,12 +20,16 @@ import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import junit.framework.Assert;
import org.eclipse.jetty.toolchain.test.OS;
import org.eclipse.jetty.util.IO;
import org.junit.Test;
@ -192,8 +196,87 @@ public class IOTest
}
catch(Exception e)
{
// Dang OSX!
System.err.println(e);
assertTrue(OS.IS_OSX);
}
}
@Test
public void testHalfCloseBadClient() throws Exception
{
ServerSocketChannel connector = ServerSocketChannel.open();
connector.socket().bind(null);
Socket client = SocketChannel.open(connector.socket().getLocalSocketAddress()).socket();
client.setSoTimeout(1000);
client.setSoLinger(false,-1);
Socket server = connector.accept().socket();
server.setSoTimeout(1000);
server.setSoLinger(false,-1);
// Write from client to server
client.getOutputStream().write(1);
// Server reads
assertEquals(1,server.getInputStream().read());
// Write from server to client with oshut
server.getOutputStream().write(1);
System.err.println("OSHUT "+server);
server.shutdownOutput();
try
{
// Client reads response
assertEquals(1,client.getInputStream().read());
// Client reads -1
assertEquals(-1,client.getInputStream().read());
assertFalse(client.isInputShutdown());
// Client can still write as we are half closed
client.getOutputStream().write(1);
// Server can still read
assertEquals(1,server.getInputStream().read());
// Server now closes
server.close();
// Client still reads -1 (not broken pipe !!)
assertEquals(-1,client.getInputStream().read());
assertFalse(client.isInputShutdown());
Thread.sleep(100);
// Client still reads -1 (not broken pipe !!)
assertEquals(-1,client.getInputStream().read());
assertFalse(client.isInputShutdown());
// Client can still write data even though server is closed???
client.getOutputStream().write(1);
Thread.sleep(100);
client.getOutputStream().write(1);
// Client eventually sees Broken Pipe
int i=0;
try
{
for (i=0;i<100000;i++)
client.getOutputStream().write(1);
Assert.fail();
}
catch (IOException e)
{
}
client.close();
}
catch (Exception e)
{
System.err.println("PLEASE INVESTIGATE:");
e.printStackTrace();
}
}

View File

@ -271,7 +271,8 @@ public class ServerInstanceWrapper {
try
{
// Execute a Jetty configuration file
is = jettyConfiguration.openStream();
Resource r = Resource.newResource(jettyConfiguration);
is = r.getInputStream();
XmlConfiguration config = new XmlConfiguration(is);
config.getIdMap().putAll(id_map);

View File

@ -376,7 +376,8 @@ public class WebBundleDeployerHelper implements IWebBundleDeployerHelper
URL contextURL = contributor.getEntry(contextFileRelativePath);
if (contextURL != null)
{
return registerContext(contributor,contextFileRelativePath,contextURL.openStream(),extraClasspath,overrideBundleInstallLocation,requireTldBundle,handler);
Resource r = Resource.newResource(contextURL);
return registerContext(contributor,contextFileRelativePath,r.getInputStream(),extraClasspath,overrideBundleInstallLocation,requireTldBundle,handler);
}
throw new IllegalArgumentException("Could not find the context " + "file " + contextFileRelativePath + " for the bundle "
+ contributor.getSymbolicName() + (overrideBundleInstallLocation != null?" using the install location " + overrideBundleInstallLocation:""));

View File

@ -21,6 +21,7 @@ import java.security.KeyStore;
import org.eclipse.jetty.policy.PolicyContext;
import org.eclipse.jetty.policy.PolicyException;
import org.eclipse.jetty.util.resource.Resource;
public class KeystoreEntry extends AbstractEntry
{
@ -49,7 +50,8 @@ public class KeystoreEntry extends AbstractEntry
keystore = KeyStore.getInstance( type );
URL keyStoreLocation = new URL ( url );
InputStream istream = keyStoreLocation.openStream();
Resource r = Resource.newResource(keyStoreLocation);
InputStream istream = r.getInputStream();
keystore.load( istream, null );

View File

@ -16,7 +16,6 @@ package org.eclipse.jetty.server;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
@ -356,7 +355,27 @@ public abstract class AbstractHttpConnection extends AbstractConnection
if (_writer==null)
{
_writer=new OutputWriter();
_printWriter=new UncheckedPrintWriter(_writer);
if (_server.isUncheckedPrintWriter())
_printWriter=new UncheckedPrintWriter(_writer);
else
_printWriter = new PrintWriter(_writer)
{
public void close()
{
synchronized (lock)
{
try
{
out.close();
}
catch (IOException e)
{
setError();
}
}
}
};
}
_writer.setCharacterEncoding(encoding);
return _printWriter;

View File

@ -203,6 +203,25 @@ public class AsyncContinuation implements AsyncContext, Continuation
}
}
}
/* ------------------------------------------------------------ */
public boolean isDispatchable()
{
synchronized(this)
{
switch(_state)
{
case __REDISPATCH:
case __REDISPATCHED:
case __REDISPATCHING:
case __COMPLETING:
return true;
default:
return false;
}
}
}
/* ------------------------------------------------------------ */
@Override

View File

@ -54,6 +54,10 @@ public class AsyncHttpConnection extends AbstractHttpConnection implements Async
try
{
setCurrentConnection(this);
// don't check for idle while dispatched (unless blocking IO is done).
_asyncEndp.setCheckForIdle(false);
// While progress and the connection has not changed
while (progress && connection==this)
@ -62,8 +66,11 @@ public class AsyncHttpConnection extends AbstractHttpConnection implements Async
try
{
// Handle resumed request
if (_request._async.isAsync() && !_request._async.isComplete())
handleRequest();
if (_request._async.isAsync())
{
if (_request._async.isDispatchable())
handleRequest();
}
// else Parse more input
else if (!_parser.isComplete() && _parser.parseAvailable())
progress=true;
@ -130,10 +137,16 @@ public class AsyncHttpConnection extends AbstractHttpConnection implements Async
finally
{
setCurrentConnection(null);
// If we are not suspended
if (!_request.isAsyncStarted())
{
// return buffers
_parser.returnBuffers();
_generator.returnBuffers();
// resuming checking for idle
_asyncEndp.setCheckForIdle(true);
}
// Safety net to catch spinning

View File

@ -4,11 +4,11 @@
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.server;
@ -16,7 +16,6 @@ package org.eclipse.jetty.server;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Enumeration;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -56,14 +55,14 @@ public class Server extends HandlerWrapper implements Attributes
private static final String __version;
static
{
if (Server.class.getPackage()!=null &&
if (Server.class.getPackage()!=null &&
"Eclipse.org - Jetty".equals(Server.class.getPackage().getImplementationVendor()) &&
Server.class.getPackage().getImplementationVersion()!=null)
__version=Server.class.getPackage().getImplementationVersion();
else
__version=System.getProperty("jetty.version","7.x.y-SNAPSHOT");
}
private final Container _container=new Container();
private final AttributesMap _attributes = new AttributesMap();
private ThreadPool _threadPool;
@ -76,14 +75,15 @@ public class Server extends HandlerWrapper implements Attributes
private int _maxCookieVersion=1;
private boolean _dumpAfterStart=false;
private boolean _dumpBeforeStop=false;
private boolean _uncheckedPrintWriter=false;
/* ------------------------------------------------------------ */
public Server()
{
setServer(this);
setServer(this);
}
/* ------------------------------------------------------------ */
/** Convenience constructor
* Creates server and a {@link SelectChannelConnector} at the passed port.
@ -96,7 +96,7 @@ public class Server extends HandlerWrapper implements Attributes
connector.setPort(port);
setConnectors(new Connector[]{connector});
}
/* ------------------------------------------------------------ */
/** Convenience constructor
* Creates server and a {@link SelectChannelConnector} at the passed address.
@ -117,7 +117,7 @@ public class Server extends HandlerWrapper implements Attributes
{
return __version;
}
/* ------------------------------------------------------------ */
/**
* @return Returns the container.
@ -132,7 +132,7 @@ public class Server extends HandlerWrapper implements Attributes
{
return _stopAtShutdown;
}
/* ------------------------------------------------------------ */
public void setStopAtShutdown(boolean stop)
{
@ -142,7 +142,7 @@ public class Server extends HandlerWrapper implements Attributes
else
ShutdownThread.deregister(this);
}
/* ------------------------------------------------------------ */
/**
* @return Returns the connectors.
@ -160,7 +160,7 @@ public class Server extends HandlerWrapper implements Attributes
/* ------------------------------------------------------------ */
/**
* Conveniance method which calls {@link #getConnectors()} and {@link #setConnectors(Connector[])} to
* Conveniance method which calls {@link #getConnectors()} and {@link #setConnectors(Connector[])} to
* remove a connector.
* @param connector The connector to remove.
*/
@ -180,7 +180,7 @@ public class Server extends HandlerWrapper implements Attributes
for (int i=0;i<connectors.length;i++)
connectors[i].setServer(this);
}
_container.update(this, _connectors, connectors, "connector");
_connectors = connectors;
}
@ -193,7 +193,7 @@ public class Server extends HandlerWrapper implements Attributes
{
return _threadPool;
}
/* ------------------------------------------------------------ */
/**
* @param threadPool The threadPool to set.
@ -239,32 +239,32 @@ public class Server extends HandlerWrapper implements Attributes
{
_dumpBeforeStop = dumpBeforeStop;
}
/* ------------------------------------------------------------ */
@Override
protected void doStart() throws Exception
{
if (getStopAtShutdown())
ShutdownThread.register(this);
LOG.info("jetty-"+__version);
HttpGenerator.setServerVersion(__version);
MultiException mex=new MultiException();
if (_threadPool==null)
setThreadPool(new QueuedThreadPool());
try
{
super.doStart();
}
catch(Throwable e)
{
try
{
super.doStart();
}
catch(Throwable e)
{
mex.add(e);
}
if (_connectors!=null)
{
for (int i=0;i<_connectors.length;i++)
@ -276,10 +276,10 @@ public class Server extends HandlerWrapper implements Attributes
}
}
}
if (isDumpAfterStart())
dumpStdErr();
mex.ifExceptionThrow();
}
@ -289,9 +289,9 @@ public class Server extends HandlerWrapper implements Attributes
{
if (isDumpBeforeStop())
dumpStdErr();
MultiException mex=new MultiException();
if (_graceful>0)
{
if (_connectors!=null)
@ -302,7 +302,7 @@ public class Server extends HandlerWrapper implements Attributes
try{_connectors[i].close();}catch(Throwable e){mex.add(e);}
}
}
Handler[] contexts = getChildHandlersByClass(Graceful.class);
for (int c=0;c<contexts.length;c++)
{
@ -312,7 +312,7 @@ public class Server extends HandlerWrapper implements Attributes
}
Thread.sleep(_graceful);
}
if (_connectors!=null)
{
for (int i=_connectors.length;i-->0;)
@ -320,7 +320,7 @@ public class Server extends HandlerWrapper implements Attributes
}
try {super.doStop(); } catch(Throwable e) { mex.add(e);}
mex.ifExceptionThrow();
if (getStopAtShutdown())
@ -338,7 +338,7 @@ public class Server extends HandlerWrapper implements Attributes
final String target=connection.getRequest().getPathInfo();
final Request request=connection.getRequest();
final Response response=connection.getResponse();
if (LOG.isDebugEnabled())
{
LOG.debug("REQUEST "+target+" on "+connection);
@ -348,7 +348,7 @@ public class Server extends HandlerWrapper implements Attributes
else
handle(target, request, request, response);
}
/* ------------------------------------------------------------ */
/* Handle a request from a connection.
* Called to handle a request on the connection when either the header has been received,
@ -377,7 +377,7 @@ public class Server extends HandlerWrapper implements Attributes
baseRequest.setRequestURI(null);
baseRequest.setPathInfo(baseRequest.getRequestURI());
if (uri.getQuery()!=null)
baseRequest.mergeQueryString(uri.getQuery());
baseRequest.mergeQueryString(uri.getQuery());
}
final String target=baseRequest.getPathInfo();
@ -394,10 +394,10 @@ public class Server extends HandlerWrapper implements Attributes
handle(target, baseRequest, request, response);
}
/* ------------------------------------------------------------ */
public void join() throws InterruptedException
public void join() throws InterruptedException
{
getThreadPool().join();
}
@ -489,8 +489,8 @@ public class Server extends HandlerWrapper implements Attributes
/**
* Add an associated bean.
* The bean will be added to the servers {@link Container}
* and if it is a {@link LifeCycle} instance, it will be
* started/stopped along with the Server. Any beans that are also
* and if it is a {@link LifeCycle} instance, it will be
* started/stopped along with the Server. Any beans that are also
* {@link Destroyable}, will be destroyed with the server.
* @param o the bean object to add
*/
@ -506,7 +506,7 @@ public class Server extends HandlerWrapper implements Attributes
}
/**
* Remove a LifeCycle object to be started/stopped
* Remove a LifeCycle object to be started/stopped
* along with the Server
* @deprecated Use {@link #removeBean(Object)}
*/
@ -515,7 +515,7 @@ public class Server extends HandlerWrapper implements Attributes
{
removeBean(c);
}
/* ------------------------------------------------------------ */
/**
* Remove an associated bean.
@ -532,7 +532,7 @@ public class Server extends HandlerWrapper implements Attributes
}
/* ------------------------------------------------------------ */
/*
/*
* @see org.eclipse.util.AttributesMap#clearAttributes()
*/
public void clearAttributes()
@ -541,7 +541,7 @@ public class Server extends HandlerWrapper implements Attributes
}
/* ------------------------------------------------------------ */
/*
/*
* @see org.eclipse.util.AttributesMap#getAttribute(java.lang.String)
*/
public Object getAttribute(String name)
@ -550,7 +550,7 @@ public class Server extends HandlerWrapper implements Attributes
}
/* ------------------------------------------------------------ */
/*
/*
* @see org.eclipse.util.AttributesMap#getAttributeNames()
*/
public Enumeration getAttributeNames()
@ -559,7 +559,7 @@ public class Server extends HandlerWrapper implements Attributes
}
/* ------------------------------------------------------------ */
/*
/*
* @see org.eclipse.util.AttributesMap#removeAttribute(java.lang.String)
*/
public void removeAttribute(String name)
@ -568,7 +568,7 @@ public class Server extends HandlerWrapper implements Attributes
}
/* ------------------------------------------------------------ */
/*
/*
* @see org.eclipse.util.AttributesMap#setAttribute(java.lang.String, java.lang.Object)
*/
public void setAttribute(String name, Object attribute)
@ -587,13 +587,13 @@ public class Server extends HandlerWrapper implements Attributes
/* ------------------------------------------------------------ */
/**
* Set graceful shutdown timeout. If set, the internal <code>doStop()</code> method will not immediately stop the
* Set graceful shutdown timeout. If set, the internal <code>doStop()</code> method will not immediately stop the
* server. Instead, all {@link Connector}s will be closed so that new connections will not be accepted
* and all handlers that implement {@link Graceful} will be put into the shutdown mode so that no new requests
* will be accepted, but existing requests can complete. The server will then wait the configured timeout
* will be accepted, but existing requests can complete. The server will then wait the configured timeout
* before stopping.
* @param timeoutMS the milliseconds to wait for existing request to complete before stopping the server.
*
*
*/
public void setGracefulShutdown(int timeoutMS)
{
@ -612,9 +612,23 @@ public class Server extends HandlerWrapper implements Attributes
public void dump(Appendable out,String indent) throws IOException
{
dumpThis(out);
dump(out,indent,TypeUtil.asList(getHandlers()),getBeans(),TypeUtil.asList(_connectors));
dump(out,indent,TypeUtil.asList(getHandlers()),getBeans(),TypeUtil.asList(_connectors));
}
/* ------------------------------------------------------------ */
public boolean isUncheckedPrintWriter()
{
return _uncheckedPrintWriter;
}
/* ------------------------------------------------------------ */
public void setUncheckedPrintWriter(boolean unchecked)
{
_uncheckedPrintWriter=unchecked;
}
/* ------------------------------------------------------------ */
/* A handler that can be gracefully shutdown.
* Called by doStop if a {@link #setGracefulShutdown} period is set.

View File

@ -667,7 +667,7 @@ public class ConnectHandler extends HandlerWrapper
_endPoint.shutdownOutput();
}
public void onIdleExpired()
public void onIdleExpired(long idleForMs)
{
try
{
@ -834,7 +834,7 @@ public class ConnectHandler extends HandlerWrapper
_endPoint.shutdownOutput();
}
public void onIdleExpired()
public void onIdleExpired(long idleForMs)
{
try
{

View File

@ -30,7 +30,6 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.servlet.RequestDispatcher;
import javax.servlet.Servlet;
import javax.servlet.ServletContext;
@ -70,13 +69,13 @@ import org.eclipse.jetty.util.resource.Resource;
/* ------------------------------------------------------------ */
/**
* ContextHandler.
*
*
* This handler wraps a call to handle by setting the context and servlet path, plus setting the context classloader.
*
*
* <p>
* If the context init parameter "org.eclipse.jetty.server.context.ManagedAttributes" is set to a comma separated list of names, then they are treated as
* context attribute names, which if set as attributes are passed to the servers Container so that they may be managed with JMX.
*
*
* @org.apache.xbean.XBean description="Creates a basic HTTP context"
*/
public class ContextHandler extends ScopedHandler implements Attributes, Server.Graceful
@ -95,7 +94,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
/* ------------------------------------------------------------ */
/**
* Get the current ServletContext implementation.
*
*
* @return ServletContext implementation
*/
public static Context getCurrentContext()
@ -244,7 +243,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
* Set the virtual hosts for the context. Only requests that have a matching host header or fully qualified URL will be passed to that context with a
* virtual host name. A context with no virtual host names or a null virtual host name is available to all requests that are not served by a context with a
* matching virtual host name.
*
*
* @param vhosts
* Array of virtual hosts that this context responds to. A null host name or null/empty array means any hostname is acceptable. Host names may be
* String representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
@ -265,7 +264,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
/* ------------------------------------------------------------ */
/** Either set virtual hosts or add to an existing set of virtual hosts.
*
*
* @param virtualHosts
* Array of virtual hosts that this context responds to. A null host name or null/empty array means any hostname is acceptable. Host names may be
* String representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
@ -287,7 +286,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
{
currentVirtualHosts = new ArrayList<String>();
}
for (int i = 0; i < virtualHosts.length; i++)
{
String normVhost = normalizeHostname(virtualHosts[i]);
@ -303,7 +302,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
/* ------------------------------------------------------------ */
/**
* Removes an array of virtual host entries, if this removes all entries the _vhosts will be set to null
*
*
* @param virtualHosts
* Array of virtual hosts that this context responds to. A null host name or null/empty array means any hostname is acceptable. Host names may be
* String representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
@ -321,7 +320,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
else
{
List<String> existingVirtualHosts = new ArrayList<String>(Arrays.asList(_vhosts));
for (int i = 0; i < virtualHosts.length; i++)
{
String toRemoveVirtualHost = normalizeHostname(virtualHosts[i]);
@ -330,7 +329,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
existingVirtualHosts.remove(toRemoveVirtualHost);
}
}
if (existingVirtualHosts.isEmpty())
{
_vhosts = null; // if we ended up removing them all, just null out _vhosts
@ -341,13 +340,13 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
}
}
/* ------------------------------------------------------------ */
/**
* Get the virtual hosts for the context. Only requests that have a matching host header or fully qualified URL will be passed to that context with a
* virtual host name. A context with no virtual host names or a null virtual host name is available to all requests that are not served by a context with a
* matching virtual host name.
*
*
* @return Array of virtual hosts that this context responds to. A null host name or empty array means any hostname is acceptable. Host names may be String
* representation of IP addresses. Host names may start with '*.' to wildcard one level of names.
*/
@ -371,9 +370,9 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
/* ------------------------------------------------------------ */
/**
* Set the names of accepted connectors.
*
*
* Names are either "host:port" or a specific configured name for a connector.
*
*
* @param connectors
* If non null, an array of connector names that this context will accept a request from.
*/
@ -425,7 +424,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
/* ------------------------------------------------------------ */
/**
* Make best effort to extract a file classpath from the context classloader
*
*
* @return Returns the classLoader.
*/
public String getClassPath()
@ -521,7 +520,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
/* ------------------------------------------------------------ */
/**
* Set the context event listeners.
*
*
* @param eventListeners
* the event listeners
* @see ServletContextListener
@ -559,7 +558,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
/* ------------------------------------------------------------ */
/**
* Add a context event listeners.
*
*
* @see ServletContextListener
* @see ServletContextAttributeListener
* @see ServletRequestListener
@ -586,7 +585,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
/**
* Set shutdown status. This field allows for graceful shutdown of a context. A started context may be put into non accepting state so that existing
* requests can complete, but no new requests are accepted.
*
*
* @param shutdown
* true if this context is (not?) accepting new requests
*/
@ -694,7 +693,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
/**
* Extensible startContext. this method is called from {@link ContextHandler#doStart()} instead of a call to super.doStart(). This allows derived classes to
* insert additional handling (Eg configuration) before the call to super.doStart by this method will start contained handlers.
*
*
* @see org.eclipse.jetty.server.handler.ContextHandler.Context
*/
protected void startContext() throws Exception
@ -1116,7 +1115,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
/*
* Set a context attribute. Attributes set via this API cannot be overriden by the ServletContext.setAttribute API. Their lifecycle spans the stop/start of
* a context. No attribute listener events are triggered by this API.
*
*
* @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
*/
public void setAttribute(String name, Object value)
@ -1381,14 +1380,17 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
StringBuilder b = new StringBuilder();
String p = getClass().getPackage().getName();
if (p != null && p.length() > 0)
Package pkg = getClass().getPackage();
if (pkg != null)
{
String[] ss = p.split("\\.");
for (String s : ss)
b.append(s.charAt(0)).append('.');
String p = pkg.getName();
if (p != null && p.length() > 0)
{
String[] ss = p.split("\\.");
for (String s : ss)
b.append(s.charAt(0)).append('.');
}
}
b.append(getClass().getSimpleName());
b.append('{').append(getContextPath()).append(',').append(getBaseResource());
@ -1431,7 +1433,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
/**
* Get the character encoding for a locale. The full locale name is first looked up in the map of encodings. If no encoding is found, then the locale
* language is looked up.
*
*
* @param locale
* a <code>Locale</code> value
* @return a <code>String</code> representing the character encoding for the locale or null if none found.
@ -1495,7 +1497,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
/* ------------------------------------------------------------ */
/**
* Convert a URL or path to a Resource. The default implementation is a wrapper for {@link Resource#newResource(String)}.
*
*
* @param urlOrPath
* The URL or path to convert
* @return The Resource for the URL/path
@ -1557,8 +1559,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
* <p>
* A partial implementation of {@link javax.servlet.ServletContext}. A complete implementation is provided by the derived {@link ContextHandler}.
* </p>
*
*
*
*
*/
public class Context implements ServletContext
{
@ -1787,7 +1789,8 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
URL url = getResource(path);
if (url == null)
return null;
return url.openStream();
Resource r = Resource.newResource(url);
return r.getInputStream();
}
catch (Exception e)
{

View File

@ -31,6 +31,7 @@ import org.eclipse.jetty.util.ByteArrayISO8859Writer;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
/* ------------------------------------------------------------ */
@ -60,7 +61,10 @@ public class DefaultHandler extends AbstractHandler
{
URL fav = this.getClass().getClassLoader().getResource("org/eclipse/jetty/favicon.ico");
if (fav!=null)
_favicon=IO.readBytes(fav.openStream());
{
Resource r = Resource.newResource(fav);
_favicon=IO.readBytes(r.getInputStream());
}
}
catch(Exception e)
{

View File

@ -51,7 +51,7 @@ import org.eclipse.jetty.util.resource.Resource;
*
* @org.apache.xbean.XBean
*/
public class ResourceHandler extends AbstractHandler
public class ResourceHandler extends HandlerWrapper
{
private static final Logger LOG = Log.getLogger(ResourceHandler.class);
@ -358,6 +358,8 @@ public class ResourceHandler extends AbstractHandler
{
if(!HttpMethods.HEAD.equals(request.getMethod()))
{
//try another handler
super.handle(target, baseRequest, request, response);
return;
}
skipContentBody = true;
@ -375,7 +377,11 @@ public class ResourceHandler extends AbstractHandler
response.setContentType("text/css");
}
else
{
//no resource - try other handlers
super.handle(target, baseRequest, request, response);
return;
}
}
if (!_aliases && resource.getAlias()!=null)

View File

@ -122,7 +122,6 @@ public class SelectChannelConnector extends AbstractNIOConnector
public void customize(EndPoint endpoint, Request request) throws IOException
{
AsyncEndPoint aEndp = ((AsyncEndPoint)endpoint);
aEndp.setCheckForIdle(false);
request.setTimeStamp(System.currentTimeMillis());
endpoint.setMaxIdleTime(_maxIdleTime);
super.customize(endpoint, request);

View File

@ -13,44 +13,48 @@
package org.eclipse.jetty.server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.concurrent.Exchanger;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import junit.framework.Assert;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.nio.SslConnection;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.IO;
import org.junit.Assert;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.matchers.JUnitMatchers.containsString;
public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
{
protected static final int MAX_IDLE_TIME=250;
static
{
System.setProperty("org.eclipse.jetty.io.nio.IDLE_TICK","100");
}
@Test
public void testMaxIdleWithRequest10() throws Exception
{
{
configureServer(new HelloWorldHandler());
Socket client=newSocket(HOST,_connector.getLocalPort());
client.setSoTimeout(10000);
assertFalse(client.isClosed());
OutputStream os=client.getOutputStream();
InputStream is=client.getInputStream();
@ -63,7 +67,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
long start = System.currentTimeMillis();
IO.toString(is);
Thread.sleep(300);
assertEquals(-1, is.read());
@ -73,13 +77,13 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
@Test
public void testMaxIdleWithRequest11() throws Exception
{
{
configureServer(new EchoHandler());
Socket client=newSocket(HOST,_connector.getLocalPort());
client.setSoTimeout(10000);
assertFalse(client.isClosed());
OutputStream os=client.getOutputStream();
InputStream is=client.getInputStream();
@ -96,24 +100,172 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
long start = System.currentTimeMillis();
IO.toString(is);
Thread.sleep(300);
assertEquals(-1, is.read());
Assert.assertTrue(System.currentTimeMillis()-start>200);
Assert.assertTrue(System.currentTimeMillis()-start<5000);
}
@Test
public void testMaxIdleWithRequest10NoClientClose() throws Exception
{
final Exchanger<EndPoint> endpoint = new Exchanger<EndPoint>();
configureServer(new HelloWorldHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException,
ServletException
{
try
{
endpoint.exchange(baseRequest.getConnection().getEndPoint());
}
catch(Exception e)
{}
super.handle(target,baseRequest,request,response);
}
});
Socket client=newSocket(HOST,_connector.getLocalPort());
client.setSoTimeout(10000);
assertFalse(client.isClosed());
OutputStream os=client.getOutputStream();
InputStream is=client.getInputStream();
os.write((
"GET / HTTP/1.0\r\n"+
"host: "+HOST+":"+_connector.getLocalPort()+"\r\n"+
"connection: close\r\n"+
"\r\n").getBytes("utf-8"));
os.flush();
// Get the server side endpoint
EndPoint endp = endpoint.exchange(null,10,TimeUnit.SECONDS);
if (endp instanceof SslConnection.SslEndPoint)
endp=((SslConnection.SslEndPoint)endp).getEndpoint();
// read the response
String result=IO.toString(is);
Assert.assertThat("OK",result,containsString("200 OK"));
// check client reads EOF
assertEquals(-1, is.read());
// wait for idle timeout
TimeUnit.MILLISECONDS.sleep(MAX_IDLE_TIME+MAX_IDLE_TIME/2);
// further writes will get broken pipe or similar
try
{
for (int i=0;i<1000;i++)
{
os.write((
"GET / HTTP/1.0\r\n"+
"host: "+HOST+":"+_connector.getLocalPort()+"\r\n"+
"connection: keep-alive\r\n"+
"\r\n").getBytes("utf-8"));
os.flush();
}
Assert.fail("half close should have timed out");
}
catch(SocketException e)
{
// expected
}
// check the server side is closed
Assert.assertFalse(endp.isOpen());
}
@Test
public void testMaxIdleWithRequest11NoClientClose() throws Exception
{
final Exchanger<EndPoint> endpoint = new Exchanger<EndPoint>();
configureServer(new EchoHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException,
ServletException
{
try
{
endpoint.exchange(baseRequest.getConnection().getEndPoint());
}
catch(Exception e)
{}
super.handle(target,baseRequest,request,response);
}
});
Socket client=newSocket(HOST,_connector.getLocalPort());
client.setSoTimeout(10000);
assertFalse(client.isClosed());
OutputStream os=client.getOutputStream();
InputStream is=client.getInputStream();
String content="Wibble";
byte[] contentB=content.getBytes("utf-8");
os.write((
"POST /echo HTTP/1.1\r\n"+
"host: "+HOST+":"+_connector.getLocalPort()+"\r\n"+
"content-type: text/plain; charset=utf-8\r\n"+
"content-length: "+contentB.length+"\r\n"+
"connection: close\r\n"+
"\r\n").getBytes("utf-8"));
os.write(contentB);
os.flush();
// Get the server side endpoint
EndPoint endp = endpoint.exchange(null,10,TimeUnit.SECONDS);
// read the response
IO.toString(is);
// check client reads EOF
assertEquals(-1, is.read());
TimeUnit.MILLISECONDS.sleep(MAX_IDLE_TIME+MAX_IDLE_TIME/2);
// further writes will get broken pipe or similar
try
{
for (int i=0;i<1000;i++)
{
os.write((
"GET / HTTP/1.0\r\n"+
"host: "+HOST+":"+_connector.getLocalPort()+"\r\n"+
"connection: keep-alive\r\n"+
"\r\n").getBytes("utf-8"));
os.flush();
}
Assert.fail("half close should have timed out");
}
catch(SocketException e)
{
// expected
}
// check the server side is closed
Assert.assertFalse(endp.isOpen());
}
@Test
public void testMaxIdleNoRequest() throws Exception
{
{
configureServer(new EchoHandler());
Socket client=newSocket(HOST,_connector.getLocalPort());
client.setSoTimeout(10000);
InputStream is=client.getInputStream();
assertFalse(client.isClosed());
Thread.sleep(500);
long start = System.currentTimeMillis();
try
@ -123,25 +275,25 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
}
catch(SSLException e)
{
}
catch(Exception e)
{
e.printStackTrace();
}
Assert.assertTrue(System.currentTimeMillis()-start<5000);
}
}
@Test
public void testMaxIdleWithSlowRequest() throws Exception
{
{
configureServer(new EchoHandler());
Socket client=newSocket(HOST,_connector.getLocalPort());
client.setSoTimeout(10000);
assertFalse(client.isClosed());
OutputStream os=client.getOutputStream();
InputStream is=client.getInputStream();
@ -163,7 +315,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
os.write(contentB);
os.flush();
}
String in = IO.toString(is);
int offset=0;
for (int i =0;i<20;i++)
@ -175,13 +327,13 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
@Test
public void testMaxIdleWithSlowResponse() throws Exception
{
{
configureServer(new SlowResponseHandler());
Socket client=newSocket(HOST,_connector.getLocalPort());
client.setSoTimeout(10000);
assertFalse(client.isClosed());
OutputStream os=client.getOutputStream();
InputStream is=client.getInputStream();
@ -192,7 +344,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
"Connection: close\r\n"+
"\r\n").getBytes("utf-8"));
os.flush();
String in = IO.toString(is);
int offset=0;
for (int i =0;i<20;i++)
@ -204,13 +356,13 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
@Test
public void testMaxIdleWithWait() throws Exception
{
{
configureServer(new WaitHandler());
Socket client=newSocket(HOST,_connector.getLocalPort());
client.setSoTimeout(10000);
assertFalse(client.isClosed());
OutputStream os=client.getOutputStream();
InputStream is=client.getInputStream();
@ -221,12 +373,12 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
"Connection: close\r\n"+
"\r\n").getBytes("utf-8"));
os.flush();
String in = IO.toString(is);
int offset=in.indexOf("Hello World");
Assert.assertTrue(offset>0);
}
protected static class SlowResponseHandler extends AbstractHandler
{
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
@ -234,7 +386,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
baseRequest.setHandled(true);
response.setStatus(200);
OutputStream out = response.getOutputStream();
for (int i=0;i<20;i++)
{
out.write("Hello World\r\n".getBytes());
@ -244,7 +396,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
out.close();
}
}
protected static class WaitHandler extends AbstractHandler
{
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException

View File

@ -13,10 +13,6 @@
package org.eclipse.jetty.server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@ -29,14 +25,12 @@ import java.net.URL;
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.Exchanger;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import junit.framework.Assert;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.IO;
@ -44,6 +38,10 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.StdErrLog;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
/**
*
*/
@ -222,7 +220,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
// Read the response
String response=readResponse(client);
// Check the response
assertEquals("response "+i,RESPONSE2,response);
}
@ -378,7 +376,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
try
{
URL url=new URL(_scheme+"://"+HOST+":"+_connector.getLocalPort()+"/?writes="+w+"&block="+b+ (e==0?"":("&encoding="+encoding[e]))+(c==0?"&chars=true":""));
InputStream in = (InputStream)url.getContent();
String response=IO.toString(in,e==0?null:encoding[e]);
@ -453,7 +451,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
client.close();
}
}
@Test
public void testBlockingWhileWritingResponseContent() throws Exception
{
@ -521,7 +519,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
"\r\n"
).getBytes());
os.flush();
// read the chunked response header
boolean chunked=false;
boolean closed=false;
@ -530,13 +528,13 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
String line=in.readLine();
if (line==null || line.length()==0)
break;
chunked|="Transfer-Encoding: chunked".equals(line);
closed|="Connection: close".equals(line);
}
Assert.assertTrue(chunked);
Assert.assertFalse(closed);
// Read the chunks
int max=Integer.MIN_VALUE;
while(true)
@ -550,22 +548,22 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
if (max<len)
max=len;
}
// Check that a direct content buffer was used as a chunk
Assert.assertEquals(128*1024,max);
// read and check the times are < 999ms
String[] times=in.readLine().split(",");
for (String t: times)
Assert.assertTrue(Integer.valueOf(t).intValue()<999);
// read the EOF chunk
String end=in.readLine();
Assert.assertEquals("0",end);
end=in.readLine();
Assert.assertEquals(0,end.length());
// read the non-chunked response header
chunked=false;
@ -575,13 +573,13 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
String line=in.readLine();
if (line==null || line.length()==0)
break;
chunked|="Transfer-Encoding: chunked".equals(line);
closed|="Connection: close".equals(line);
}
Assert.assertFalse(chunked);
Assert.assertTrue(closed);
String bigline = in.readLine();
Assert.assertEquals(10*128*1024,bigline.length());
@ -589,7 +587,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
times=in.readLine().split(",");
for (String t: times)
Assert.assertTrue(t,Integer.valueOf(t).intValue()<999);
// check close
Assert.assertTrue(in.readLine()==null);
}
@ -607,7 +605,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
byte[] buf = new byte[128*1024];
for (int i=0;i<buf.length;i++)
buf[i]=(byte)("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_".charAt(i%63));
baseRequest.setHandled(true);
response.setStatus(200);
response.setContentType("text/plain");
@ -631,7 +629,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
out.close();
}
}
@Test
public void testPipeline() throws Exception
@ -741,6 +739,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
IO.copy(is,bout);
byte[] b=bout.toByteArray();
System.err.println("OUTPUT: "+new String(b));
int i=0;
while (b[i]!='Z')
i++;
@ -795,14 +794,14 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
"content-length: 10\r\n"+
"\015\012"+
"123456789\n" +
"HEAD /R1 HTTP/1.1\015\012"+
"Host: "+HOST+":"+_connector.getLocalPort()+"\015\012"+
"content-type: text/plain; charset=utf-8\r\n"+
"content-length: 10\r\n"+
"\015\012"+
"123456789\n"+
"POST /R1 HTTP/1.1\015\012"+
"Host: "+HOST+":"+_connector.getLocalPort()+"\015\012"+
"content-type: text/plain; charset=utf-8\r\n"+
@ -810,25 +809,25 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
"Connection: close\015\012"+
"\015\012"+
"123456789\n"
).getBytes("iso-8859-1"));
String in = IO.toString(is);
int index=in.indexOf("123456789");
assertTrue(index>0);
index=in.indexOf("123456789",index+1);
assertTrue(index>0);
index=in.indexOf("123456789",index+1);
assertTrue(index==-1);
}
finally
{
client.close();
}
}
@Test
public void testRecycledReaders() throws Exception
{
@ -908,27 +907,27 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
"Connection: Keep-Alive\r\n"+
"\r\n"
).getBytes());
// Never send a body.
// HelloWorldHandler does not read content, so 100 is not sent.
// So close will have to happen anyway, without reset!
os.flush();
client.setSoTimeout(2000);
long start=System.currentTimeMillis();
String in = IO.toString(is);
assertTrue(System.currentTimeMillis()-start<1000);
assertTrue(in.indexOf("Connection: close")>0);
assertTrue(in.indexOf("Hello world")>0);
}
finally
{
client.close();
}
}
@Test
public void testCommittedError() throws Exception
{
@ -949,25 +948,25 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
"\r\n"
).getBytes());
os.flush();
client.setSoTimeout(2000);
String in = IO.toString(is);
assertEquals(-1,is.read()); // Closed by error!
assertTrue(in.indexOf("HTTP/1.1 200 OK")>=0);
assertTrue(in.indexOf("Transfer-Encoding: chunked")>0);
assertTrue(in.indexOf("Now is the time for all good men to come to the aid of the party")>0);
assertTrue(in.indexOf("\r\n0\r\n")==-1); // chunking is interrupted by error close
client.close();
Thread.sleep(100);
Thread.sleep(100);
assertTrue(!handler._endp.isOpen());
}
finally
{
((StdErrLog)Log.getLogger(AbstractHttpConnection.class)).setHideStacks(false);
if (!client.isClosed())
client.close();
}
@ -976,7 +975,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
protected static class CommittedErrorHandler extends AbstractHandler
{
public EndPoint _endp;
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
_endp=baseRequest.getConnection().getEndPoint();
@ -986,15 +985,15 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
response.getWriter().println("Now is the time for all good men to come to the aid of the party");
response.getWriter().flush();
response.flushBuffer();
throw new ServletException(new Exception("exception after commit"));
}
}
protected static class AvailableHandler extends AbstractHandler
{
public Exchanger<Object> _ex = new Exchanger<Object>();
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
@ -1002,31 +1001,31 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
response.setContentType("text/plain");
InputStream in = request.getInputStream();
ServletOutputStream out=response.getOutputStream();
// should always be some input available, because of deferred dispatch.
int avail=in.available();
out.println(avail);
String buf="";
for (int i=0;i<avail;i++)
buf+=(char)in.read();
avail=in.available();
out.println(avail);
try
{
_ex.exchange(null);
_ex.exchange(null);
_ex.exchange(null);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
avail=in.available();
if (avail==0)
{
// handle blocking channel connectors
@ -1046,15 +1045,15 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
for (int i=0;i<avail;i++)
buf+=(char)in.read();
avail=in.available();
out.println(avail);
out.println(buf);
out.close();
}
}
@Test
public void testAvailable() throws Exception
{
@ -1081,7 +1080,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
"1234567890"
).getBytes());
os.flush();
ah._ex.exchange(null);
os.write((
@ -1090,7 +1089,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
os.flush();
Thread.sleep(500);
ah._ex.exchange(null);
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
// skip header
while(reader.readLine().length()>0);
@ -1099,7 +1098,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
assertEquals(20,Integer.parseInt(reader.readLine()));
assertEquals(0,Integer.parseInt(reader.readLine()));
assertEquals("1234567890abcdefghijklmnopqrst",reader.readLine());
}
finally
{
@ -1107,7 +1106,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
}
}
@Test
public void testDualRequest1() throws Exception
{
@ -1205,5 +1204,5 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
}
}

View File

@ -1,17 +1,33 @@
package org.eclipse.jetty.server;
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.util.IO;
import org.junit.Test;
public class SelectChannelAsyncContextTest extends LocalAsyncContextTest
{
volatile SelectChannelEndPoint _endp;
@Override
protected Connector initConnector()
{
return new SelectChannelConnector();
return new SelectChannelConnector(){
@Override
public void customize(EndPoint endpoint, Request request) throws IOException
{
super.customize(endpoint,request);
_endp=(SelectChannelEndPoint)endpoint;
}
};
}
@Override
@ -23,4 +39,42 @@ public class SelectChannelAsyncContextTest extends LocalAsyncContextTest
return IO.toString(socket.getInputStream());
}
@Test
public void testSuspendResumeWithAsyncDispatch() throws Exception
{
// Test that suspend/resume works in the face of spurious asyncDispatch call that may be
// produced by the SslConnection
final AtomicBoolean running = new AtomicBoolean(true);
Thread thread = new Thread()
{
public void run()
{
while (running.get())
{
try
{
TimeUnit.MILLISECONDS.sleep(200);
SelectChannelEndPoint endp=_endp;
if (endp!=null && endp.isOpen())
endp.asyncDispatch();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
};
try
{
thread.start();
testSuspendResume();
}
finally
{
running.set(false);
thread.join();
}
}
}

View File

@ -1,12 +1,10 @@
package org.eclipse.jetty.server.handler;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Matcher;
@ -18,6 +16,9 @@ import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.junit.AfterClass;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* @version $Revision$ $Date$
*/
@ -91,11 +92,21 @@ public abstract class AbstractConnectHandlerTest
StringBuilder body = new StringBuilder();
if (headers.containsKey("content-length"))
{
int readLen = 0;
int length = Integer.parseInt(headers.get("content-length"));
for (int i = 0; i < length; ++i)
try
{
char c = (char)reader.read();
body.append(c);
for (int i = 0; i < length; ++i)
{
char c = (char)reader.read();
body.append(c);
readLen++;
}
}
catch (SocketTimeoutException e)
{
System.err.printf("Read %,d bytes (out of an expected %,d bytes)%n",readLen,length);
throw e;
}
}
else if ("chunked".equals(headers.get("transfer-encoding")))
@ -126,7 +137,7 @@ public abstract class AbstractConnectHandlerTest
protected Socket newSocket() throws IOException
{
Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
socket.setSoTimeout(5000);
socket.setSoTimeout(10000);
return socket;
}

View File

@ -14,23 +14,17 @@
package org.eclipse.jetty.server.handler;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import junit.framework.Assert;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Request;
@ -38,6 +32,10 @@ import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.resource.Resource;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @version $Revision$
*/
@ -122,22 +120,22 @@ public class ContextHandlerTest
server.setConnectors(new Connector[] { connector });
ContextHandlerCollection contexts = new ContextHandlerCollection();
server.setHandler(contexts);
ContextHandler rootA = new ContextHandler(contexts,"/");
ContextHandler fooA = new ContextHandler(contexts,"/foo");
ContextHandler foobarA = new ContextHandler(contexts,"/foo/bar");
server.start();
// System.err.println(server.dump());
Assert.assertEquals(rootA._scontext,rootA._scontext.getContext("/"));
Assert.assertEquals(fooA._scontext,rootA._scontext.getContext("/foo"));
Assert.assertEquals(foobarA._scontext,rootA._scontext.getContext("/foo/bar"));
Assert.assertEquals(foobarA._scontext,rootA._scontext.getContext("/foo/bar/bob.jsp"));
Assert.assertEquals(rootA._scontext,rootA._scontext.getContext("/other"));
Assert.assertEquals(fooA._scontext,rootA._scontext.getContext("/foo/other"));
Assert.assertEquals(rootA._scontext,foobarA._scontext.getContext("/"));
Assert.assertEquals(fooA._scontext,foobarA._scontext.getContext("/foo"));
Assert.assertEquals(foobarA._scontext,foobarA._scontext.getContext("/foo/bar"));
@ -145,7 +143,7 @@ public class ContextHandlerTest
Assert.assertEquals(rootA._scontext,foobarA._scontext.getContext("/other"));
Assert.assertEquals(fooA._scontext,foobarA._scontext.getContext("/foo/other"));
}
@Test
public void testContextVirtualGetContext() throws Exception
{
@ -154,45 +152,45 @@ public class ContextHandlerTest
server.setConnectors(new Connector[] { connector });
ContextHandlerCollection contexts = new ContextHandlerCollection();
server.setHandler(contexts);
ContextHandler rootA = new ContextHandler(contexts,"/");
rootA.setVirtualHosts(new String[] {"a.com"});
ContextHandler rootB = new ContextHandler(contexts,"/");
rootB.setVirtualHosts(new String[] {"b.com"});
ContextHandler rootC = new ContextHandler(contexts,"/");
rootC.setVirtualHosts(new String[] {"c.com"});
ContextHandler fooA = new ContextHandler(contexts,"/foo");
fooA.setVirtualHosts(new String[] {"a.com"});
ContextHandler fooB = new ContextHandler(contexts,"/foo");
fooB.setVirtualHosts(new String[] {"b.com"});
ContextHandler foobarA = new ContextHandler(contexts,"/foo/bar");
foobarA.setVirtualHosts(new String[] {"a.com"});
server.start();
// System.err.println(server.dump());
Assert.assertEquals(rootA._scontext,rootA._scontext.getContext("/"));
Assert.assertEquals(fooA._scontext,rootA._scontext.getContext("/foo"));
Assert.assertEquals(foobarA._scontext,rootA._scontext.getContext("/foo/bar"));
Assert.assertEquals(foobarA._scontext,rootA._scontext.getContext("/foo/bar/bob"));
Assert.assertEquals(rootA._scontext,rootA._scontext.getContext("/other"));
Assert.assertEquals(rootB._scontext,rootB._scontext.getContext("/other"));
Assert.assertEquals(rootC._scontext,rootC._scontext.getContext("/other"));
Assert.assertEquals(fooB._scontext,rootB._scontext.getContext("/foo/other"));
Assert.assertEquals(rootC._scontext,rootC._scontext.getContext("/foo/other"));
}
@Test
public void testVirtualHostWildcard() throws Exception
{
@ -238,31 +236,31 @@ public class ContextHandlerTest
ContextHandler context = new ContextHandler("/");
// test singular
context.setVirtualHosts(new String[] { "www.example.com"} );
context.setVirtualHosts(new String[] { "www.example.com"} );
Assert.assertEquals(1,context.getVirtualHosts().length);
// test adding two more
context.addVirtualHosts(new String[] { "www.example2.com", "www.example3.com"});
Assert.assertEquals(3,context.getVirtualHosts().length);
// test adding existing context
context.addVirtualHosts(new String[] { "www.example.com" });
Assert.assertEquals(3,context.getVirtualHosts().length);
// test removing existing
context.removeVirtualHosts(new String[] { "www.example3.com" });
Assert.assertEquals(2,context.getVirtualHosts().length);
// test removing non-existent
context.removeVirtualHosts(new String[] { "www.example3.com" });
Assert.assertEquals(2,context.getVirtualHosts().length);
// test removing all remaining and resets to null
context.removeVirtualHosts(new String[] { "www.example.com", "www.example2.com" });
Assert.assertEquals(null,context.getVirtualHosts());
Assert.assertEquals(null,context.getVirtualHosts());
}
@Test
public void testAttributes() throws Exception
{
@ -332,11 +330,12 @@ public class ContextHandlerTest
return root;
}
@Test
public void testUncheckedPrintWriter() throws Exception
{
Server server = new Server();
server.setUncheckedPrintWriter(true);
LocalConnector connector = new LocalConnector();
server.setConnectors(new Connector[] { connector });
ContextHandler context = new ContextHandler("/");
@ -347,7 +346,7 @@ public class ContextHandlerTest
try
{
server.start();
String response = connector.getResponses("GET / HTTP/1.1\n" + "Host: www.example.com.\n\n");
Assert.assertTrue(response.indexOf("Goodbye")>0);
@ -400,7 +399,7 @@ public class ContextHandlerTest
handled = false;
}
}
private static final class WriterHandler extends AbstractHandler
{
boolean error;
@ -422,7 +421,10 @@ public class ContextHandlerTest
writer.write("Goodbye cruel world\n");
writer.close();
response.flushBuffer();
writer.write("speaking from the dead");
//writer.write("speaking from the dead");
writer.write("give the printwriter a chance"); //should create an error
if (writer.checkError())
writer.write("didn't take the chance, will throw now"); //write after an error
}
catch(Throwable th)
{

View File

@ -1230,7 +1230,6 @@ public class ServletHandler extends ScopedHandler
HttpServletResponse response)
throws IOException
{
new Throwable().printStackTrace();
if(LOG.isDebugEnabled())
LOG.debug("Not Found "+request.getRequestURI());
response.sendError(HttpServletResponse.SC_NOT_FOUND);

View File

@ -14,7 +14,9 @@
package org.eclipse.jetty.servlets;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Map;
import java.util.Queue;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
@ -32,8 +34,10 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionEvent;
import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationListener;
@ -908,13 +912,14 @@ public class DoSFilter implements Filter
* A RateTracker is associated with a connection, and stores request rate
* data.
*/
class RateTracker extends Timeout.Task implements HttpSessionBindingListener
class RateTracker extends Timeout.Task implements HttpSessionBindingListener, HttpSessionActivationListener
{
protected final String _id;
protected final int _type;
protected final long[] _timestamps;
protected int _next;
transient protected final String _id;
transient protected final int _type;
transient protected final long[] _timestamps;
transient protected int _next;
public RateTracker(String id, int type,int maxRequestsPerSecond)
{
_id = id;
@ -953,25 +958,49 @@ public class DoSFilter implements Filter
public void valueBound(HttpSessionBindingEvent event)
{
{
if (LOG.isDebugEnabled())
LOG.debug("Value bound:"+_id);
}
public void valueUnbound(HttpSessionBindingEvent event)
{
_rateTrackers.remove(_id);
//take the tracker out of the list of trackers
if (_rateTrackers != null)
_rateTrackers.remove(_id);
if (LOG.isDebugEnabled()) LOG.debug("Tracker removed: "+_id);
}
public void sessionWillPassivate(HttpSessionEvent se)
{
//take the tracker of the list of trackers (if its still there)
//and ensure that we take ourselves out of the session so we are not saved
if (_rateTrackers != null)
_rateTrackers.remove(_id);
se.getSession().removeAttribute(__TRACKER);
if (LOG.isDebugEnabled()) LOG.debug("Value removed: "+_id);
}
public void sessionDidActivate(HttpSessionEvent se)
{
LOG.warn("Unexpected session activation");
}
public void expired()
{
long now = _trackerTimeoutQ.getNow();
int latestIndex = _next == 0 ? 3 : (_next - 1 ) % _timestamps.length;
long last=_timestamps[latestIndex];
boolean hasRecentRequest = last != 0 && (now-last)<1000L;
if (_rateTrackers != null && _trackerTimeoutQ != null)
{
long now = _trackerTimeoutQ.getNow();
int latestIndex = _next == 0 ? 3 : (_next - 1 ) % _timestamps.length;
long last=_timestamps[latestIndex];
boolean hasRecentRequest = last != 0 && (now-last)<1000L;
if (hasRecentRequest)
reschedule();
else
_rateTrackers.remove(_id);
if (hasRecentRequest)
reschedule();
else
_rateTrackers.remove(_id);
}
}
@Override
@ -979,6 +1008,8 @@ public class DoSFilter implements Filter
{
return "RateTracker/"+_id+"/"+_type;
}
}
class FixedRateTracker extends RateTracker

View File

@ -67,10 +67,10 @@ public class IncludableGzipFilter extends GzipFilter
public IncludableResponseWrapper(HttpServletRequest request, HttpServletResponse response)
{
super(request,response);
_mimeTypes = IncludableGzipFilter.this._mimeTypes;
_bufferSize = IncludableGzipFilter.this._bufferSize;
_minGzipSize = IncludableGzipFilter.this._minGzipSize;
super.setMimeTypes(IncludableGzipFilter.this._mimeTypes);
super.setBufferSize(IncludableGzipFilter.this._bufferSize);
super.setMinGzipSize(IncludableGzipFilter.this._minGzipSize);
}
@Override

View File

@ -1,13 +1,12 @@
package org.eclipse.jetty.servlets;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import javax.servlet.Servlet;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.gzip.GzipResponseWrapper;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlets.gzip.GzipTester;
import org.eclipse.jetty.servlets.gzip.TestServletLengthStreamTypeWrite;
@ -25,7 +24,7 @@ import org.junit.runners.Parameterized.Parameters;
/**
* Test the GzipFilter support for Content-Length setting variations.
*
*
* @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
*/
@RunWith(Parameterized.class)
@ -39,9 +38,9 @@ public class GzipFilterContentLengthTest
* in different order so as to simulate the real world scenario
* that caused the bug in Eclipse <a href="Bug 354014">http://bugs.eclipse.org/354014</a>
* <p>
* This test case will be run with each of the entries in
* This test case will be run with each of the entries in
* the array below as setup parameters for the test case.
*
*
* @return the junit parameters
*/
@Parameters
@ -49,7 +48,6 @@ public class GzipFilterContentLengthTest
{
return Arrays.asList(new Object[][]
{
{ DefaultServlet.class },
{ TestServletLengthStreamTypeWrite.class },
{ TestServletLengthTypeStreamWrite.class },
{ TestServletStreamLengthTypeWrite.class },
@ -77,7 +75,7 @@ public class GzipFilterContentLengthTest
{
GzipTester tester = new GzipTester(testingdir);
tester.prepareServerFile(filename,filesize);
File testfile = tester.prepareServerFile(testServlet.getSimpleName() + "-" + filename,filesize);
FilterHolder holder = tester.setContentServlet(testServlet);
holder.setInitParameter("mimeTypes","text/plain");
@ -85,7 +83,7 @@ public class GzipFilterContentLengthTest
try
{
tester.start();
tester.assertIsResponseGzipCompressed(filename);
tester.assertIsResponseGzipCompressed(testfile.getName());
}
finally
{
@ -97,7 +95,7 @@ public class GzipFilterContentLengthTest
{
GzipTester tester = new GzipTester(testingdir);
tester.prepareServerFile(filename,filesize);
File testfile = tester.prepareServerFile(testServlet.getSimpleName() + "-" + filename,filesize);
FilterHolder holder = tester.setContentServlet(testServlet);
holder.setInitParameter("mimeTypes","text/plain");
@ -105,7 +103,7 @@ public class GzipFilterContentLengthTest
try
{
tester.start();
tester.assertIsResponseNotGzipCompressed(filename,filesize,HttpStatus.OK_200);
tester.assertIsResponseNotGzipCompressed(testfile.getName(),filesize,HttpStatus.OK_200);
}
finally
{
@ -119,7 +117,7 @@ public class GzipFilterContentLengthTest
@Test
public void testIsGzipCompressedSmall() throws Exception
{
assertIsGzipCompressed("file.txt",SMALL);
assertIsGzipCompressed("file-small.txt",SMALL);
}
/**
@ -128,7 +126,7 @@ public class GzipFilterContentLengthTest
@Test
public void testIsGzipCompressedMedium() throws Exception
{
assertIsGzipCompressed("file.txt",MEDIUM);
assertIsGzipCompressed("file-med.txt",MEDIUM);
}
/**
@ -137,54 +135,54 @@ public class GzipFilterContentLengthTest
@Test
public void testIsGzipCompressedLarge() throws Exception
{
assertIsGzipCompressed("file.txt",LARGE);
assertIsGzipCompressed("file-large.txt",LARGE);
}
/**
* Tests for problems with Content-Length header on small size files
* Tests for problems with Content-Length header on small size files
* that are not being compressed encountered when using GzipFilter
*
*
* @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
*/
@Test
public void testIsNotGzipCompressedTiny() throws Exception
{
assertIsNotGzipCompressed("file.txt",TINY);
assertIsNotGzipCompressed("file-tiny.txt",TINY);
}
/**
* Tests for problems with Content-Length header on small size files
* Tests for problems with Content-Length header on small size files
* that are not being compressed encountered when using GzipFilter
*
*
* @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
*/
@Test
public void testIsNotGzipCompressedSmall() throws Exception
{
assertIsNotGzipCompressed("file.mp3",SMALL);
assertIsNotGzipCompressed("file-small.mp3",SMALL);
}
/**
* Tests for problems with Content-Length header on medium size files
* Tests for problems with Content-Length header on medium size files
* that are not being compressed encountered when using GzipFilter
*
*
* @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
*/
@Test
public void testIsNotGzipCompressedMedium() throws Exception
{
assertIsNotGzipCompressed("file.mp3",MEDIUM);
assertIsNotGzipCompressed("file-medium.mp3",MEDIUM);
}
/**
* Tests for problems with Content-Length header on large size files
* Tests for problems with Content-Length header on large size files
* that were not being compressed encountered when using GzipFilter
*
*
* @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
*/
@Test
public void testIsNotGzipCompressedLarge() throws Exception
{
assertIsNotGzipCompressed("file.mp3",LARGE);
assertIsNotGzipCompressed("file-large.mp3",LARGE);
}
}

View File

@ -0,0 +1,85 @@
// ========================================================================
// Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.servlets;
import javax.servlet.Servlet;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlets.gzip.GzipTester;
import org.eclipse.jetty.servlets.gzip.TestMinGzipSizeServlet;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.junit.Rule;
import org.junit.Test;
/**
* Perform specific tests on the IncludableGzipFilter's ability to manage
* minGzipSize initialization parameter.
*
* @see <a href="Eclipse Bug 366106">http://bugs.eclipse.org/366106</a>
*/
public class IncludableGzipFilterMinSizeTest
{
@Rule
public TestingDir testdir = new TestingDir();
private Class<? extends Servlet> testServlet = TestMinGzipSizeServlet.class;
@Test
public void testUnderMinSize() throws Exception
{
GzipTester tester = new GzipTester(testdir);
// Use IncludableGzipFilter
tester.setGzipFilterClass(IncludableGzipFilter.class);
FilterHolder holder = tester.setContentServlet(testServlet);
// A valid mime type that we will never use in this test.
// configured here to prevent mimeType==null logic
holder.setInitParameter("mimeTypes","application/soap+xml");
holder.setInitParameter("minGzipSize", "2048");
holder.setInitParameter("uncheckedPrintWriter","true");
tester.copyTestServerFile("small_script.js");
try {
tester.start();
tester.assertIsResponseNotGzipFiltered("small_script.js",
"small_script.js.sha1",
"text/javascript; charset=utf-8");
} finally {
tester.stop();
}
}
@Test
public void testOverMinSize() throws Exception
{
GzipTester tester = new GzipTester(testdir);
// Use IncludableGzipFilter
tester.setGzipFilterClass(IncludableGzipFilter.class);
FilterHolder holder = tester.setContentServlet(testServlet);
holder.setInitParameter("mimeTypes","application/soap+xml,text/javascript");
holder.setInitParameter("minGzipSize", "2048");
holder.setInitParameter("uncheckedPrintWriter","true");
tester.copyTestServerFile("big_script.js");
try {
tester.start();
tester.assertIsResponseGzipCompressed("big_script.js");
} finally {
tester.stop();
}
}
}

View File

@ -1,12 +1,5 @@
package org.eclipse.jetty.servlets.gzip;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
@ -19,7 +12,6 @@ import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import javax.servlet.Servlet;
import javax.servlet.http.HttpServletResponse;
@ -34,8 +26,15 @@ import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.junit.Assert;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
public class GzipTester
{
private Class<? extends GzipFilter> gzipFilterClass = GzipFilter.class;
private String encoding = "ISO8859_1";
private ServletTester servletTester;
private TestingDir testdir;
@ -44,7 +43,7 @@ public class GzipTester
{
this.testdir = testingdir;
// Make sure we start with a clean testing directory.
this.testdir.ensureEmpty();
// DOES NOT WORK IN WINDOWS - this.testdir.ensureEmpty();
}
public void assertIsResponseGzipCompressed(String filename) throws Exception
@ -109,7 +108,7 @@ public class GzipTester
* <p>
* An example is to test that it is possible to configure GzipFilter to not recompress content that shouldn't be
* compressed by the GzipFilter.
*
*
* @param requestedFilename
* the filename used to on the GET request,.
* @param testResourceSha1Sum
@ -192,7 +191,7 @@ public class GzipTester
/**
* Asserts that the requested filename results in a properly structured GzipFilter response, where the content is
* not compressed, and the content-length is returned appropriately.
*
*
* @param filename
* the filename used for the request, and also used to compare the response to the server file, assumes
* that the file is suitable for {@link Assert#assertEquals(Object, Object)} use. (in other words, the
@ -233,7 +232,7 @@ public class GzipTester
Assert.assertThat("Response.header[Content-Length]",serverLength,is(expectedFilesize));
}
Assert.assertThat("Response.header[Content-Encoding]",response.getHeader("Content-Encoding"),not(containsString("gzip")));
// Assert that the contents are what we expect.
if (filename != null)
{
@ -259,13 +258,13 @@ public class GzipTester
}
}
}
/**
* Generate string content of arbitrary length.
*
*
* @param length
* the length of the string to generate.
* @return the string content.
@ -306,37 +305,56 @@ public class GzipTester
/**
* Create a file on the server resource path of a specified filename and size.
*
*
* @param filename
* the filename to create
* @param filesize
* the file size to create (Note: this isn't suitable for creating large multi-megabyte files)
*/
public void prepareServerFile(String filename, int filesize) throws IOException
public File prepareServerFile(String filename, int filesize) throws IOException
{
File testFile = testdir.getFile(filename);
File dir = testdir.getDir();
File testFile = new File(dir,filename);
// Make sure we have a uniq filename (to work around windows File.delete bug)
int i = 0;
while (testFile.exists())
{
testFile = new File(dir,(i++) + "-" + filename);
}
FileOutputStream fos = null;
BufferedOutputStream out = null;
ByteArrayInputStream in = null;
try
{
fos = new FileOutputStream(testFile,false);
out = new BufferedOutputStream(fos);
in = new ByteArrayInputStream(generateContent(filesize).getBytes(encoding));
IO.copy(in,out);
IO.copy(in,fos);
return testFile;
}
finally
{
IO.close(in);
IO.close(out);
IO.close(fos);
}
}
/**
* Copy a src/test/resource file into the server tree for eventual serving.
*
* @param filename
* the filename to look for in src/test/resources
*/
public void copyTestServerFile(String filename) throws IOException
{
File srcFile = MavenTestingUtils.getTestResourceFile(filename);
File testFile = testdir.getFile(filename);
IO.copy(srcFile,testFile);
}
/**
* Set the servlet that provides content for the GzipFilter in being tested.
*
*
* @param servletClass
* the servlet that will provide content.
* @return the FilterHolder for configuring the GzipFilter's initParameters with
@ -348,11 +366,19 @@ public class GzipTester
servletTester.setResourceBase(testdir.getDir().getCanonicalPath());
ServletHolder servletHolder = servletTester.addServlet(servletClass,"/");
servletHolder.setInitParameter("baseDir",testdir.getDir().getAbsolutePath());
FilterHolder holder = servletTester.addFilter(GzipFilter.class,"/*",0);
FilterHolder holder = servletTester.addFilter(gzipFilterClass,"/*",0);
return holder;
}
public Class<? extends GzipFilter> getGzipFilterClass()
{
return gzipFilterClass;
}
public void setGzipFilterClass(Class<? extends GzipFilter> gzipFilterClass)
{
this.gzipFilterClass = gzipFilterClass;
}
public void setEncoding(String encoding)
{

View File

@ -0,0 +1,52 @@
package org.eclipse.jetty.servlets.gzip;
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.io.Buffer;
/**
* Test servlet for testing against unusual minGzip configurable.
*/
@SuppressWarnings("serial")
public class TestMinGzipSizeServlet extends TestDirContentServlet
{
private MimeTypes mimeTypes;
@Override
public void init(ServletConfig config) throws ServletException
{
super.init(config);
mimeTypes = new MimeTypes();
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String fileName = request.getServletPath();
byte[] dataBytes = loadContentFileBytes(fileName);
response.setContentLength(dataBytes.length);
if (fileName.endsWith(".js"))
{
// intentionally long-form content type to test ";" splitting in code
response.setContentType("text/javascript; charset=utf-8");
}
else
{
Buffer buf = mimeTypes.getMimeByExtension(fileName);
if (buf != null)
{
response.setContentType(buf.toString());
}
}
ServletOutputStream out = response.getOutputStream();
out.write(dataBytes);
}
}

View File

@ -0,0 +1,792 @@
//----------------------------------------------------------------------
//
// Silly / Pointless Javascript to test GZIP compression.
//
//----------------------------------------------------------------------
var LOGO = {
dat: [
0x50, 0x89, 0x47, 0x4e, 0x0a, 0x0d, 0x0a, 0x1a, 0x00, 0x00, 0x0d, 0x00, 0x48, 0x49, 0x52, 0x44,
0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x78, 0x00, 0x06, 0x08, 0x00, 0x00, 0x2a, 0x00, 0x21, 0x96,
0x00, 0x0f, 0x00, 0x00, 0x73, 0x04, 0x49, 0x42, 0x08, 0x54, 0x08, 0x08, 0x7c, 0x08, 0x64, 0x08,
0x00, 0x88, 0x00, 0x00, 0x70, 0x09, 0x59, 0x48, 0x00, 0x73, 0x04, 0x00, 0x00, 0x27, 0x04, 0x00,
0x01, 0x27, 0x4f, 0xd9, 0x80, 0x1d, 0x00, 0x00, 0x19, 0x00, 0x45, 0x74, 0x74, 0x58, 0x6f, 0x53,
0x74, 0x66, 0x61, 0x77, 0x65, 0x72, 0x77, 0x00, 0x77, 0x77, 0x69, 0x2e, 0x6b, 0x6e, 0x63, 0x73,
0x70, 0x61, 0x2e, 0x65, 0x72, 0x6f, 0x9b, 0x67, 0x3c, 0xee, 0x00, 0x1a, 0x20, 0x00, 0x49, 0x00,
0x41, 0x44, 0x78, 0x54, 0xed, 0x9c, 0x79, 0x9d, 0x1c, 0xd8, 0x95, 0x55, 0x3f, 0xff, 0xfb, 0xa7,
0xb2, 0xdd, 0x24, 0xef, 0x24, 0x81, 0x81, 0x2c, 0x20, 0x40, 0xd5, 0x91, 0x8b, 0xb0, 0x3f, 0xbb,
0x1d, 0x04, 0x54, 0x1c, 0x46, 0x74, 0x17, 0x18, 0xd1, 0x98, 0x19, 0xd1, 0x05, 0x11, 0x37, 0xf4,
0xe2, 0x8f, 0xcc, 0xb8, 0x28, 0x8c, 0x82, 0x02, 0x22, 0x8c, 0xe0, 0xe8, 0xe2, 0x86, 0x02, 0x88,
0x8e, 0xa2, 0x08, 0xe8, 0x42, 0x42, 0x4b, 0x08, 0xc2, 0xc8, 0x12, 0x12, 0xf6, 0x42, 0x7d, 0x90,
0xf7, 0x7d, 0x3e, 0xee, 0x47, 0xf3, 0x77, 0x75, 0xeb, 0x6a, 0xdf, 0x7e, 0xee, 0xea, 0xab, 0x7b,
0xdc, 0x93, 0xf3, 0xcf, 0xd0, 0xf0, 0xa9, 0x55, 0x53, 0xae, 0x6f, 0x5d, 0x3d, 0xd5, 0x9e, 0xf7,
0xee, 0x7b, 0x8a, 0xf9, 0xe2, 0xaa, 0x38, 0x70, 0x0e, 0x1c, 0xc3, 0x87, 0x99, 0x2e, 0x2f, 0xb4,
0xe1, 0xc0, 0x38, 0x70, 0x8e, 0x1c, 0x11, 0x83, 0x80, 0xe7, 0x0e, 0x1d, 0xc3, 0x87, 0x48, 0xe1,
0xe7, 0x01, 0x1d, 0x80, 0x87, 0x0e, 0xe1, 0xc3, 0x01, 0x48, 0x80, 0xe7, 0x0e, 0x1d, 0xc3, 0x87,
0x48, 0xe1, 0xe7, 0x01, 0x1d, 0x80, 0x87, 0x0e, 0xe1, 0xc3, 0x01, 0x48, 0x80, 0xe7, 0x0e, 0x1d,
0xc3, 0x87, 0x48, 0xe1, 0xe7, 0x01, 0x1d, 0x80, 0x87, 0x0e, 0xe1, 0xc3, 0x01, 0x48, 0x80, 0xe7,
0x0e, 0x1d, 0xc3, 0x87, 0x48, 0xe1, 0xe7, 0x01, 0x1d, 0x80, 0x87, 0x0e, 0xe1, 0xc3, 0x01, 0x48,
0x80, 0xe7, 0x0e, 0x1d, 0xc3, 0x87, 0x48, 0xe1, 0xe7, 0x01, 0x1d, 0x80, 0x87, 0x0e, 0xe1, 0xc3,
0x01, 0x48, 0x80, 0xe7, 0x0e, 0x1d, 0xc3, 0x87, 0x48, 0xe1, 0x96, 0x81, 0x2f, 0xb4, 0x44, 0x40,
0x2c, 0x3a, 0xe9, 0x98, 0xa7, 0x55, 0xe1, 0x3a, 0x38, 0x70, 0x8e, 0x1c, 0x42, 0x26, 0xf0, 0xd2,
0x22, 0x4b, 0x16, 0x32, 0x0c, 0xb8, 0x02, 0xb8, 0xde, 0x38, 0xc9, 0x82, 0xe0, 0x4e, 0x80, 0xbf,
0xa9, 0x4f, 0xbf, 0x6a, 0x7b, 0x05, 0x88, 0xb1, 0x6c, 0xc8, 0xc3, 0xe0, 0xfb, 0xc0, 0xd1, 0x81,
0xcd, 0x86, 0x81, 0xe5, 0xc0, 0xe5, 0xab, 0xdf, 0x02, 0xea, 0xb6, 0xc3, 0x0e, 0x1c, 0xa3, 0x87,
0x44, 0x6e, 0x12, 0x64, 0x29, 0x70, 0x41, 0xf0, 0x7a, 0x60, 0xaf, 0xc2, 0x01, 0x77, 0x80, 0xf3,
0x55, 0x9b, 0x21, 0xf5, 0xf6, 0x0b, 0x81, 0xba, 0x81, 0xc7, 0x54, 0x5b, 0x77, 0xf5, 0xbf, 0x09,
0xd9, 0xeb, 0xed, 0xb7, 0x45, 0x80, 0x0b, 0x24, 0x04, 0x2c, 0x5b, 0x66, 0xec, 0x35, 0x28, 0xf1,
0xd7, 0xf0, 0xba, 0xaa, 0xb6, 0xd5, 0x11, 0x61, 0x23, 0x79, 0x27, 0xf0, 0x76, 0xdb, 0x5e, 0x81,
0x27, 0x3c, 0xc3, 0xfc, 0x6c, 0x14, 0x1c, 0x3b, 0xc7, 0x0e, 0x10, 0xa0, 0x23, 0x91, 0x67, 0x81,
0x91, 0x81, 0x9e, 0x75, 0x13, 0xaa, 0x4b, 0x38, 0x17, 0x55, 0xb2, 0x5b, 0x05, 0xd7, 0xa3, 0x9c,
0x0b, 0xaa, 0x7e, 0x93, 0x8d, 0x31, 0xe0, 0x39, 0x48, 0x2b, 0xf9, 0xc7, 0x9c, 0x02, 0xbc, 0x0b,
0xb6, 0xdb, 0x62, 0xd1, 0xe3, 0xa7, 0xdb, 0x66, 0x8b, 0x76, 0x03, 0xb4, 0xa5, 0x37, 0xdb, 0x64,
0x70, 0xe1, 0x06, 0x38, 0x2d, 0xcb, 0xef, 0xd4, 0x01, 0x0c, 0x01, 0x86, 0x8b, 0xf7, 0xab, 0x48,
0x7b, 0x25, 0x81, 0x43, 0x0d, 0x5f, 0x5e, 0xc2, 0x34, 0x84, 0x80, 0xe6, 0x50, 0x3f, 0x20, 0xfa,
0x98, 0x19, 0xfa, 0xfd, 0xd7, 0xc4, 0x0c, 0x9c, 0x96, 0x55, 0x92, 0x3c, 0x0b, 0x43, 0x3d, 0xe5,
0xcc, 0x33, 0x8c, 0x1a, 0x0e, 0x65, 0xab, 0x30, 0x31, 0xb4, 0x4d, 0xb9, 0xd6, 0x98, 0xb6, 0x6e,
0xf3, 0xef, 0x9f, 0x6a, 0xba, 0xb2, 0xfc, 0xb7, 0xc7, 0xa3, 0xc8, 0x88, 0x55, 0x04, 0x62, 0xdd,
0xa8, 0xd4, 0xe1, 0xc3, 0xd4, 0x70, 0x71, 0x40, 0xee, 0x7a, 0xd2, 0x82, 0xb0, 0xf6, 0x30, 0x8c,
0x58, 0x6b, 0x36, 0xb2, 0x55, 0x72, 0x81, 0x4f, 0xfd, 0x4d, 0x88, 0xe5, 0x34, 0xee, 0x69, 0xbc,
0x63, 0x38, 0xd6, 0xf6, 0x16, 0xf4, 0xd8, 0xd8, 0xb6, 0x57, 0x38, 0x77, 0xa8, 0x50, 0x78, 0x72,
0x56, 0x2c, 0xb0, 0x1d, 0xb4, 0x88, 0xa7, 0x03, 0xb6, 0x95, 0x1e, 0xa7, 0xe5, 0x97, 0x9f, 0x9a,
0x33, 0x0f, 0x73, 0x6a, 0xba, 0xdb, 0x9f, 0x02, 0x79, 0x3c, 0x7f, 0xb7, 0x34, 0xd7, 0x06, 0xa3,
0x39, 0xe3, 0xbf, 0xdb, 0xdd, 0x71, 0x76, 0x94, 0x9f, 0x2e, 0x66, 0xd8, 0xe0, 0xd4, 0xaf, 0x95,
0x70, 0xf4, 0xab, 0xeb, 0xfe, 0x7d, 0x87, 0x5d, 0xce, 0x03, 0x3b, 0x01, 0x8e, 0x1c, 0xe4, 0x66,
0xff, 0x5a, 0xa7, 0xc6, 0x6d, 0x0e, 0x8b, 0xe3, 0xdb, 0x53, 0xfd, 0x07, 0x02, 0xe5, 0xfc, 0x70,
0xbd, 0xc2, 0x07, 0x7e, 0x53, 0xbc, 0xab, 0x55, 0xc4, 0x39, 0xea, 0x6b, 0xa7, 0xb1, 0x89, 0xc0,
0xf6, 0x8b, 0x1d, 0xfa, 0xa7, 0x70, 0x56, 0xaa, 0xf8, 0x74, 0xb0, 0x95, 0x82, 0x1d, 0x15, 0x3e,
0x24, 0x2f, 0xc0, 0x0a, 0x73, 0x31, 0xfb, 0xcc, 0x65, 0xff, 0xe4, 0x4f, 0xbb, 0xc2, 0x1a, 0x96,
0x1a, 0x37, 0xe0, 0x25, 0xcf, 0x80, 0x69, 0x1a, 0x77, 0xfe, 0xdd, 0xcf, 0x78, 0x13, 0xf2, 0x16,
0x32, 0xc0, 0x46, 0xe3, 0x0e, 0x1d, 0x23, 0x87, 0x22, 0x21, 0x38, 0x72, 0x49, 0x70, 0x7b, 0x69,
0x46, 0x68, 0xc4, 0xf8, 0x64, 0xe4, 0x94, 0x03, 0xb7, 0x7b, 0xb3, 0xf5, 0x27, 0xa2, 0x6f, 0xe0,
0xb6, 0x2b, 0x45, 0x77, 0xef, 0x7b, 0xc7, 0xab, 0x83, 0xde, 0x72, 0x3b, 0xdf, 0x3c, 0xb0, 0x15,
0x3c, 0xb7, 0x09, 0xd1, 0xd8, 0x8a, 0xc0, 0x76, 0x47, 0x01, 0x63, 0x34, 0xd6, 0x4e, 0xc1, 0xb8,
0x16, 0x97, 0x3a, 0x44, 0x8f, 0x25, 0x37, 0x19, 0xe5, 0x1a, 0xd2, 0xac, 0xb1, 0x87, 0xc2, 0x2d,
0x21, 0xcc, 0x6f, 0x66, 0xde, 0xfb, 0xb2, 0xbc, 0x2b, 0xb8, 0xbb, 0xf0, 0xa8, 0x97, 0x1e, 0xea,
0x46, 0xa3, 0x0e, 0x1d, 0xa3, 0x87, 0x3e, 0x36, 0x2f, 0x8d, 0xfb, 0x1a, 0x43, 0xa1, 0x19, 0x5a,
0x22, 0xdf, 0x4e, 0x89, 0xfd, 0x70, 0xbe, 0xfa, 0xae, 0xf0, 0xcd, 0x1b, 0xeb, 0xda, 0xef, 0x0d,
0x66, 0xfa, 0x13, 0xa2, 0xb1, 0x14, 0x07, 0x3d, 0x74, 0x1c, 0xa7, 0xc0, 0x37, 0x9b, 0xd2, 0xff,
0xc0, 0xfc, 0x38, 0x08, 0xcc, 0x0f, 0x6e, 0x37, 0x87, 0xd4, 0x1c, 0x88, 0x1c, 0x03, 0xda, 0x52,
0x63, 0x3e, 0x96, 0x44, 0x7f, 0x64, 0xe4, 0xea, 0xd8, 0x2c, 0x27, 0x9b, 0x4c, 0x1f, 0x9f, 0x6e,
0xd8, 0x6b, 0x01, 0xe4, 0x88, 0x83, 0x0d, 0x1c, 0x0e, 0x5c, 0x07, 0x9c, 0xff, 0x46, 0x0a, 0x54,
0xc2, 0x6c, 0x32, 0x5b, 0x67, 0xf1, 0x46, 0x53, 0x64, 0x44, 0x5e, 0x08, 0xe1, 0xe2, 0x62, 0xdf,
0xe9, 0x7e, 0x37, 0x5b, 0x08, 0xf0, 0x15, 0xf0, 0x8d, 0x55, 0x9e, 0x84, 0x8e, 0x1c, 0x22, 0x30,
0x1e, 0x32, 0x48, 0xf8, 0xbb, 0x69, 0xe0, 0x45, 0x43, 0xaa, 0x8d, 0x93, 0xff, 0x46, 0x57, 0x77,
0x67, 0x8e, 0x03, 0x3a, 0x8e, 0x03, 0xc0, 0x15, 0x9b, 0x7f, 0x37, 0xb2, 0x4f, 0x77, 0x79, 0x9e,
0x08, 0xc1, 0x1a, 0xe3, 0xee, 0xe0, 0x27, 0x44, 0xd9, 0x29, 0xe5, 0xaf, 0x75, 0x4b, 0x1e, 0x50,
0x8e, 0x09, 0x98, 0x9e, 0xc2, 0x61, 0xb3, 0x34, 0xc1, 0x23, 0xdd, 0xae, 0xda, 0xca, 0x03, 0x17,
0x6a, 0x37, 0xaa, 0x91, 0x35, 0xee, 0x34, 0x6a, 0x30, 0x4a, 0x3c, 0xfc, 0xfc, 0xc2, 0x3f, 0xa8,
0x7e, 0x14, 0xe7, 0x06, 0x07, 0x80, 0x88, 0x85, 0x1b, 0xfc, 0x59, 0xf0, 0x3a, 0xcc, 0x30, 0xde,
0x88, 0x17, 0xa7, 0xc8, 0xf5, 0x55, 0x8d, 0x5b, 0xb1, 0x3e, 0xcc, 0x88, 0x8b, 0xc2, 0x8c, 0xf8,
0xf4, 0x6a, 0xab, 0xb9, 0x7a, 0xf0, 0xf5, 0xe0, 0xf2, 0x22, 0x55, 0x5e, 0x6c, 0xdd, 0xae, 0xd1,
0xff, 0x63, 0x9f, 0xe4, 0xb2, 0xf0, 0x01, 0x88, 0xef, 0x78, 0x56, 0xb8, 0x4f, 0x0e, 0xa0, 0x98,
0xb5, 0xfa, 0xe8, 0xe8, 0x1b, 0xf7, 0xe6, 0x55, 0xeb, 0x7f, 0x17, 0xb6, 0xfa, 0x37, 0xb5, 0xad,
0x69, 0xc3, 0x04, 0x2d, 0x22, 0x2d, 0x80, 0x33, 0xa5, 0x09, 0x1b, 0x6d, 0xe7, 0xe1, 0x4f, 0x15,
0xb2, 0x05, 0x21, 0x9f, 0x47, 0x1d, 0x70, 0x14, 0x67, 0xc0, 0x30, 0x8f, 0xe7, 0xdf, 0xe7, 0x99,
0x70, 0x1c, 0x44, 0x62, 0x38, 0xe4, 0x6a, 0xe0, 0x3a, 0xec, 0xf0, 0x5f, 0xc1, 0x3a, 0x8b, 0x37,
0x39, 0xc8, 0xce, 0x06, 0x13, 0x7d, 0x9d, 0x76, 0x89, 0x6f, 0x80, 0xf3, 0x52, 0xdb, 0xeb, 0xb0,
0x8f, 0xd8, 0x91, 0x10, 0xc0, 0x61, 0xfc, 0x27, 0xae, 0xfb, 0x6c, 0x39, 0x89, 0xf0, 0x00, 0x50,
0x74, 0xcf, 0xf9, 0xe6, 0xae, 0xd3, 0xef, 0x80, 0x04, 0xdb, 0x65, 0xdc, 0xde, 0xca, 0x5d, 0x73,
0x7e, 0x05, 0x23, 0xbb, 0x6f, 0x60, 0x70, 0x1b, 0xa2, 0x47, 0xf8, 0x93, 0x39, 0xb0, 0xb6, 0x02,
0x7e, 0x1f, 0xa2, 0x7e, 0xe6, 0x29, 0xcb, 0x7f, 0xbb, 0xbf, 0xe0, 0x55, 0xe1, 0xb4, 0x3b, 0x66,
0x05, 0x1e, 0x7e, 0x60, 0x01, 0xd0, 0x53, 0xaf, 0x35, 0xd5, 0x8d, 0x46, 0x18, 0x1e, 0x13, 0xfc,
0xf7, 0xbe, 0x3d, 0xa1, 0x3e, 0x63, 0xdc, 0xfe, 0xec, 0x1b, 0xce, 0x1c, 0x81, 0xa4, 0x67, 0xcf,
0x99, 0x71, 0x9b, 0xc5, 0xdb, 0x4a, 0xf1, 0x59, 0x3f, 0x9e, 0xf4, 0x93, 0x02, 0x15, 0x30, 0xeb,
0x9a, 0x66, 0xe6, 0xb5, 0x00, 0x38, 0x02, 0xb8, 0x5b, 0x18, 0x38, 0xda, 0x4c, 0x7f, 0xb3, 0x0b,
0x26, 0x86, 0x8d, 0x1d, 0x46, 0x46, 0x37, 0xbf, 0x6a, 0xa9, 0x85, 0x4f, 0x2f, 0xc3, 0xd7, 0xaf,
0xd7, 0xde, 0xf4, 0x37, 0x2a, 0x12, 0x75, 0x5d, 0xaa, 0xab, 0x26, 0x76, 0x89, 0x3a, 0xf4, 0x8f,
0x70, 0x1c, 0x19, 0x3a, 0x56, 0xd0, 0x82, 0x47, 0x32, 0x22, 0x38, 0x0e, 0xb4, 0xae, 0xca, 0x7d,
0x2c, 0xf0, 0xf3, 0x86, 0x58, 0xaf, 0x99, 0xd2, 0x4f, 0x67, 0x02, 0x70, 0x8d, 0xd8, 0x07, 0x7e,
0x79, 0x47, 0xda, 0x04, 0x81, 0x9e, 0xaf, 0xed, 0x89, 0x1d, 0xa5, 0xc9, 0x8b, 0xda, 0xf3, 0x3b,
0xb2, 0x9c, 0xf0, 0x38, 0x1e, 0xde, 0x60, 0xd3, 0x81, 0x13, 0xf5, 0x11, 0x87, 0xf5, 0xf0, 0x77,
0x84, 0xc9, 0x9e, 0x99, 0x79, 0x49, 0x73, 0xd3, 0x3c, 0x5d, 0xbb, 0xb2, 0xce, 0xfc, 0x17, 0x4d,
0x71, 0x11, 0xd8, 0x35, 0x71, 0x1d, 0x8b, 0x14, 0x5c, 0x56, 0xdf, 0xe5, 0xed, 0x77, 0xc9, 0xa1,
0xa3, 0x46, 0x7a, 0x2b, 0xdc, 0x0a, 0x2d, 0xbb, 0x59, 0x50, 0x37, 0x78, 0x9f, 0xf0, 0xec, 0x55,
0xba, 0x7d, 0x7b, 0x1e, 0x7a, 0x6b, 0xfc, 0x0b, 0x6b, 0x6c, 0x5e, 0xc0, 0x5e, 0x17, 0x8f, 0x31,
0xb1, 0x9a, 0x05, 0x99, 0x76, 0x5d, 0x6d, 0xc0, 0x84, 0x43, 0x67, 0xc3, 0x1d, 0x9b, 0xe6, 0x09,
0xe1, 0xfb, 0x67, 0xe5, 0x23, 0x02, 0x55, 0xc1, 0xba, 0xaa, 0xa8, 0xde, 0x28, 0xd1, 0xe2, 0x67,
0x48, 0x1b, 0x9d, 0x9d, 0xfc, 0xce, 0x3c, 0xef, 0x46, 0xe3, 0x73, 0xf7, 0x64, 0x44, 0xbe, 0x14,
0x29, 0x42, 0x0c, 0xa7, 0x1a, 0xcb, 0xbe, 0x75, 0xfb, 0x10, 0x6a, 0x77, 0xb3, 0xf4, 0x70, 0x19,
0xa6, 0xc0, 0xbd, 0x9f, 0xc2, 0x9c, 0x7b, 0x93, 0xbf, 0x03, 0xa3, 0x69, 0xbe, 0x73, 0x2a, 0x8e,
0x0d, 0xfc, 0xb5, 0x30, 0x71, 0xb4, 0x88, 0xc6, 0xe7, 0x2c, 0x4c, 0x8c, 0x8c, 0xf6, 0x73, 0x7e,
0x5f, 0x43, 0xf2, 0xb8, 0x77, 0xc5, 0x75, 0x55, 0x85, 0x57, 0xdf, 0xc3, 0xaf, 0x5f, 0x1b, 0xbd,
0xf6, 0x37, 0x0b, 0x91, 0xd1, 0x3e, 0xa8, 0x77, 0xb6, 0xea, 0x27, 0x44, 0x61, 0x09, 0x01, 0xc5,
0x47, 0x17, 0xc7, 0x82, 0xb6, 0x96, 0x9f, 0xa7, 0x25, 0x92, 0x78, 0x6b, 0xbc, 0x00, 0x49, 0x6a,
0xbe, 0x9e, 0xc0, 0xee, 0x4a, 0xdd, 0xfc, 0x23, 0x68, 0x1c, 0xcf, 0x04, 0xf3, 0x72, 0xcd, 0xbf,
0x59, 0xca, 0xde, 0xf8, 0x1b, 0x05, 0x25, 0x1d, 0x7f, 0x0b, 0xf2, 0xa7, 0x37, 0xb3, 0x23, 0x34,
0x34, 0x9c, 0xec, 0xcc, 0x7a, 0x6f, 0x33, 0xb6, 0x57, 0x4f, 0x45, 0xc1, 0x1c, 0x5e, 0x23, 0x03,
0x42, 0x22, 0x10, 0xa8, 0xb5, 0xc6, 0xe7, 0x09, 0x5f, 0x62, 0x57, 0xed, 0xda, 0xee, 0x08, 0x12,
0xf7, 0x3f, 0x61, 0x52, 0x51, 0xe9, 0xec, 0x23, 0xcb, 0x6d, 0x77, 0x29, 0x0a, 0x6e, 0x7e, 0x8c,
0xc0, 0x73, 0x89, 0x0d, 0x12, 0x4e, 0xad, 0x83, 0xf0, 0x11, 0xf8, 0x59, 0x82, 0x46, 0xc2, 0x36,
0x2f, 0xcf, 0x48, 0x2d, 0xfc, 0x37, 0x24, 0x5c, 0x76, 0x10, 0xd3, 0x3f, 0xc3, 0x7e, 0x89, 0xde,
0x6c, 0x57, 0x51, 0xdc, 0x50, 0x9d, 0xba, 0xa6, 0x8e, 0xf5, 0x17, 0x52, 0xfc, 0x0d, 0x89, 0x3b,
0x14, 0xc8, 0x15, 0x7c, 0x5e, 0xdf, 0x3c, 0x3b, 0xc2, 0x4b, 0x8e, 0x65, 0x71, 0x89, 0xf7, 0x99,
0x04, 0x8e, 0x03, 0xbc, 0x0a, 0x9f, 0xf1, 0xde, 0x32, 0x43, 0x38, 0x5c, 0xd9, 0x26, 0x77, 0x3b,
0xc6, 0xcb, 0x70, 0xbe, 0xc5, 0x0e, 0x54, 0x8f, 0x63, 0x75, 0xc3, 0x85, 0x04, 0x2f, 0xa8, 0x4e,
0xde, 0xc7, 0x37, 0xb7, 0x85, 0xf6, 0x98, 0x3b, 0x37, 0x77, 0xb7, 0x22, 0x96, 0xc6, 0x0e, 0x03,
0x60, 0x25, 0x28, 0x59, 0x11, 0xc0, 0xff, 0x9d, 0x3a, 0xb5, 0x16, 0x02, 0x36, 0x91, 0x65, 0x7c,
0x8f, 0x37, 0xc8, 0xee, 0xd1, 0x30, 0x16, 0x70, 0xba, 0xcc, 0x02, 0xbe, 0x82, 0x6b, 0xe5, 0x4f,
0xb5, 0x13, 0x47, 0x94, 0x08, 0x3b, 0x44, 0x09, 0x4c, 0x2a, 0x94, 0x77, 0x07, 0xd6, 0x74, 0xeb,
0xf7, 0x83, 0x6a, 0x77, 0xba, 0xe4, 0xab, 0x59, 0x67, 0xe1, 0x91, 0x70, 0x47, 0x17, 0x02, 0x22,
0x27, 0x65, 0x73, 0x3f, 0x7b, 0x58, 0x84, 0xa2, 0xdd, 0xc7, 0xe5, 0x79, 0xc1, 0x3b, 0xbb, 0x32,
0xe0, 0x05, 0xa6, 0xeb, 0x75, 0xec, 0x94, 0x16, 0x07, 0x6f, 0x29, 0xed, 0x7d, 0x70, 0x53, 0x82,
0xc4, 0x54, 0x03, 0x96, 0x27, 0x2e, 0x88, 0x98, 0x31, 0xc0, 0xd9, 0xa7, 0x80, 0xc8, 0xb4, 0x0e,
0x3b, 0x67, 0xe7, 0x81, 0x1b, 0x8c, 0x32, 0x0d, 0xe8, 0x0d, 0x6d, 0x28, 0x58, 0xd8, 0xeb, 0xff,
0xc1, 0x1a, 0x14, 0xe4, 0x33, 0x93, 0x94, 0xe6, 0x67, 0xb6, 0x64, 0x74, 0x60, 0x98, 0xa3, 0xb8,
0x6f, 0x34, 0xf1, 0x8f, 0x4c, 0xe8, 0xf4, 0xa8, 0x8d, 0xa9, 0x2f, 0x67, 0x9b, 0xf0, 0x93, 0x76,
0xc9, 0x4e, 0x47, 0x57, 0x53, 0x93, 0x6e, 0x5c, 0xae, 0x57, 0x31, 0x07, 0x2d, 0xb5, 0xfb, 0xc3,
0x27, 0xc7, 0xe4, 0x13, 0xee, 0xf9, 0x7e, 0xa6, 0x05, 0x76, 0x4d, 0x13, 0xa5, 0x7f, 0x2f, 0xaa,
0xb0, 0x55, 0x36, 0x77, 0xf7, 0xbe, 0x16, 0xa1, 0x77, 0x7b, 0xea, 0x6e, 0x5b, 0x67, 0xf2, 0x70,
0x41, 0xf7, 0x6d, 0x55, 0xf3, 0xc8, 0x2b, 0x6d, 0xba, 0x0b, 0x54, 0x3c, 0x0c, 0x97, 0xf1, 0x7c,
0xb3, 0x7c, 0x30, 0xb5, 0x05, 0x0a, 0xd7, 0x65, 0xc0, 0x96, 0x9b, 0x0d, 0x42, 0x97, 0xc8, 0x42,
0xbc, 0x5a, 0x59, 0x9e, 0xe0, 0x06, 0x3b, 0x0f, 0x9c, 0x73, 0xb8, 0xba, 0x6c, 0xe6, 0x41, 0xf5,
0xb9, 0xb1, 0xb2, 0x3f, 0x7c, 0xf6, 0x86, 0xc0, 0x58, 0xbd, 0x75, 0x17, 0xe9, 0x22, 0x53, 0x48,
0x16, 0x0e, 0xf6, 0x05, 0xad, 0xe0, 0xdb, 0xf1, 0xcb, 0x5e, 0x52, 0xdf, 0x0e, 0x44, 0x5e, 0x25,
0x63, 0x88, 0x30, 0x06, 0xb4, 0xa4, 0xc7, 0x61, 0x45, 0x59, 0x4a, 0x3a, 0x45, 0xbb, 0x8c, 0xe4,
0x9a, 0x06, 0x43, 0x78, 0x43, 0xe9, 0x38, 0x06, 0xf0, 0xc3, 0xb7, 0xd2, 0x82, 0x57, 0x30, 0xaa,
0x55, 0xe0, 0x9a, 0x22, 0x4d, 0xe3, 0xd5, 0xbd, 0x3f, 0x44, 0x52, 0xb0, 0x77, 0x55, 0x48, 0x88,
0x38, 0x06, 0x5f, 0x11, 0x79, 0x0d, 0xf4, 0x43, 0x2b, 0x00, 0x16, 0x92, 0x11, 0xea, 0xc3, 0x91,
0x25, 0xf0, 0x18, 0xf7, 0x7c, 0xa2, 0xaa, 0x3f, 0x47, 0x5c, 0x70, 0x2b, 0x60, 0x12, 0x01, 0xfa,
0x01, 0xee, 0xd7, 0x96, 0xf4, 0xb1, 0xb3, 0xe6, 0x8d, 0xfe, 0x0f, 0x2b, 0x4b, 0x6d, 0x3c, 0xb4,
0xb7, 0xae, 0xdf, 0x00, 0x18, 0xda, 0x84, 0x29, 0x35, 0xf1, 0xf6, 0x53, 0x15, 0xfa, 0xdc, 0x6e,
0x59, 0x10, 0x54, 0xea, 0x5e, 0xcd, 0x6c, 0x22, 0xe0, 0x39, 0x1f, 0xf2, 0x90, 0xab, 0x0d, 0x87,
0xad, 0xcb, 0x45, 0x47, 0x6b, 0xbf, 0x20, 0xdb, 0xdb, 0x5e, 0x03, 0xb7, 0x07, 0x18, 0xd2, 0x5e,
0xab, 0xc0, 0x56, 0xfd, 0xf7, 0x7f, 0x40, 0xcb, 0xc4, 0xa4, 0xb1, 0x61, 0xc4, 0xe0, 0xc0, 0x25,
0x78, 0x19, 0xf2, 0x21, 0x36, 0xf1, 0xaa, 0x2f, 0xf4, 0x01, 0xc8, 0x8b, 0xc0, 0xab, 0xe0, 0x02,
0xc0, 0xd7, 0x55, 0x03, 0x1f, 0x32, 0x91, 0x11, 0x00, 0xb7, 0x0c, 0xff, 0xcf, 0x9c, 0x1a, 0x20,
0x1b, 0x27, 0xbf, 0xf3, 0x77, 0x73, 0x9e, 0x47, 0x86, 0x83, 0xb3, 0x84, 0x98, 0xe2, 0x34, 0x39,
0x68, 0xc3, 0x13, 0xc3, 0xf3, 0xc0, 0xbf, 0xa2, 0x14, 0xe7, 0xf1, 0xe0, 0x4f, 0x3a, 0x2d, 0xdb,
0x77, 0x22, 0xef, 0x00, 0x53, 0xc4, 0xb2, 0xf2, 0x16, 0xc1, 0x79, 0x11, 0xaa, 0x8f, 0x32, 0x3e,
0x81, 0xd0, 0xf2, 0x22, 0xe0, 0x0e, 0xc0, 0xe3, 0xb0, 0x5b, 0x79, 0xd3, 0x16, 0xed, 0x2b, 0x91,
0xf5, 0x54, 0xa1, 0x27, 0x98, 0xeb, 0x5c, 0x02, 0x5c, 0x09, 0xaf, 0x86, 0x91, 0xd0, 0x36, 0x61,
0xc8, 0x89, 0x55, 0xbb, 0xd1, 0x35, 0x5e, 0xb4, 0x32, 0xb1, 0xdb, 0xdb, 0xdb, 0x4b, 0x5b, 0x63,
0xcb, 0x84, 0x26, 0x27, 0x8d, 0x1b, 0x68, 0xfe, 0x5f, 0x4b, 0xb3, 0xb8, 0x67, 0xf7, 0x7d, 0x55,
0x82, 0xb2, 0x13, 0xbd, 0x49, 0xf0, 0xd6, 0x0e, 0xef, 0x62, 0x5b, 0x67, 0x59, 0xfb, 0x17, 0xdb,
0xd8, 0x18, 0x4d, 0xcc, 0xbb, 0xfa, 0x61, 0xab, 0xb5, 0xbc, 0xf6, 0x29, 0xaf, 0x10, 0x6d, 0x34,
0x0a, 0xa7, 0x50, 0x7f, 0xfb, 0xd5, 0xb0, 0x53, 0x59, 0xfb, 0xce, 0x8a, 0x25, 0x37, 0xad, 0x3e,
0xe5, 0xaa, 0x9b, 0xae, 0x4c, 0x88, 0xbe, 0x04, 0x4f, 0x81, 0x79, 0x8f, 0xa5, 0x3f, 0x6f, 0x15,
0x31, 0xc4, 0xb8, 0x15, 0xe8, 0x18, 0x91, 0x13, 0x80, 0x07, 0x54, 0xeb, 0x30, 0x35, 0x20, 0xcd,
0x33, 0x22, 0x07, 0x81, 0xdf, 0x8b, 0x14, 0x19, 0xe7, 0xa6, 0x3b, 0x4b, 0xca, 0x0b, 0x2e, 0xa2,
0x1d, 0x7b, 0x73, 0xb1, 0x74, 0x6c, 0xa2, 0x28, 0xfa, 0x23, 0x30, 0x46, 0xaf, 0x04, 0x8a, 0x38,
0x26, 0x4d, 0x7f, 0x00, 0x91, 0x14, 0x86, 0x0f, 0x9d, 0x1d, 0x11, 0x1f, 0x15, 0x39, 0x0f, 0xb8,
0x55, 0xbb, 0x86, 0xd3, 0x3f, 0x00, 0x91, 0x16, 0xaa, 0xe9, 0x25, 0xfa, 0xfe, 0xdf, 0x00, 0x9f,
0xb3, 0xaf, 0x1d, 0x78, 0x45, 0xe0, 0xfe, 0xcc, 0x78, 0xb7, 0x7e, 0x9f, 0xe0, 0x9a, 0xd7, 0x7b,
0xbb, 0xe2, 0x1f, 0x67, 0xd4, 0x9f, 0xb0, 0xc6, 0xe0, 0xcc, 0x61, 0x6f, 0x6f, 0x01, 0x51, 0xe9,
0xff, 0x88, 0xf2, 0xa8, 0x6b, 0x95, 0xb1, 0xea, 0x78, 0xa7, 0x37, 0x85, 0x34, 0x42, 0xf4, 0x6c,
0x76, 0x0b, 0x6e, 0x7a, 0x01, 0x5f, 0x8a, 0xcc, 0x56, 0xfe, 0xfa, 0xc7, 0x57, 0xe8, 0x26, 0x44,
0x95, 0xe3, 0x4c, 0x35, 0x35, 0x8b, 0x7a, 0xaa, 0xf1, 0x5f, 0x32, 0x5a, 0xf2, 0x22, 0xc0, 0x8f,
0xec, 0x8b, 0xce, 0xff, 0x8e, 0x37, 0x3c, 0x36, 0xbc, 0x47, 0x44, 0x50, 0x26, 0xbe, 0x43, 0x22,
0xff, 0x7d, 0x4d, 0xf6, 0x38, 0x12, 0xf1, 0xdf, 0xc2, 0x2d, 0x86, 0xb1, 0xa5, 0x2b, 0xb1, 0x3c,
0x22, 0x27, 0x61, 0x94, 0x3b, 0x14, 0xc7, 0xb5, 0x28, 0x0f, 0x85, 0xdd, 0xe0, 0x16, 0x01, 0xd2,
0xb9, 0x8e, 0xfb, 0x01, 0x4b, 0x25, 0x7c, 0x4b, 0xf4, 0xb8, 0x44, 0x41, 0xc2, 0x2e, 0xf3, 0xbe,
0xd1, 0x2d, 0x27, 0x8a, 0x31, 0xf0, 0xe2, 0x28, 0x00, 0x24, 0x9e, 0x3f, 0x30, 0xb0, 0xaf, 0xcc,
0xf5, 0xb7, 0x52, 0xf3, 0x72, 0x50, 0xa1, 0x70, 0xfe, 0xaa, 0x82, 0xa1, 0x69, 0xbd, 0x6b, 0x78,
0x6b, 0x7f, 0x17, 0xb6, 0x39, 0x23, 0x1c, 0xf8, 0x9b, 0xf0, 0x27, 0x44, 0x00, 0x19, 0x21, 0x1b,
0x0b, 0xe8, 0x1f, 0x4b, 0x8f, 0x86, 0xde, 0x15, 0xb9, 0x7a, 0xa9, 0xd9, 0xae, 0x9c, 0xca, 0x5d,
0xbf, 0xef, 0xe8, 0x1b, 0xcc, 0xb5, 0x6a, 0x30, 0x9e, 0x30, 0xe1, 0xb7, 0x57, 0x9b, 0xcb, 0x7c,
0x16, 0xe8, 0x31, 0x4e, 0x0e, 0xbc, 0x4f, 0xf3, 0x72, 0xbb, 0xb5, 0x3c, 0x10, 0xaf, 0x54, 0xc2,
0xe9, 0x11, 0x53, 0xc0, 0xfa, 0x7f, 0xf0, 0x39, 0xb8, 0xae, 0x4c, 0xef, 0x13, 0x6e, 0x19, 0x4e,
0xe5, 0x96, 0x21, 0xb8, 0x5a, 0x19, 0x9f, 0x6a, 0x33, 0xaf, 0x02, 0x8e, 0x72, 0xbb, 0x86, 0xca,
0x02, 0xbe, 0xfa, 0x1b, 0x95, 0x95, 0x05, 0x3d, 0xe4, 0x76, 0x5e, 0x22, 0x15, 0xac, 0xc7, 0xaf,
0x57, 0xfb, 0x72, 0x22, 0xb0, 0x3e, 0x2f, 0x05, 0x08, 0x3c, 0x94, 0xc0, 0x0c, 0xb6, 0x3b, 0x7f,
0xba, 0xa1, 0x3e, 0x2f, 0xdd, 0xb2, 0x3d, 0xfc, 0xc8, 0xe8, 0xe2, 0x48, 0x88, 0x35, 0xf0, 0xf5,
0x51, 0xc6, 0x5f, 0x66, 0x02, 0xf1, 0xce, 0xf0, 0xad, 0x31, 0x38, 0x5c, 0x71, 0xa6, 0x0c, 0xe7,
0xcb, 0x3d, 0x7b, 0xbc, 0x2b, 0x5c, 0x3b, 0xd3, 0x76, 0xcc, 0x7e, 0x8c, 0xad, 0xb1, 0x95, 0x9f,
0x1a, 0xfb, 0x8a, 0xf7, 0x86, 0x6d, 0x8c, 0x88, 0xe7, 0xc1, 0x8e, 0xf4, 0xcf, 0x68, 0x49, 0x70,
0xa4, 0xc2, 0xa4, 0x9e, 0xbd, 0xc4, 0xc5, 0xdb, 0x8b, 0x72, 0xf9, 0x17, 0xfb, 0x8f, 0x21, 0xc8,
0x8c, 0xad, 0xb5, 0x6f, 0x9f, 0x7a, 0x4e, 0x8e, 0xd5, 0xf0, 0xf0, 0xab, 0x9b, 0xf9, 0xd7, 0x0f,
0x01, 0x22, 0x1d, 0x18, 0x56, 0xfd, 0xba, 0x9b, 0x0a, 0xb9, 0xe7, 0x5f, 0x16, 0xbb, 0x5f, 0x7b,
0x8a, 0x8b, 0x5c, 0x2e, 0xe8, 0xdf, 0x22, 0x5c, 0x6f, 0xa3, 0x31, 0x67, 0x58, 0x04, 0x63, 0x9e,
0xfe, 0x3c, 0x16, 0xec, 0xfa, 0xfe, 0x8e, 0xea, 0xdf, 0x2a, 0x9f, 0xa8, 0x1f, 0x67, 0x0f, 0xea,
0xe0, 0x3b, 0x36, 0x06, 0x83, 0x0d, 0x90, 0x26, 0xfa, 0x85, 0x0f, 0xff, 0xe7, 0x6b, 0xc3, 0x5c,
0x3d, 0xc9, 0xbc, 0xcf, 0x08, 0x10, 0x0a, 0x03, 0xcd, 0x5e, 0x2f, 0xd3, 0x72, 0x11, 0xad, 0xbe,
0x57, 0x02, 0xda, 0x1d, 0x27, 0xce, 0xb5, 0x26, 0xb9, 0x31, 0x79, 0xad, 0x6e, 0x9d, 0xf2, 0x35,
0x8f, 0x0a, 0xce, 0xec, 0xef, 0x71, 0x1c, 0xb6, 0x6f, 0x77, 0x64, 0x8d, 0x1e, 0x44, 0x3c, 0x0b,
0x17, 0x8a, 0x2a, 0x2a, 0xf4, 0xdf, 0xc6, 0x0b, 0xf0, 0xb4, 0x69, 0xd5, 0x1c, 0xf6, 0x25, 0x5f,
0x1a, 0xce, 0xb5, 0x91, 0x2a, 0x32, 0x45, 0xb5, 0xf8, 0x06, 0x83, 0x72, 0xeb, 0xef, 0xe0, 0xc2,
0xcc, 0xb3, 0x87, 0xf5, 0x33, 0xb7, 0x70, 0xcd, 0xb2, 0x69, 0x83, 0xbb, 0x06, 0x25, 0x4d, 0xab,
0x05, 0x1d, 0x1a, 0x6a, 0x34, 0x5c, 0xfc, 0xd6, 0x15, 0x73, 0xb7, 0x7a, 0x78, 0x33, 0xda, 0x6d,
0x7c, 0x46, 0x4c, 0xed, 0x06, 0x47, 0x39, 0x6e, 0x08, 0x6a, 0x5f, 0xa6, 0xd0, 0xe9, 0x1a, 0x7d,
0xdb, 0x54, 0x2c, 0x5a, 0x74, 0xc4, 0x69, 0x79, 0x45, 0xbb, 0x53, 0xe0, 0x25, 0x09, 0xff, 0x00,
0xea, 0x1c, 0x01, 0x94, 0x81, 0x2b, 0x98, 0x5f, 0x37, 0xb2, 0x4f, 0x77, 0xc7, 0x9e, 0x24, 0x1b,
0x58, 0x39, 0xd3, 0x0d, 0xe8, 0x21, 0x7a, 0xc0, 0x63, 0xc4, 0xb0, 0xcf, 0x80, 0x51, 0x32, 0x23,
0xb5, 0x1f, 0xb8, 0xc0, 0x28, 0xd1, 0x05, 0xd6, 0x9e, 0x18, 0x3e, 0x08, 0x1b, 0x2c, 0xf7, 0x81,
0xd3, 0xe2, 0x04, 0xbd, 0x6f, 0x38, 0x0b, 0x64, 0x9c, 0xcf, 0x9c, 0x38, 0x4e, 0xaf, 0xdf, 0x6b,
0x9d, 0x6f, 0x78, 0x2f, 0x02, 0xc2, 0x1c, 0x6f, 0xc2, 0xd5, 0x47, 0xad, 0xf0, 0x75, 0xc9, 0xc2,
0xb9, 0xc3, 0x42, 0x6c, 0x38, 0x6b, 0x78, 0x1e, 0x10, 0x18, 0xf7, 0x90, 0x90, 0x3b, 0xde, 0x9c,
0x4c, 0x81, 0x4c, 0x58, 0x4a, 0x64, 0xc6, 0x5b, 0xf3, 0xb8, 0x59, 0x8d, 0xff, 0x3f, 0x95, 0x54,
0xad, 0xc3, 0x8b, 0xca, 0xbc, 0xc6, 0x1b, 0xdb, 0x75, 0x54, 0x31, 0x63, 0xa3, 0xa7, 0x02, 0x3c,
0x75, 0x3d, 0xd6, 0x58, 0xf3, 0xb8, 0xb9, 0x8d, 0xb1, 0x4f, 0x4f, 0xc9, 0x04, 0x31, 0x00, 0x24,
0x19, 0x2f, 0x83, 0x5f, 0x60, 0x04, 0xf4, 0x74, 0x8d, 0xfb, 0x35, 0x2a, 0x3e, 0x0e, 0x6f, 0x81,
0xb3, 0xda, 0x7b, 0x16, 0xa3, 0x31, 0x6f, 0xdf, 0x6a, 0xaa, 0xdc, 0x7f, 0xf5, 0xb1, 0xd5, 0x60,
0xdb, 0x01, 0x40, 0x51, 0x5d, 0x4a, 0x61, 0x80, 0x3e, 0x2c, 0x86, 0x01, 0x87, 0x93, 0x17, 0x99,
0x58, 0x60, 0x99, 0xda, 0x77, 0x67, 0x14, 0x70, 0xc1, 0x98, 0xb8, 0x4b, 0x73, 0x2a, 0xc4, 0xc5,
0xfe, 0x36, 0xc2, 0x74, 0x66, 0x50, 0xbe, 0x9a, 0xcb, 0x18, 0xd2, 0x1c, 0x7c, 0x2a, 0xe8, 0xff,
0xfc, 0x21, 0xd8, 0xe2, 0x55, 0xa1, 0x37, 0xeb, 0x58, 0xdb, 0xe4, 0x5e, 0x9f, 0xa8, 0xba, 0xe7,
0xcf, 0x82, 0x13, 0x72, 0x9f, 0xae, 0x96, 0x0f, 0x1d, 0x97, 0xb6, 0x69, 0x8b, 0x4d, 0xb7, 0x79,
0xd7, 0x4b, 0x96, 0x76, 0xe7, 0x7f, 0x86, 0xd9, 0x03, 0xaf, 0xab, 0x6a, 0x10, 0x33, 0x01, 0x27,
0x75, 0x78, 0x11, 0x42, 0x67, 0x84, 0xf3, 0x3b, 0xb4, 0x3c, 0xd0, 0x33, 0x6f, 0xae, 0x7e, 0x06,
0xc1, 0x50, 0xd0, 0xde, 0xed, 0x7a, 0xd7, 0x3d, 0xe0, 0x55, 0x0d, 0x77, 0x1c, 0x94, 0x86, 0xac,
0x07, 0x35, 0x31, 0xdc, 0x38, 0x4c, 0x96, 0x7c, 0x01, 0x79, 0x95, 0x86, 0x83, 0x0b, 0x0a, 0x61,
0x7d, 0x55, 0xa8, 0xd5, 0x28, 0xd1, 0x10, 0x81, 0x4a, 0x55, 0x0c, 0x02, 0xf1, 0x13, 0x5f, 0x85,
0x3f, 0x5f, 0x85, 0xaa, 0x1d, 0x6f, 0x36, 0x69, 0x9f, 0xf4, 0x17, 0x36, 0x6d, 0x8d, 0xbe, 0xe1,
0x86, 0xe3, 0x96, 0xc6, 0x9c, 0x5c, 0x26, 0xdc, 0x69, 0x1c, 0x5d, 0x5a, 0xd1, 0xc1, 0xc2, 0x10,
0xb2, 0xcf, 0x75, 0x95, 0xd1, 0xd9, 0x54, 0x65, 0x8e, 0xb1, 0x58, 0xa5, 0xee, 0xdd, 0xa5, 0xb5,
0xa3, 0xed, 0x32, 0x3a, 0x32, 0x4c, 0xb4, 0x5c, 0x4a, 0xa5, 0x4f, 0x89, 0xd5, 0xeb, 0x62, 0xce,
0xcf, 0x96, 0x46, 0x0d, 0xe5, 0xe8, 0x7c, 0x72, 0xfd, 0x21, 0x49, 0x06, 0xde, 0x0e, 0x45, 0x5c,
0xf0, 0x72, 0xf8, 0x23, 0x3d, 0xa6, 0x41, 0x9b, 0xb0, 0x72, 0xc6, 0x1a, 0xa5, 0x5a, 0xe1, 0x62,
0xa3, 0x83, 0xdb, 0x4a, 0x9f, 0x47, 0x25, 0x96, 0xf8, 0x63, 0x59, 0x19, 0x38, 0xb5, 0x5f, 0x4f,
0xea, 0x4f, 0xe1, 0x05, 0xa8, 0x53, 0x54, 0x42, 0x19, 0x79, 0xd1, 0xc8, 0x19, 0x1d, 0x99, 0xee,
0xc4, 0x31, 0x16, 0xb8, 0x33, 0x6c, 0xe6, 0xf0, 0x2d, 0x51, 0x74, 0x7c, 0x74, 0x62, 0xe7, 0xae,
0x0b, 0x4c, 0x71, 0x4b, 0x8d, 0x1c, 0x34, 0x21, 0xc6, 0xef, 0x16, 0x39, 0xcb, 0x1c, 0x41, 0x63,
0x05, 0x9b, 0xaf, 0x2a, 0x3e, 0x61, 0x5f, 0x05, 0xbb, 0x75, 0x4b, 0x94, 0x96, 0x7c, 0x19, 0xdb,
0x4e, 0xc1, 0xf9, 0x9d, 0x67, 0x50, 0xc7, 0x20, 0xd3, 0x00, 0x8d, 0x7b, 0x1c, 0x90, 0xb9, 0x7c,
0xc0, 0xa7, 0xb7, 0x2f, 0x22, 0x07, 0x7b, 0xbf, 0xdb, 0x80, 0xd8, 0x2a, 0xa1, 0x6b, 0xc9, 0x4e,
0xd5, 0xc1, 0x24, 0x06, 0xab, 0x07, 0x72, 0x61, 0x90, 0x58, 0xfc, 0xc2, 0xf3, 0x6f, 0x30, 0x09,
0xa4, 0xa4, 0x54, 0x80, 0xc6, 0xe6, 0x3a, 0x23, 0x6b, 0x18, 0x12, 0xba, 0x13, 0x7f, 0xdd, 0x71,
0xf6, 0xd4, 0xb6, 0x48, 0x9b, 0xa3, 0xdd, 0xff, 0xf0, 0x7f, 0x46, 0x8f, 0x67, 0x87, 0xd9, 0x85,
0xcd, 0x43, 0xed, 0xfe, 0xe6, 0xf6, 0x25, 0x95, 0x75, 0xc1, 0x4b, 0xd9, 0x75, 0x54, 0x85, 0x77,
0xad, 0xc3, 0xff, 0xce, 0xbc, 0x42, 0x8a, 0xb8, 0x0c, 0x88, 0xeb, 0xc1, 0x00, 0x0c, 0xdc, 0x70,
0x8c, 0x90, 0x77, 0xf1, 0x4f, 0x7c, 0x3e, 0xf4, 0x54, 0x2d, 0xae, 0xd5, 0xbe, 0x01, 0x7e, 0xf6,
0xd5, 0x7c, 0x6a, 0xe7, 0x1c, 0x95, 0x2d, 0x0c, 0xbb, 0xdb, 0x55, 0x5d, 0x56, 0x77, 0xfc, 0x38,
0xf8, 0x7d, 0xb6, 0x8a, 0x62, 0xd4, 0x26, 0xef, 0x92, 0x03, 0xd5, 0x83, 0xe7, 0xb0, 0x6d, 0x80,
0xff, 0xcc, 0xec, 0xa6, 0xc3, 0x80, 0x2a, 0x21, 0x02, 0x5b, 0xa1, 0x0c, 0xfe, 0xc2, 0x26, 0x65,
0x0a, 0xb5, 0x1d, 0x6f, 0x70, 0x7d, 0xfe, 0x8d, 0xb4, 0xc6, 0xff, 0x08, 0xca, 0x1a, 0xb5, 0x4a,
0x52, 0x31, 0xd4, 0x71, 0xc6, 0xcf, 0xe5, 0x3e, 0xdf, 0x15, 0x7c, 0xb3, 0xf0, 0xfa, 0x59, 0x2c,
0x7d, 0xc3, 0x27, 0xc7, 0x46, 0xf6, 0x65, 0xd6, 0x7a, 0x0f, 0xf7, 0xfe, 0xc3, 0x6c, 0xeb, 0x9d,
0xa2, 0x2a, 0x73, 0x1d, 0x2d, 0xf0, 0xb1, 0xb1, 0xff, 0x31, 0xf0, 0x9b, 0x35, 0x3e, 0x02, 0x44,
0x53, 0x70, 0x72, 0x54, 0x8f, 0xb0, 0x92, 0x0a, 0x45, 0x83, 0xb2, 0xea, 0x35, 0xb7, 0x72, 0x5e,
0x9b, 0xf0, 0x4e, 0x89, 0x10, 0x92, 0x0e, 0x93, 0xdc, 0xf8, 0x87, 0xd2, 0x0b, 0x6c, 0x3f, 0x4c,
0x82, 0xd5, 0x52, 0x02, 0x03, 0x30, 0x0d, 0x3a, 0x86, 0x01, 0x88, 0x40, 0xa5, 0x2a, 0x92, 0x04,
0x7f, 0x32, 0xa1, 0x33, 0x78, 0xd5, 0xd6, 0x03, 0xbc, 0x8c, 0xec, 0x6b, 0x75, 0xbe, 0x43, 0x99,
0x18, 0x32, 0xc2, 0xd7, 0x34, 0x75, 0xb0, 0x86, 0x45, 0x48, 0xf9, 0x4a, 0xd5, 0x13, 0x4b, 0x1c,
0xf1, 0x39, 0x4c, 0x69, 0x30, 0x8b, 0xe1, 0x33, 0x62, 0xdc, 0x62, 0x52, 0x9c, 0x0a, 0xc2, 0x44,
0x60, 0xcf, 0x9d, 0xa9, 0xe4, 0x19, 0xb9, 0x3c, 0xba, 0x8f, 0x00, 0x25, 0xd9, 0x6f, 0x47, 0xd8,
0xee, 0xb0, 0x3f, 0x06, 0xd5, 0x56, 0x71, 0xf5, 0x8a, 0xc7, 0x05, 0xc8, 0xec, 0xc0, 0xec, 0x7a,
0x16, 0xdd, 0x1c, 0x95, 0x99, 0xfc, 0xae, 0xaa, 0x74, 0x4e, 0x84, 0x92, 0x79, 0x18, 0x44, 0x82,
0x24, 0x64, 0x9a, 0xf0, 0xf6, 0xd2, 0xb3, 0xe1, 0xb4, 0xb3, 0x35, 0x0f, 0x09, 0xeb, 0xad, 0x36,
0xb0, 0x2a, 0x73, 0x6b, 0xaf, 0xe0, 0x78, 0xf5, 0x02, 0x0a, 0xcd, 0x0c, 0x62, 0xf0, 0xb9, 0x94,
0x42, 0x6c, 0xb5, 0xb2, 0x07, 0x6f, 0x87, 0x0a, 0x0a, 0xb4, 0x8a, 0x63, 0xb9, 0x21, 0x86, 0xd3,
0x0f, 0x67, 0xf9, 0x8a, 0x03, 0xef, 0xf0, 0x81, 0x6f, 0xfc, 0x25, 0x93, 0x1d, 0x60, 0x1c, 0x03,
0xda, 0x52, 0x63, 0x3e, 0xd6, 0x44, 0x29, 0x78, 0x2a, 0xac, 0x66, 0xd7, 0x9d, 0xbd, 0x41, 0xa9,
0xb9, 0xef, 0x09, 0x2e, 0x3d, 0xc0, 0xe5, 0x79, 0x9b, 0x3b, 0x12, 0x22, 0x46, 0x80, 0x07, 0x25,
0x8d, 0x6f, 0x0e, 0x4a, 0xb4, 0x36, 0x64, 0xec, 0xa6, 0x1c, 0x17, 0x62, 0xe2, 0x67, 0xee, 0x73,
0xc2, 0x36, 0xcb, 0xcf, 0x5e, 0x53, 0x54, 0x7e, 0x94, 0x24, 0x95, 0x80, 0x1d, 0x5e, 0xe9, 0xde,
0x99, 0x1d, 0xd9, 0xc4, 0xf5, 0x7f, 0x6c, 0xf0, 0x9e, 0x67, 0x6d, 0xef, 0x67, 0xea, 0x4f, 0x45,
0x97, 0x81, 0x0a, 0x7b, 0x30, 0x28, 0x4d, 0xa5, 0xd6, 0x98, 0xe1, 0x9e, 0xa3, 0x6d, 0xf8, 0x5b,
0x00, 0xc0, 0xa8, 0x55, 0x4d, 0x1a, 0x4d, 0x69, 0xfc, 0xe4, 0x79, 0x3d, 0x3f, 0xc6, 0xa7, 0xb9,
0xb1, 0xea, 0x1e, 0x37, 0xc1, 0xd1, 0x86, 0xa5, 0xef, 0xaf, 0xcf, 0xcd, 0xf1, 0x77, 0xa7, 0x6c,
0x1a, 0x9d, 0xc7, 0xd0, 0xc9, 0x0e, 0x77, 0x30, 0xd9, 0x96, 0xaf, 0xea, 0xde, 0x3f, 0xcf, 0xd2,
0xab, 0x95, 0x25, 0x2a, 0xd6, 0xa2, 0xa7, 0x46, 0xc6, 0x2f, 0x15, 0x99, 0x04, 0x2f, 0x92, 0x6d,
0x7a, 0xbc, 0x55, 0xa1, 0xeb, 0x1f, 0x8c, 0xab, 0x82, 0x78, 0x8f, 0xcb, 0x3b, 0xec, 0x02, 0xbd,
0xbf, 0x30, 0xda, 0x18, 0x8f, 0x2d, 0x27, 0xc4, 0x0a, 0xb6, 0x1b, 0x47, 0x12, 0xae, 0x73, 0x16,
0x56, 0x9f, 0xea, 0xa9, 0x4a, 0xc6, 0x37, 0xc7, 0x02, 0x42, 0xf6, 0xf0, 0xfd, 0x4d, 0x0c, 0xec,
0xc6, 0x4e, 0x5a, 0xfe, 0x5f, 0x55, 0x60, 0xac, 0x4c, 0xef, 0x11, 0x7c, 0x5a, 0xd7, 0x07, 0x25,
0x07, 0xb7, 0xfd, 0xed, 0x51, 0xb6, 0x83, 0x92, 0x30, 0xd5, 0x21, 0xe5, 0xaf, 0xec, 0x6d, 0xff,
0x04, 0xb2, 0xb1, 0xac, 0x62, 0x2d, 0xcd, 0x7c, 0xde, 0xed, 0xb2, 0xbc, 0x42, 0xb8, 0xff, 0x61,
0x59, 0xd7, 0xf7, 0x5a, 0x5d, 0xda, 0x2f, 0x80, 0xed, 0xae, 0xa6, 0xe1, 0x91, 0x8d, 0x09, 0x64,
0xf7, 0x56, 0x8d, 0x7a, 0xdd, 0xc8, 0xfb, 0x5b, 0x7f, 0xb9, 0x8e, 0x47, 0xa7, 0x3b, 0x30, 0x77,
0x42, 0xc2, 0x7c, 0x4c, 0x5e, 0x4f, 0xd4, 0xd9, 0x5d, 0xef, 0xbf, 0x4c, 0x7e, 0x12, 0x23, 0x91,
0x6a, 0x9c, 0xa1, 0xb8, 0x2a, 0xef, 0x73, 0x28, 0xe7, 0x77, 0xb7, 0xe8, 0x8f, 0x14, 0x9d, 0x31,
0x04, 0xb8, 0x97, 0xf0, 0xb9, 0xdd, 0xef, 0x01, 0x3d, 0x6b, 0x1e, 0xcc, 0x35, 0x9a, 0xac, 0xfe,
0xe9, 0x2c, 0xb3, 0xca, 0xf8, 0x27, 0x56, 0xd0, 0xb9, 0x53, 0x74, 0x09, 0x4d, 0xae, 0x5c, 0x8b,
0xbc, 0xc4, 0x4b, 0xdb, 0x75, 0x55, 0x51, 0x57, 0x68, 0x51, 0x69, 0x74, 0x8d, 0xa7, 0xcf, 0x24,
0x5d, 0xc5, 0xf6, 0x79, 0xaf, 0xc6, 0xae, 0x45, 0x5d, 0x44, 0x80, 0x12, 0x0a, 0x7d, 0x27, 0x37,
0x1c, 0x93, 0x8c, 0x0c, 0x1b, 0x7e, 0x39, 0x24, 0x4d, 0x58, 0xa1, 0xe4, 0x98, 0x61, 0x10, 0xf2,
0xfa, 0xe7, 0x52, 0x37, 0x80, 0xa8, 0x60, 0x35, 0x7c, 0x8a, 0x49, 0xa3, 0x44, 0x23, 0x20, 0xb5,
0x18, 0x9a, 0xe6, 0x51, 0x16, 0x75, 0xdf, 0x46, 0x57, 0x00, 0xea, 0xaf, 0x86, 0xe1, 0xa6, 0x68,
0x84, 0x62, 0xb7, 0x7b, 0xf3, 0xf5, 0x3b, 0x3f, 0x14, 0xed, 0x5f, 0xed, 0x13, 0xd1, 0x94, 0x9f,
0xc7, 0x16, 0x0b, 0x18, 0xa5, 0x1d, 0xf7, 0x27, 0xad, 0xe6, 0x5f, 0x39, 0x2d, 0x48, 0xd9, 0x24,
0x9a, 0x50, 0xfe, 0x70, 0x6a, 0xd7, 0xbb, 0x75, 0x77, 0x98, 0x3e, 0x3b, 0x6c, 0xfc, 0xa3, 0xa1,
0xe4, 0x9d, 0x35, 0x3e, 0x02, 0x42, 0xc7, 0xf0, 0xfa, 0x5b, 0x10, 0xd9, 0x1c, 0x94, 0x44, 0x7c,
0x9f, 0x55, 0x60, 0xa8, 0x26, 0x6f, 0x64, 0x9e, 0xcd, 0x65, 0x62, 0xf6, 0x07, 0x24, 0x35, 0xe7,
0x72, 0x52, 0x1a, 0xb0, 0x6f, 0x0d, 0x8b, 0xa5, 0xbf, 0x6b, 0x94, 0xe6, 0x27, 0xb6, 0xcf, 0x4d,
0x72, 0x30, 0x59, 0xbc, 0xd4, 0x57, 0x4b, 0xb5, 0xbf, 0x59, 0x71, 0x2c, 0x7c, 0x8a, 0x69, 0xa3,
0x79, 0x86, 0x32, 0x99, 0x6b, 0xa6, 0x83, 0x4f, 0x48, 0xd7, 0xbe, 0xdc, 0xd2, 0x29, 0x04, 0x73,
0x72, 0xaf, 0x37, 0x4c, 0x09, 0x30, 0x33, 0xdf, 0x60, 0xb6, 0x9e, 0x6f, 0x8f, 0x77, 0x1f, 0x33,
0x5e, 0x8a, 0x75, 0x1e, 0x6b, 0xc0, 0xed, 0x81, 0xcf, 0xc5, 0xf0, 0xc3, 0xf0, 0x95, 0xd3, 0x3b,
0x47, 0x42, 0xe6, 0x29, 0xb4, 0xef, 0x6f, 0x12, 0x63, 0x3d, 0x98, 0xa3, 0x4f, 0x81, 0xc3, 0xfc,
0xb3, 0xf4, 0x35, 0xb7, 0x2c, 0xa7, 0x0e, 0xeb, 0xe3, 0xdc, 0xc0, 0xdd, 0x75, 0x2b, 0x76, 0x9e,
0xbe, 0x0a, 0x0d, 0x82, 0x54, 0xcd, 0xab, 0x01, 0x5b, 0x58, 0x6a, 0xd8, 0xbb, 0x75, 0x4e, 0x2a,
0xd0, 0x2f, 0x58, 0xce, 0x8b, 0x59, 0x2c, 0x9c, 0xab, 0x01, 0xc9, 0x6e, 0x1b, 0xc1, 0x12, 0xa2,
0xb6, 0x80, 0x07, 0x25, 0x8c, 0xad, 0xc1, 0x7e, 0x08, 0x4c, 0x54, 0xfa, 0x8d, 0x7c, 0x95, 0x81,
0x47, 0xe5, 0xe6, 0x4f, 0x04, 0xd0, 0xf2, 0x29, 0xb0, 0x86, 0xa2, 0x1a, 0xca, 0x82, 0xd0, 0x19,
0xda, 0xd3, 0x1c, 0x33, 0xb8, 0x61, 0x93, 0xf2, 0x1f, 0x02, 0xd9, 0x5d, 0xc8, 0x43, 0xdd, 0xd5,
0x7c, 0x07, 0x34, 0xac, 0x54, 0x3f, 0xcb, 0x2c, 0x5e, 0x59, 0xb7, 0x43, 0x73, 0xa8, 0x23, 0xf0,
0x7a, 0xd3, 0xde, 0x58, 0xb1, 0x1d, 0x89, 0xf3, 0x26, 0x92, 0x88, 0xb4, 0x10, 0x7c, 0x19, 0x5f,
0x94, 0xbb, 0xe5, 0x12, 0xae, 0xde, 0xcb, 0xaa, 0x71, 0x1a, 0xe2, 0xee, 0xe0, 0x9c, 0x8a, 0x36,
0xdc, 0xef, 0x16, 0x21, 0x04, 0xe6, 0xc9, 0xc3, 0xc0, 0x52, 0x55, 0xf7, 0x53, 0xf5, 0x9c, 0xf5,
0x44, 0x53, 0x8f, 0x7e, 0x22, 0xa7, 0xa4, 0x06, 0x80, 0x92, 0x17, 0xb5, 0x54, 0x58, 0x50, 0xe1,
0x4e, 0xfb, 0x10, 0x6f, 0xb0, 0x15, 0x64, 0x86, 0xce, 0xbc, 0xe5, 0x80, 0xd9, 0x6b, 0xc4, 0x0d,
0x8d, 0xce, 0x42, 0x36, 0xf0, 0x02, 0xed, 0xbe, 0xe1, 0xfd, 0xdd, 0xce, 0x55, 0x22, 0x7d, 0xfd,
0x7b, 0x05, 0xf0, 0x53, 0x22, 0x49, 0x48, 0x36, 0xf0, 0x72, 0x1a, 0x79, 0x39, 0x2c, 0x0d, 0x58,
0x5e, 0x13, 0x7e, 0xc2, 0xf9, 0xf8, 0x81, 0x89, 0x68, 0x7b, 0x1a, 0xc5, 0xef, 0xa1, 0xce, 0x01,
0xb9, 0x20, 0x67, 0x96, 0x58, 0x01, 0x7c, 0x07, 0x55, 0x5b, 0x55, 0x77, 0xa6, 0x38, 0x92, 0xfc,
0x59, 0xb6, 0x60, 0x12, 0x5f, 0x98, 0x01, 0xc1, 0x98, 0xdb, 0x7d, 0xff, 0x6f, 0x7a, 0xc5, 0x9e,
0xa4, 0xd1, 0x1e, 0xa2, 0x3a, 0xe0, 0x27, 0xd9, 0x18, 0x6d, 0x7f, 0xa8, 0xc1, 0x39, 0x3a, 0x52,
0x4c, 0x72, 0x7a, 0x08, 0xef, 0xb9, 0xa8, 0x73, 0x35, 0x31, 0x7c, 0xfb, 0x30, 0x2a, 0x98, 0x3f,
0xb2, 0xc0, 0x51, 0xa5, 0xb7, 0xce, 0xf1, 0xc8, 0x54, 0xf8, 0xec, 0x69, 0x28, 0x74, 0x53, 0x22,
0x75, 0x09, 0xeb, 0x85, 0x24, 0x44, 0x97, 0x83, 0x09, 0xb8, 0x49, 0x78, 0x53, 0x34, 0xcc, 0xdb,
0x22, 0x76, 0xf6, 0xd6, 0xc2, 0x15, 0xc7, 0x61, 0xaa, 0x27, 0xf7, 0x14, 0x1a, 0xb7, 0xad, 0xf6,
0xc0, 0x20, 0xaa, 0xb0, 0x33, 0xa8, 0x64, 0x7e, 0xe7, 0x69, 0x2d, 0x6b, 0xf8, 0x64, 0xee, 0xaf,
0xb0, 0x2d, 0x2f, 0xae, 0xba, 0xb2, 0x52, 0xa4, 0xba, 0x4f, 0xc9, 0x7e, 0x68, 0xc1, 0x49, 0xd9,
0x92, 0xdb, 0x5f, 0x83, 0xb9, 0xb3, 0xc6, 0x7a, 0xeb, 0x44, 0x3d, 0x63, 0x6b, 0x01, 0x00, 0xf9,
0x58, 0x09, 0x72, 0x22, 0x70, 0x15, 0x30, 0x1d, 0x4e, 0xb2, 0x97, 0x53, 0xc8, 0x89, 0x85, 0x5f,
0xd0, 0x1b, 0x00, 0x88, 0xf0, 0xc3, 0x86, 0x2c, 0x21, 0x07, 0xe9, 0x2c, 0x24, 0x8c, 0x94, 0x90,
0x31, 0xb1, 0x7b, 0xff, 0xf6, 0xdf, 0xf9, 0xd8, 0x4b, 0xdc, 0x7d, 0xaa, 0x18, 0xe7, 0x37, 0xd3,
0x48, 0xa5, 0x04, 0x28, 0xc0, 0x5d, 0xf9, 0x0b, 0xb0, 0x96, 0x97, 0x3a, 0x7c, 0xf7, 0x21, 0x57,
0xac, 0x9c, 0x98, 0x34, 0xb9, 0xfa, 0xb0, 0x49, 0x79, 0x5e, 0x86, 0xad, 0x29, 0x25, 0x31, 0x03,
0xdf, 0x1b, 0xd9, 0xbb, 0xcb, 0x4c, 0x8a, 0x8f, 0xbd, 0x58, 0x31, 0xc7, 0x0c, 0x02, 0x0e, 0xa5,
0xd5, 0x8e, 0x23, 0xce, 0x0e, 0x24, 0xd7, 0xd3, 0x01, 0x25, 0xf0, 0xf8, 0x1c, 0xae, 0x86, 0x4f,
0xad, 0xb2, 0xf4, 0xf1, 0xe3, 0xcc, 0x8d, 0xec, 0xae, 0x01, 0xc7, 0xa8, 0x8c, 0x5e, 0xe0, 0xe4,
0x0c, 0x5a, 0x0e, 0x48, 0xa3, 0x56, 0x2d, 0xa1, 0x31, 0x62, 0x56, 0x1c, 0x48, 0x7e, 0x4d, 0x46,
0x26, 0x10, 0x65, 0x1d, 0x45, 0x38, 0x1f, 0xbe, 0x7e, 0x5e, 0x70, 0x26, 0x5f, 0x13, 0xa7, 0xf1,
0x8b, 0xc8, 0x0c, 0xc8, 0x98, 0xbc, 0xbd, 0x7e, 0x17, 0xce, 0xad, 0x3c, 0x27, 0xd9, 0x35, 0x8a,
0xfd, 0x47, 0x83, 0x34, 0x43, 0x00, 0x1b, 0x19, 0xbf, 0xf3, 0xed, 0xf7, 0xae, 0x48, 0x65, 0xcc,
0x34, 0x3a, 0x62, 0x5a, 0x30, 0x45, 0xb5, 0x84, 0x55, 0x56, 0x21, 0x7b, 0xb9, 0xad, 0xc6, 0xbc,
0x16, 0x8e, 0xb0, 0x63, 0x94, 0x5a, 0xb7, 0xbf, 0x4f, 0x00, 0x23, 0x07, 0x2f, 0x1f, 0x40, 0x37,
0xc4, 0xf0, 0x88, 0x7a, 0xe6, 0x37, 0x89, 0xfd, 0xbb, 0xfd, 0x48, 0x88, 0x70, 0x3b, 0x69, 0x5a,
0x46, 0xdb, 0xbb, 0x75, 0x06, 0xc7, 0xff, 0x39, 0xd2, 0x9b, 0x79, 0x3c, 0x0e, 0xa5, 0xdd, 0x64,
0x80, 0x12, 0x25, 0x09, 0x3f, 0x07, 0x0c, 0x0e, 0xc7, 0xaf, 0x2d, 0x9e, 0xc1, 0xc9, 0x34, 0x6a,
0xf9, 0xba, 0x89, 0x99, 0x91, 0x2f, 0xc6, 0xc2, 0x77, 0xe8, 0x92, 0xd5, 0x05, 0x01, 0x3e, 0x18,
0x2f, 0x8b, 0xd1, 0x4c, 0x46, 0x00, 0x13, 0xe1, 0x28, 0x99, 0x34, 0xd2, 0x9f, 0xf5, 0xb7, 0x27,
0xf1, 0x89, 0xe6, 0x91, 0x9e, 0x8a, 0x78, 0x42, 0x64, 0xde, 0xd8, 0x50, 0xc0, 0x70, 0xfb, 0x9b,
0xad, 0x95, 0x11, 0xc1, 0x3f, 0xa7, 0x1c, 0xfc, 0x28, 0xe8, 0xad, 0x25, 0x5b, 0x13, 0xb5, 0x0b,
0xb8, 0x48, 0xa4, 0x25, 0xec, 0xfc, 0xc9, 0x0d, 0x29, 0xc0, 0x47, 0x86, 0xcf, 0xf4, 0xe6, 0x74,
0x55, 0x09, 0x6d, 0x3e, 0xef, 0xc4, 0xdb, 0x49, 0xe7, 0x01, 0xf9, 0x81, 0x0e, 0x15, 0x0d, 0x3d,
0xd7, 0x28, 0x4c, 0x34, 0x39, 0x39, 0x98, 0xec, 0x0e, 0xd2, 0x02, 0x5b, 0x8f, 0x0c, 0x8e, 0xed,
0xa7, 0xdc, 0x36, 0x48, 0x71, 0x72, 0xef, 0x7a, 0xfc, 0x9f, 0x92, 0xfb, 0x00, 0x4a, 0xea, 0x2e,
0xf3, 0xcc, 0xe0, 0x87, 0x89, 0x2a, 0xc0, 0x2d, 0xe3, 0xf7, 0x2d, 0x8e, 0xc2, 0xd6, 0xe7, 0xfe,
0xec, 0x7a, 0x48, 0xc5, 0x6e, 0x0e, 0xbe, 0x07, 0xe8, 0x97, 0x0d, 0x24, 0xd1, 0xa0, 0xa2, 0x9e,
0x05, 0x19, 0x5e, 0x18, 0xfa, 0x5f, 0xd6, 0xd0, 0x7c, 0x21, 0x8f, 0xf9, 0xc9, 0xc3, 0x5c, 0x24,
0xf8, 0x5f, 0x6f, 0xe4, 0xb8, 0xfa, 0xab, 0xe7, 0xa2, 0x81, 0x33, 0x03, 0x97, 0x80, 0xdb, 0x7c,
0xd0, 0xcd, 0x05, 0x33, 0x84, 0xec, 0x27, 0x9f, 0x0a, 0xb5, 0x9d, 0xcf, 0x7c, 0x34, 0x03, 0xe0,
0x1c, 0x43, 0xc1, 0x65, 0x3f, 0x01, 0x9d, 0x1a, 0x7c, 0x9f, 0xf7, 0xc6, 0xed, 0x39, 0x52, 0x8e,
0xf0, 0x0f, 0xa3, 0x74, 0x2d, 0xce, 0x93, 0x22, 0x75, 0xf0, 0x6d, 0x06, 0xfd, 0x54, 0xec, 0x1a,
0x31, 0xc8, 0xe5, 0x21, 0xcb, 0xbf, 0xac, 0x88, 0xcb, 0x30, 0x3a, 0xf0, 0x3d, 0xe6, 0x65, 0x79,
0x54, 0x69, 0xa1, 0x80, 0x56, 0x52, 0xda, 0x7a, 0x76, 0xef, 0xfb, 0x27, 0xc3, 0x92, 0x53, 0xca,
0x19, 0x29, 0x73, 0x81, 0x1a, 0x96, 0xc8, 0x51, 0x10, 0x0b, 0x10, 0x93, 0x37, 0xb5, 0xb0, 0xe6,
0xe4, 0x80, 0xdb, 0xe0, 0x91, 0x6b, 0x8c, 0x1c, 0x7e, 0x8e, 0x51, 0x6f, 0xee, 0xd5, 0x87, 0x0a,
0x18, 0x7f, 0x50, 0x98, 0x3d, 0x9f, 0x92, 0x7b, 0xd5, 0x83, 0xeb, 0x30, 0x6d, 0x80, 0x40, 0x64,
0xa9, 0x57, 0x15, 0x80, 0x5a, 0x4e, 0x7e, 0x98, 0x96, 0x4a, 0x8e, 0xc3, 0xfe, 0x4d, 0x6d, 0x02,
0x1f, 0x5b, 0x6b, 0x99, 0x2f, 0x2d, 0x8a, 0x71, 0x60, 0x13, 0xc4, 0x38, 0x00, 0x82, 0x9a, 0xc3,
0x02, 0x0a, 0x36, 0x0c, 0xb0, 0x12, 0x67, 0x86, 0x63, 0x85, 0x34, 0x87, 0x7d, 0x67, 0x47, 0xc7,
0x73, 0xa3, 0x8f, 0x7b, 0x3e, 0xf8, 0x6d, 0x5b, 0x37, 0x4c, 0x29, 0xf6, 0x83, 0x2b, 0xd1, 0xf3,
0xaa, 0x4f, 0xec, 0x6a, 0xad, 0xba, 0x49, 0x1a, 0x4e, 0x61, 0xb1, 0xb0, 0x6f, 0x05, 0xe4, 0x44,
0x7c, 0x30, 0x36, 0x0a, 0x28, 0x4d, 0x60, 0xc0, 0xba, 0xb5, 0xcc, 0x5d, 0x5d, 0xbb, 0x60, 0x12,
0x5f, 0x00, 0x3f, 0xa7, 0xf0, 0xaf, 0x34, 0x62, 0xdf, 0xdb, 0xff, 0x34, 0xde, 0x19, 0x27, 0x11,
0x78, 0x01, 0xc2, 0x4d, 0xe8, 0xd1, 0x9e, 0xca, 0xff, 0x02, 0x2d, 0xbd, 0xf9, 0x10, 0x0b, 0xdd,
0x1a, 0xdc, 0xac, 0x77, 0x64, 0x88, 0xb5, 0xeb, 0xba, 0xb7, 0xc0, 0xb7, 0x2d, 0xcf, 0x0e, 0x4a,
0xa3, 0x56, 0x43, 0xd1, 0xb2, 0x92, 0x6e, 0x03, 0x2a, 0x1f, 0x9b, 0x4c, 0x5d, 0x65, 0x61, 0x80,
0x41, 0x17, 0x86, 0x01, 0xcb, 0xc0, 0xcc, 0x73, 0xda, 0x9c, 0x9e, 0x5e, 0x93, 0x90, 0x11, 0xcf,
0xc3, 0x0c, 0x95, 0x34, 0x83, 0x00, 0x02, 0xad, 0xcd, 0x1c, 0x23, 0xca, 0x90, 0xd1, 0x63, 0xd9,
0xee, 0x50, 0x05, 0x28, 0xf2, 0xe6, 0x77, 0x4c, 0x1a, 0x94, 0xac, 0xed, 0x48, 0x62, 0xf6, 0xbb,
0x76, 0xf8, 0x85, 0xe0, 0x4f, 0x3a, 0x2c, 0x19, 0xd8, 0xf1, 0x09, 0xc4, 0x36, 0x58, 0xdb, 0xaa,
0x30, 0x0d, 0xf1, 0x65, 0xe0, 0x43, 0xa3, 0x7e, 0x50, 0x17, 0x05, 0x99, 0x87, 0xc4, 0x03, 0x84,
0x83, 0x92, 0xd6, 0x67, 0x01, 0x20, 0x63, 0x78, 0x8a, 0xb4, 0xf7, 0xde, 0x75, 0x54, 0x85, 0x47,
0x2f, 0xc3, 0x37, 0xc1, 0xaa, 0xa8, 0xde, 0xc9, 0x88, 0x86, 0xe0, 0xe4, 0xaa, 0x9d, 0x35, 0xba,
0x49, 0xd1, 0x44, 0x1a, 0x5a, 0xc3, 0x62, 0xeb, 0xb6, 0x4f, 0x63, 0x7c, 0x3e, 0x8e, 0x4b, 0x2d,
0xb0, 0xd6, 0xd8, 0x2f, 0x3a, 0xbc, 0x80, 0x56, 0xbf, 0xc1, 0x14, 0x23, 0x4f, 0x12, 0x72, 0xfe,
0xed, 0x77, 0xd6, 0x52, 0x8b, 0x05, 0x3c, 0x7c, 0x0a, 0x1b, 0x04, 0x55, 0x21, 0x47, 0x1c, 0x16,
0x25, 0x60, 0x86, 0x01, 0xad, 0x09, 0x1b, 0xe6, 0x66, 0x88, 0x47, 0x66, 0x12, 0x2e, 0x5c, 0x5a,
0xd3, 0xe8, 0x3d, 0x76, 0xf4, 0x95, 0x4a, 0x3b, 0x4e, 0x8d, 0xb2, 0xc0, 0xca, 0x2a, 0xac, 0xbe,
0x10, 0xbb, 0x5f, 0x9e, 0xd7, 0x9f, 0x25, 0x80, 0x69, 0x1b, 0xff, 0xcf, 0xe5, 0x42, 0xde, 0xcc,
0xf0, 0xe3, 0x56, 0x85, 0x16, 0x53, 0x40, 0x60, 0x77, 0x55, 0xc8, 0x8b, 0xc0, 0x45, 0x80, 0xe7,
0x00, 0x0f, 0x0d, 0x63, 0x0e, 0x5f, 0xb3, 0xc0, 0x73, 0x78, 0xf7, 0xa3, 0x97, 0x87, 0xc5, 0x1f,
0x0e, 0x49, 0x93, 0x5e, 0x02, 0x50, 0x73, 0x70, 0xf2, 0xbf, 0xcd, 0x83, 0xa0, 0x81, 0x3f, 0x50,
0x25, 0xd5, 0xeb, 0x00, 0xb7, 0xb2, 0xa7, 0x35, 0x7f, 0x7c, 0x44, 0x73, 0xf0, 0x72, 0x44, 0x86,
0x69, 0x27, 0x8d, 0x20, 0x16, 0xf4, 0x15, 0x4d, 0x2e, 0x7e, 0xaf, 0xb2, 0x12, 0x81, 0xe6, 0x38,
0xe4, 0xb4, 0xb4, 0xd7, 0xa9, 0xfc, 0x5a, 0x1c, 0xe9, 0x3d, 0x7a, 0xc4, 0xf6, 0x91, 0x2c, 0x98,
0x00, 0x49, 0x3d, 0xce, 0x47, 0xc8, 0x8f, 0xbf, 0xc9, 0xed, 0xe7, 0x87, 0x1e, 0x75, 0x7d, 0xf6,
0xbb, 0x4e, 0x99, 0x52, 0xc0, 0xd2, 0x48, 0x25, 0xc4, 0xc5, 0x93, 0x97, 0xdb, 0x4b, 0x87, 0x33,
0xca, 0x64, 0x50, 0x0a, 0x30, 0xa6, 0x50, 0x94, 0xfe, 0x56, 0xa4, 0xbb, 0xc0, 0x24, 0x56, 0xb0,
0xd7, 0x55, 0x74, 0x0c, 0x90, 0x6d, 0xe7, 0x4a, 0x00, 0xa0, 0xee, 0x3c, 0x51, 0xdf, 0xf0, 0xec,
0x5b, 0xfc, 0xbf, 0xf1, 0xb9, 0xb4, 0x98, 0x00, 0xda, 0x55, 0x45, 0xa8, 0xf0, 0x02, 0xa8, 0xd6,
0xe0, 0xe4, 0x54, 0x4f, 0x5d, 0x75, 0xb1, 0xdc, 0xf2, 0x22, 0x80, 0xff, 0xeb, 0x13, 0xf7, 0xb1,
0x14, 0xed, 0x07, 0x24, 0xd1, 0xab, 0xe1, 0xc8, 0x53, 0x52, 0x60, 0x25, 0xc8, 0x89, 0xe0, 0x30,
0xd2, 0xa4, 0x61, 0xf6, 0x65, 0xc7, 0x3a, 0x19, 0x79, 0x2a, 0x35, 0x03, 0x22, 0x88, 0x65, 0x1f,
0x92, 0x65, 0x2d, 0xb1, 0x71, 0xc2, 0x04, 0x16, 0x96, 0x18, 0x2c, 0xa4, 0xd0, 0xc0, 0xdc, 0xac,
0x3d, 0xb7, 0x82, 0xb2, 0x3f, 0xc0, 0x1b, 0xff, 0xae, 0x5c, 0xb8, 0x63, 0xd2, 0xb3, 0xd0, 0x55,
0xfa, 0xb0, 0xf3, 0xd1, 0xc2, 0x55, 0xb5, 0x6a, 0x5d, 0x60, 0x3d, 0x94, 0xa4, 0x26, 0x6f, 0x5e,
0x8e, 0x01, 0xf8, 0xe1, 0xd4, 0x1a, 0x0c, 0xd7, 0x32, 0xcd, 0xf0, 0xdc, 0x70, 0x04, 0x61, 0x42,
0xd0, 0x81, 0xaf, 0x12, 0x04, 0x54, 0x76, 0x18, 0x3a, 0x57, 0x09, 0xbe, 0x5b, 0xa8, 0xf0, 0x72,
0xa8, 0xb6, 0x60, 0xe4, 0xc2, 0xb5, 0x01, 0x1b, 0x87, 0x91, 0x24, 0x5a, 0xbf, 0x07, 0x95, 0x1d,
0xac, 0x1c, 0xcf, 0x66, 0x8d, 0x38, 0x16, 0xf4, 0x0a, 0xe5, 0xd9, 0x58, 0x2f, 0x16, 0x6d, 0x04,
0x18, 0x9a, 0x06, 0x01, 0xdc, 0xff, 0x1c, 0xec, 0x23, 0x7c, 0x19, 0xfc, 0x6a, 0x73, 0x9e, 0xbb,
0x9c, 0x01, 0x40, 0x7c, 0xc1, 0x80, 0x5d, 0x46, 0x98, 0xe1, 0x92, 0x04, 0xe7, 0x32, 0xc4, 0x1d,
0xb8, 0x0e, 0x23, 0x4f, 0x41, 0xc5, 0x81, 0xba, 0xfb, 0x3f, 0xad, 0xb6, 0x94, 0x76, 0xdc, 0x9e,
0x0f, 0x9b, 0x33, 0xcf, 0xff, 0xed, 0xbf, 0xf3, 0xd1, 0xc1, 0x1f, 0x59, 0x54, 0xf0, 0xa7, 0x9d,
0x1d, 0x3c, 0x5f, 0x28, 0x33, 0x78, 0x9f, 0x85, 0x58, 0x8b, 0x6e, 0xad, 0x23, 0x17, 0x60, 0xc0,
0x10, 0x45, 0x16, 0xa0, 0xe4, 0x44, 0x7c, 0x2c, 0x94, 0x1d, 0x24, 0x5a, 0xbf, 0x00, 0x95, 0x17,
0x7c, 0x1c, 0x55, 0x40, 0x73, 0x63, 0x44, 0x0a, 0x74, 0xe4, 0xbc, 0xe0, 0xec, 0x7a, 0x11, 0xdd,
0x39, 0x2f, 0x24, 0xb8, 0x49, 0xd1, 0x4c, 0x1a, 0xba, 0x43, 0x22, 0x74, 0x0a, 0x32, 0x9a, 0xaf,
0x00, 0x0d, 0x66, 0xd3, 0x69, 0x65, 0x6a, 0x1f, 0x80, 0x41, 0xc5, 0xe1, 0xdc, 0xc8, 0x5f, 0xac,
0xa1, 0x80, 0x90, 0xee, 0x85, 0x78, 0x1f, 0x22, 0x7c, 0x1b, 0x69, 0xdb, 0x35, 0x87, 0x8c, 0xcb,
0x31, 0x69, 0x7c, 0xbf, 0x99, 0xa3, 0xe3, 0xf9, 0x1c, 0xce, 0x82, 0x3b, 0xd7, 0x2f, 0xa5, 0x83,
0xfa, 0xb9, 0x31, 0x62, 0xf2, 0x96, 0x85, 0xb2, 0x04, 0x94, 0x8c, 0x18, 0xff, 0xcd, 0x2a, 0x42,
0x0c, 0x02, 0xa9, 0x4f, 0xbd, 0x6a, 0xc0, 0x82, 0x9b, 0x4d, 0x25, 0x80, 0x87, 0x22, 0x5b, 0xe0,
0x6b, 0x77, 0xba, 0xa3, 0x40, 0xdd, 0x03, 0x9d, 0x19, 0x11, 0xbc, 0x0d, 0xc6, 0x89, 0x35, 0xd6,
0xa3, 0x88, 0xaf, 0x17, 0xad, 0x3d, 0xb0, 0x26, 0x42, 0x61, 0xb0, 0x72, 0x08, 0x9a, 0xdd, 0x42,
0x83, 0x92, 0x45, 0x37, 0x3a, 0x97, 0x93, 0x55, 0xb4, 0x38, 0xa3, 0x42, 0x2a, 0x62, 0xe2, 0x67,
0x4d, 0x1b, 0x98, 0xdb, 0x5d, 0xff, 0xd4, 0xfe, 0x02, 0x80, 0x81, 0x0c, 0xaa, 0x06, 0xa2, 0x96,
0xab, 0x20, 0xe4, 0x96, 0xeb, 0xe9, 0x1a, 0xae, 0xb2, 0xae, 0x0c, 0xbf, 0x42, 0xa4, 0xd6, 0x02,
0x16, 0x39, 0x4a, 0x9c, 0x73, 0x33, 0xb6, 0xcf, 0xf8, 0x48, 0x1e, 0xf9, 0xe7, 0xdf, 0xb3, 0xb4,
0xa1, 0x85, 0x23, 0x81, 0x62, 0xe0, 0x4b, 0xf2, 0x76, 0xb9, 0xc4, 0xf2, 0x61, 0x56, 0x7d, 0xba,
0x86, 0x01, 0x17, 0x86, 0x48, 0xe0, 0x14, 0x61, 0x07, 0xbf, 0x5c, 0x58, 0xd0, 0xe1, 0xfc, 0xb3,
0x56, 0x1b, 0xe7, 0x46, 0x9e, 0x15, 0x11, 0x19, 0x12, 0x39, 0x57, 0xf8, 0xdd, 0xe0, 0x84, 0xf8,
0x0c, 0x6e, 0x27, 0xd3, 0x9f, 0x22, 0xd5, 0x50, 0x71, 0x48, 0xa2, 0x8a, 0xe0, 0xe4, 0xa5, 0x85,
0x5a, 0xed, 0x00, 0x24, 0xb2, 0x7f, 0x9f, 0xa5, 0x41, 0xf5, 0xc1, 0xc9, 0x55, 0x47, 0xf1, 0xf5,
0x63, 0xb8, 0xc9, 0x8b, 0x17, 0x70, 0x63, 0xd5, 0xee, 0xef, 0xc9, 0x78, 0x47, 0xc1, 0x9d, 0x13,
0x00, 0xc4, 0xf2, 0x8d, 0xf6, 0x18, 0x7f, 0xe7, 0xcc, 0xab, 0x86, 0xcd, 0xa8, 0x1b, 0x13, 0xd1,
0x4c, 0x33, 0x96, 0x98, 0x81, 0xbc, 0x44, 0x1a, 0x0f, 0x91, 0x21, 0xab, 0x98, 0xaa, 0x92, 0x04,
0xaf, 0x32, 0x75, 0x1b, 0x26, 0xf0, 0xf5, 0x60, 0xe0, 0x16, 0x60, 0xfe, 0xb9, 0xf8, 0x78, 0x13,
0xb7, 0xd0, 0xb6, 0x9d, 0xc3, 0x00, 0x55, 0x46, 0x59, 0x5d, 0xf0, 0xe1, 0x38, 0x5a, 0xcb, 0x01,
0xec, 0xa2, 0x02, 0x31, 0xf5, 0x0c, 0xff, 0xae, 0xe2, 0xcd, 0xa3, 0x09, 0x70, 0x00, 0x7b, 0x78,
0x29, 0x86, 0x05, 0x86, 0x62, 0x18, 0x05, 0x84, 0x09, 0xaa, 0x58, 0x30, 0xff, 0x9d, 0x10, 0x2d,
0xb7, 0x79, 0xfb, 0xd7, 0x83, 0x92, 0x07, 0x6e, 0x6c, 0xde, 0x02, 0xf4, 0xb4, 0xa2, 0xb7, 0x01,
0xc8, 0x8b, 0x55, 0x14, 0x2e, 0xbd, 0x6f, 0xf4, 0x4b, 0x75, 0xc6, 0x00, 0x0e, 0x48, 0x1b, 0x56,
0x5e, 0x8d, 0xaf, 0x5d, 0x6f, 0xbd, 0x88, 0xc4, 0x0c, 0x2e, 0x04, 0xfa, 0x69, 0x06, 0x53, 0xd4,
0x50, 0x15, 0xb2, 0x40, 0x82, 0x51, 0x9a, 0xb2, 0x54, 0x73, 0x15, 0x55, 0xf2, 0x91, 0x3c, 0xd3,
0x92, 0x66, 0xf4, 0xf0, 0x92, 0xef, 0xf8, 0x97, 0x26, 0x7c, 0x1d, 0xfa, 0x93, 0xbf, 0x08, 0x0f,
0x9c, 0x30, 0x21, 0x62, 0xb5, 0x44, 0xab, 0xb4, 0x00, 0x8a, 0x85, 0x83, 0xfa, 0x1e, 0xbb, 0xc3,
0x54, 0x72, 0x80, 0x18, 0xca, 0xa7, 0xae, 0xe2, 0xf1, 0x48, 0x07, 0xfa, 0xd3, 0x43, 0x56, 0x11,
0x4a, 0x3b, 0x02, 0x31, 0x66, 0x0c, 0x7f, 0xe7, 0x4c, 0x9b, 0x7d, 0x94, 0xcc, 0x90, 0x46, 0x06,
0x36, 0x94, 0x70, 0x9a, 0x58, 0x59, 0x1e, 0xe0, 0x5e, 0x9b, 0x15, 0xe2, 0xae, 0x23, 0x1a, 0x52,
0xbe, 0xfd, 0xfb, 0x15, 0xd7, 0xce, 0xc5, 0xcf, 0x42, 0x78, 0x00, 0x36, 0xc8, 0x88, 0xea, 0x54,
0x1c, 0x94, 0x7f, 0xbc, 0x2e, 0x7b, 0x44, 0x5c, 0x09, 0x64, 0xdb, 0xf0, 0x63, 0xb8, 0xcb, 0x8b,
0x3f, 0x5b, 0x8f, 0x50, 0xdf, 0xbd, 0xc8, 0xec, 0x7c, 0xf1, 0xde, 0xd0, 0xc0, 0xf3, 0x89, 0x03,
0x62, 0x4e, 0xba, 0x88, 0x65, 0x3d, 0xc0, 0xb1, 0x9c, 0xc1, 0xf6, 0xd2, 0xa3, 0xa4, 0x8c, 0x32,
0x60, 0x9a, 0x18, 0x36, 0xcf, 0x9a, 0xe6, 0x45, 0x57, 0x66, 0xe6, 0x85, 0x01, 0x66, 0x51, 0x96,
0x61, 0xec, 0xb2, 0xbf, 0xcf, 0x28, 0x97, 0x8d, 0x5d, 0xd5, 0x6c, 0x65, 0xc1, 0x23, 0x1f, 0x22,
0x05, 0xd6, 0x2a, 0x18, 0x3f, 0x2d, 0xe0, 0x12, 0xc3, 0x78, 0xbf, 0xf3, 0x85, 0x79, 0x96, 0x8b,
0x87, 0x75, 0x48, 0x33, 0x95, 0x9b, 0x82, 0xb2, 0x58, 0x4a, 0xc1, 0x80, 0x47, 0x46, 0x46, 0x29,
0xc1, 0x80, 0x03, 0xa8, 0xc7, 0x3e, 0x00, 0xbe, 0xb2, 0xc3, 0x14, 0x06, 0x48, 0x2e, 0xfc, 0x3b,
0x95, 0x0c, 0x7f, 0xe7, 0xf0, 0x3b, 0xec, 0x0a, 0x9e, 0x00, 0x97, 0x8a, 0xa2, 0xe9, 0x55, 0x2d,
0x38, 0x3a, 0xfb, 0xe7, 0x7e, 0x77, 0x72, 0x6a, 0xc7, 0x1b, 0x4e, 0x18, 0xbc, 0x20, 0x4b, 0x6b,
0x5f, 0xbf, 0xf1, 0x0e, 0x11, 0x68, 0xf8, 0xc9, 0x00, 0xd6, 0x90, 0x9b, 0xbc, 0x00, 0xca, 0xbe,
0xf2, 0x7a, 0xe1, 0x4f, 0xc6, 0x8d, 0xb6, 0x6b, 0x7d, 0x77, 0x7b, 0x32, 0x69, 0x56, 0x50, 0xc4,
0x50, 0x35, 0xc0, 0xe0, 0xe8, 0xc6, 0xf5, 0x77, 0x73, 0xc0, 0x00, 0xb3, 0xe1, 0x0f, 0x71, 0x0b,
0x80, 0x35, 0x09, 0x7f, 0x44, 0x2d, 0xbb, 0x4f, 0x70, 0xae, 0x69, 0x99, 0x19, 0xed, 0xaa, 0xe3,
0x31, 0x3e, 0x37, 0xcb, 0x95, 0x9a, 0xc0, 0x05, 0xbe, 0x7d, 0xb4, 0xed, 0x72, 0xd7, 0x41, 0x82,
0xa4, 0x05, 0x19, 0x21, 0xc9, 0x38, 0x28, 0xb0, 0xd3, 0xfb, 0x01, 0x51, 0x86, 0x86, 0x7f, 0xd6,
0xa6, 0x86, 0x60, 0x13, 0x83, 0x98, 0xd1, 0xaf, 0xb9, 0x4f, 0x10, 0x73, 0xb6, 0x28, 0x2f, 0xf2,
0xda, 0x53, 0x6d, 0x19, 0xbd, 0x78, 0x77, 0xf7, 0x05, 0x36, 0x7f, 0x22, 0x78, 0x2d, 0x14, 0x62,
0xc5, 0xdd, 0x5f, 0xa8, 0x12, 0x5d, 0x7f, 0x80, 0x9d, 0xd9, 0x41, 0x67, 0xd4, 0x30, 0x1a, 0xbe,
0x59, 0xf8, 0xb1, 0xdc, 0xa4, 0xc5, 0xbf, 0xb3, 0xc7, 0xaf, 0x05, 0x5e, 0xc1, 0xc9, 0x26, 0x9f,
0x89, 0x3a, 0x1a, 0x41, 0x6c, 0xd1, 0xc2, 0x07, 0x69, 0xcf, 0xff, 0xaf, 0xa1, 0x2d, 0x5f, 0xaa,
0xeb, 0xc0, 0xd5, 0x3d, 0x34, 0x1b, 0x01, 0xdd, 0x08, 0xdc, 0x15, 0xdc, 0x6f, 0xda, 0x44, 0x35,
0x95, 0x05, 0xb0, 0x13, 0x58, 0x66, 0x7e, 0xa8, 0x4d, 0x55, 0xb0, 0xfe, 0xc8, 0x49, 0xcd, 0xe3,
0x5e, 0xfb, 0x9a, 0x1a, 0x9b, 0xd7, 0x3f, 0xe9, 0x4c, 0xe8, 0x1d, 0x0b, 0x67, 0xa5, 0x0d, 0x82,
0x17, 0x4c, 0x35, 0x5e, 0x86, 0xa1, 0x22, 0x20, 0xf1, 0x23, 0x9c, 0x55, 0x21, 0xb3, 0xf0, 0xc0,
0x67, 0x4c, 0xc3, 0xc3, 0xe0, 0xcf, 0x44, 0xeb, 0xcc, 0x8c, 0xb3, 0x0a, 0x77, 0x0c, 0x76, 0x22,
0x95, 0xe7, 0xc1, 0xa5, 0xb1, 0xdf, 0x82, 0xb8, 0x00, 0xb0, 0xae, 0x58, 0x0c, 0xff, 0x02, 0xf1,
0xe1, 0x0c, 0x2b, 0x62, 0x0f, 0xc7, 0x18, 0xcd, 0xbe, 0x77, 0xee, 0x15, 0x49, 0x53, 0xa1, 0x14,
0xc9, 0x6e, 0x98, 0xc1, 0xef, 0xd1, 0xbe, 0x0d, 0xd7, 0x5a, 0xfe, 0x61, 0xdf, 0x11, 0xc5, 0x34,
0x2a, 0xfe, 0x58, 0x39, 0x46, 0x8d, 0x59, 0x3c, 0x58, 0xcd, 0x0b, 0x01, 0x55, 0x00, 0x49, 0xbd,
0xfe, 0x44, 0x37, 0x8c, 0xae, 0x12, 0x7b, 0x25, 0x03, 0x70, 0x09, 0x5e, 0xc0, 0xb6, 0x8b, 0x72,
0x00, 0xb0, 0x8d, 0x83, 0xd5, 0x10, 0xbe, 0xda, 0x38, 0x42, 0xaf, 0x8b, 0xb1, 0xcc, 0x54, 0x16,
0x62, 0x86, 0xff, 0x9c, 0x03, 0xbf, 0x8d, 0x9b, 0x1e, 0x1b, 0x3d, 0x1c, 0x6b, 0x78, 0xd7, 0x08,
0xfc, 0xc7, 0x24, 0xdb, 0xc6, 0xff, 0xed, 0x11, 0xa7, 0x86, 0x14, 0x47, 0x0e, 0x5e, 0xf6, 0x96,
0xb1, 0x79, 0x03, 0x4a, 0x0b, 0x53, 0xfd, 0x93, 0x47, 0x1b, 0x6c, 0x25, 0x6a, 0x90, 0xf4, 0x21,
0x00, 0x7b, 0xd6, 0x96, 0xbe, 0x73, 0x3c, 0xe2, 0xd4, 0xe2, 0xf6, 0xd2, 0x23, 0xe9, 0x0d, 0xb2,
0x4a, 0x2f, 0xe6, 0x1f, 0x3d, 0x89, 0x42, 0x79, 0x62, 0xbf, 0x02, 0xf0, 0x86, 0x0c, 0xd7, 0x3b,
0x04, 0x95, 0x44, 0x18, 0x5c, 0xe4, 0x6d, 0x7c, 0x8d, 0xb4, 0x16, 0x55, 0xa3, 0xf3, 0xa9, 0xf7,
0x4b, 0x54, 0x20, 0x3d, 0x28, 0x39, 0x97, 0x24, 0x5c, 0x00, 0x99, 0xdc, 0x7f, 0xe7, 0x92, 0x82,
0x5b, 0x83, 0x3b, 0x81, 0x8e, 0xe3, 0x4e, 0x2d, 0x7c, 0x03, 0x1e, 0xb2, 0xcd, 0x7b, 0x39, 0x22,
0x8d, 0x58, 0xfe, 0xba, 0xc5, 0xa2, 0xe3, 0x6c, 0xc3, 0xf2, 0x72, 0x3a, 0x30, 0xbc, 0xba, 0x69,
0x06, 0xf9, 0x46, 0x21, 0xa1, 0x80, 0x83, 0x62, 0xaa, 0xa0, 0xd8, 0x8b, 0xe7, 0x27, 0x28, 0xd5,
0xd7, 0xec, 0xae, 0x15, 0xfd, 0x92, 0x63, 0x64, 0x1c, 0x04, 0xab, 0x23, 0x1b, 0x77, 0xf1, 0x5e,
0x34, 0x93, 0x01, 0xc9, 0x06, 0xdb, 0xcf, 0x98, 0xc4, 0x39, 0x31, 0xbf, 0x70, 0xde, 0x69, 0x47,
0x6f, 0x75, 0xe0, 0x81, 0x0a, 0xea, 0x89, 0x26, 0x4d, 0xc8, 0x3a, 0xc0, 0xf0, 0xbc, 0x06, 0xd7,
0xfb, 0xbc, 0xc7, 0x97, 0x7e, 0x4b, 0xfb, 0xcd, 0x8b, 0xfe, 0xee, 0xdb, 0xd5, 0x54, 0xbe, 0x5d,
0x05, 0xf3, 0x8f, 0x7e, 0x8e, 0xe9, 0x78, 0x1f, 0xcf, 0xfa, 0xae, 0x21, 0x91, 0x15, 0x80, 0x17,
0x78, 0x55, 0xc4, 0x21, 0x44, 0xdd, 0xbf, 0xaf, 0x6f, 0xd2, 0x11, 0xcb, 0x9a, 0x8a, 0x34, 0xa5,
0xf0, 0x2d, 0x51, 0x42, 0x31, 0xce, 0xf2, 0x8e, 0xa4, 0x12, 0x16, 0x71, 0xb6, 0x8a, 0x49, 0x54,
0x6b, 0x0e, 0x79, 0xa6, 0x8f, 0xf2, 0xe3, 0x00, 0x3b, 0x4b, 0xaf, 0xcf, 0x02, 0x41, 0xeb, 0xf0,
0x75, 0xd1, 0xb7, 0xb8, 0x92, 0x86, 0xfd, 0x25, 0x08, 0x7c, 0xb3, 0xdf, 0x8b, 0x5e, 0x66, 0xbd,
0x1c, 0x91, 0x46, 0xac, 0x5d, 0xbd, 0xe3, 0xaa, 0x15, 0xf1, 0xb7, 0x04, 0xff, 0x31, 0x65, 0xbb,
0x81, 0x4d, 0x9b, 0x9d, 0x0d, 0x02, 0xfc, 0xc2, 0x91, 0x70, 0x0b, 0x70, 0x1d, 0xa4, 0x0a, 0xa2,
0xba, 0x60, 0x58, 0x81, 0x5d, 0x13, 0xd1, 0xbe, 0xe7, 0x81, 0x9a, 0x4c, 0xed, 0x81, 0x72, 0x22,
0xaa, 0xa9, 0x66, 0xc6, 0x12, 0x51, 0x58, 0x72, 0x5a, 0x13, 0xde, 0xcc, 0x18, 0xab, 0x75, 0x67,
0xbe, 0x28, 0x7c, 0x9e, 0x88, 0x2d, 0x42, 0xc8, 0xe2, 0xe0, 0x61, 0x62, 0xe0, 0x84, 0x69, 0xef,
0xdf, 0x35, 0x5a, 0xf9, 0xfd, 0x19, 0x17, 0x4d, 0xf2, 0x37, 0x29, 0xfc, 0xe0, 0x39, 0x17, 0x6f,
0xab, 0xff, 0x8d, 0x8a, 0x2d, 0xa2, 0xd6, 0x55, 0x37, 0x8c, 0xa1, 0x43, 0xb9, 0x9f, 0x90, 0x8d,
0x7c, 0x1c, 0xb7, 0xa5, 0x5e, 0x10, 0xdf, 0xab, 0x75, 0x49, 0xc1, 0xc9, 0xd4, 0xcf, 0xaf, 0x63,
0x24, 0x99, 0xab, 0x07, 0xef, 0x51, 0x9b, 0xdb, 0xfa, 0xc2, 0xe3, 0x5f, 0xa6, 0x19, 0x21, 0x83,
0x04, 0xed, 0x00, 0x92, 0x47, 0xa6, 0xd5, 0xc0, 0x7f, 0x9c, 0x30, 0x13, 0x78, 0x16, 0x44, 0x40,
0xaa, 0xae, 0xef, 0xf0, 0x4b, 0x13, 0x46, 0x1f, 0x05, 0x64, 0x11, 0xd3, 0x13, 0xdb, 0x66, 0x86,
0x30, 0x31, 0x77, 0x4d, 0xf0, 0x32, 0x88, 0xa4, 0x8d, 0x9c, 0xf7, 0xef, 0x57, 0x80, 0xc3, 0x84,
0x27, 0x34, 0x9c, 0x1b, 0x6d, 0x9f, 0x98, 0x11, 0xf8, 0x63, 0xc9, 0xfd, 0x2c, 0x29, 0xf1, 0xa8,
0x4e, 0x19, 0x5c, 0x41, 0x00, 0xa5, 0x17, 0xcc, 0x0c, 0x91, 0x5a, 0xbe, 0x93, 0x06, 0x84, 0xdb,
0x0c, 0xa3, 0x35, 0x4f, 0xdc, 0xc5, 0x35, 0xa7, 0x61, 0x45, 0xf7, 0x84, 0xd3, 0xe1, 0xae, 0x6c,
0x02, 0x55, 0x94, 0x30, 0x79, 0xc3, 0xaa, 0x87, 0xab, 0x6e, 0xf8, 0x70, 0x80, 0x7b, 0xea, 0xa3,
0xd7, 0xb5, 0x92, 0x2c, 0xd5, 0x83, 0xac, 0x68, 0xb6, 0x03, 0xfe, 0xb2, 0x5c, 0xd7, 0xa1, 0x85,
0x04, 0x04, 0x54, 0x42, 0x1a, 0xc7, 0x80, 0x76, 0xaa, 0x7b, 0x30, 0x08, 0xcb, 0x0c, 0x87, 0x08,
0x2e, 0x19, 0x10, 0x60, 0x05, 0x53, 0xd9, 0xa9, 0xe0, 0xc8, 0xb4, 0x49, 0x51, 0xc5, 0xd0, 0x8e,
0x33, 0x8c, 0xc0, 0x9c, 0x70, 0x02, 0x9b, 0x58, 0xdb, 0xf0, 0x43, 0x99, 0x75, 0x39, 0xb6, 0x78,
0x99, 0xd1, 0xe3, 0xe9, 0x8f, 0x80, 0x92, 0x12, 0x9b, 0xa0, 0xc1, 0x60, 0xdf, 0x01, 0x64, 0x7c,
0x1f, 0x07, 0xc4, 0x3e, 0xe3, 0xbb, 0x00, 0x58, 0x0f, 0x00, 0x49, 0x3b, 0x41, 0x44, 0xa4, 0x54,
0x88, 0xd5, 0xd7, 0xad, 0xcd, 0x0c, 0x2a, 0x26, 0x50, 0xaa, 0x8b, 0x0b, 0xf2, 0xbb, 0x16, 0x74,
0xcb, 0x6a, 0x37, 0xe0, 0x72, 0x2d, 0x23, 0x11, 0xb2, 0xae, 0x28, 0xa4, 0x30, 0xc0, 0x18, 0x0b,
0xda, 0x55, 0x63, 0x69, 0xc5, 0x95, 0xce, 0xa2, 0x7d, 0xc8, 0xb5, 0x2a, 0x75, 0xad, 0x00, 0x49,
0x89, 0x6e, 0x0e, 0x4a, 0x38, 0xe6, 0x24, 0x88, 0xab, 0x07, 0xef, 0x51, 0xb5, 0x5f, 0x80, 0xec,
0x87, 0xdb, 0x87, 0x0a, 0xb6, 0xcf, 0xc0, 0x20, 0x1c, 0x10, 0x87, 0x01, 0x18, 0x05, 0xd3, 0x8c,
0x02, 0x6c, 0x43, 0x0c, 0x18, 0x32, 0x3f, 0xb7, 0x45, 0xb6, 0x24, 0xb8, 0xe1, 0x61, 0x1b, 0x73,
0xd7, 0x3c, 0x67, 0x95, 0x57, 0x69, 0x3c, 0x39, 0x5e, 0x3a, 0xc6, 0x44, 0x28, 0xc4, 0x95, 0x0e,
0x5d, 0x33, 0x85, 0x43, 0x8d, 0xd8, 0xc6, 0xf1, 0x2d, 0x51, 0x31, 0x3c, 0x7b, 0xdb, 0xb6, 0xb5,
0x94, 0xe6, 0xbd, 0x75, 0xd6, 0x05, 0x2a, 0xf4, 0xfa, 0x6b, 0xbc, 0x0a, 0xa7, 0xda, 0x54, 0xe4,
0x6b, 0xc9, 0x72, 0x71, 0x8b, 0x54, 0xa3, 0x93, 0x79, 0x0a, 0x7d, 0xd5, 0x81, 0x9f, 0xc2, 0x82,
0x77, 0x1f, 0xd8, 0xe5, 0x5f, 0xd2, 0xee, 0xbe, 0x84, 0x19, 0x10, 0x04, 0xfc, 0x6c, 0x71, 0xa6,
0xc2, 0x2d, 0xd3, 0xed, 0x1c, 0xbd, 0x9e, 0xc6, 0x97, 0x82, 0xb8, 0x10, 0xb7, 0xae, 0x9a, 0xc0,
0x65, 0x3e, 0x6f, 0x6d, 0xce, 0x81, 0xc6, 0xbc, 0x96, 0xfc, 0x67, 0xd2, 0xfc, 0x0d, 0x95, 0x9e,
0x05, 0xbd, 0xfb, 0xe6, 0x74, 0x3a, 0x56, 0x36, 0xec, 0x0f, 0x69, 0xca, 0xcf, 0xcd, 0xa9, 0xef,
0xb3, 0x86, 0x63, 0xb3, 0xed, 0xde, 0x0b, 0x66, 0x5f, 0x3f, 0xaf, 0x80, 0x7f, 0xce, 0x92, 0x4d,
0xaf, 0x83, 0x11, 0xf6, 0x83, 0xac, 0x4f, 0xc1, 0x49, 0x2b, 0x16, 0x40, 0x8f, 0x0b, 0xcb, 0xec,
0x6e, 0xfc, 0xe4, 0x8c, 0xcf, 0xe0, 0x94, 0xd3, 0xac, 0x1c, 0xcd, 0x46, 0x58, 0x0e, 0xa6, 0x44,
0x47, 0x00, 0xb6, 0x94, 0x9f, 0xa7, 0x25, 0x9a, 0xb8, 0x6b, 0xd6, 0x93, 0x5b, 0xb5, 0xb7, 0x59,
0xf0, 0x2c, 0x2c, 0x90, 0x61, 0x4a, 0xbd, 0x52, 0xea, 0x19, 0x97, 0x0a, 0x91, 0xb1, 0x75, 0x05,
0xd4, 0xdd, 0x3d, 0x64, 0x5b, 0x50, 0x70, 0x7c, 0xb7, 0x45, 0x01, 0xdf, 0x37, 0x83, 0x1a, 0xdf,
0xc0, 0x76, 0x81, 0x2b, 0xc1, 0x63, 0x62, 0x5b, 0x35, 0xb1, 0x89, 0xa7, 0xf2, 0x17, 0xc3, 0xd7,
0x16, 0xf8, 0x7c, 0x61, 0x96, 0x4b, 0x86, 0x93, 0x7c, 0x0d, 0x1c, 0x6c, 0x3e, 0x87, 0x27, 0xb5,
0x09, 0xb0, 0xf7, 0x2c, 0x88, 0xef, 0x50, 0xc9, 0x4a, 0x36, 0xc6, 0x47, 0xfa, 0xcb, 0xa6, 0x3f,
0x70, 0xc6, 0xbf, 0xe8, 0xed, 0xae, 0x38, 0x0d, 0xa4, 0xe0, 0x4a, 0xcb, 0xe1, 0x6a, 0xa3, 0x0d,
0x78, 0x5a, 0x93, 0x43, 0x6a, 0x96, 0x52, 0xad, 0x23, 0x80, 0x5d, 0x10, 0x5d, 0x34, 0xa5, 0x6c,
0xb5, 0xca, 0xcb, 0x7c, 0x23, 0xbf, 0x04, 0xa9, 0xde, 0xe0, 0x72, 0xbc, 0x32, 0x5b, 0xc0, 0x09,
0x4b, 0x46, 0x42, 0x0e, 0x94, 0x93, 0x8c, 0x9d, 0x9e, 0xa3, 0xdf, 0xa7, 0xf8, 0x7a, 0xc5, 0x79,
0x39, 0x53, 0xe9, 0x42, 0xe9, 0x56, 0xff, 0xcf, 0x48, 0x5a, 0x8a, 0xc0, 0x31, 0x79, 0x95, 0x36,
0xf4, 0x3e, 0x4e, 0xdb, 0x6b, 0x3f, 0x42, 0x22, 0xd3, 0x73, 0xc0, 0xdd, 0x31, 0x4b, 0xad, 0x87,
0x6f, 0xf0, 0xdf, 0x3c, 0xff, 0xb5, 0xb0, 0xdc, 0x3d, 0xe5, 0x36, 0x05, 0x07, 0xf7, 0xf2, 0x7a,
0x08, 0x8b, 0xc6, 0xfd, 0x89, 0x50, 0xa6, 0xbd, 0xfc, 0x27, 0x5b, 0x2c, 0x0b, 0xec, 0x25, 0x35,
0x49, 0x83, 0x1a, 0xc0, 0x11, 0x9a, 0x34, 0x4e, 0x12, 0x3c, 0x88, 0x87, 0xf3, 0xde, 0x60, 0x1e,
0xbe, 0x38, 0xf5, 0x8c, 0x24, 0x9a, 0x37, 0x07, 0xc7, 0xf7, 0x94, 0xa9, 0x17, 0x3d, 0xac, 0x77,
0xbc, 0x88, 0x78, 0x16, 0x3d, 0x43, 0x9a, 0xf6, 0x72, 0x51, 0x1a, 0xb0, 0x95, 0xfb, 0xae, 0x03,
0xc0, 0x26, 0x91, 0x60, 0x5d, 0x40, 0xd3, 0x61, 0xaa, 0x21, 0x01, 0x18, 0xd5, 0x86, 0xb5, 0x78,
0x01, 0x4c, 0xf8, 0x4f, 0x52, 0xbd, 0xe8, 0x7d, 0x99, 0x03, 0x7d, 0x8d, 0xce, 0x1a, 0x7d, 0xce,
0xc2, 0xb2, 0xfb, 0x02, 0xfc, 0x67, 0xbf, 0x1b, 0xaa, 0x8c, 0x35, 0x26, 0x15, 0x2d, 0x84, 0x96,
0xc4, 0xac, 0xf6, 0x5b, 0xc0, 0x03, 0x77, 0x6f, 0xc2, 0xe4, 0xbd, 0xe1, 0x25, 0xa6, 0x84, 0xaf,
0x20, 0xab, 0x54, 0xf1, 0x71, 0x05, 0x83, 0x91, 0x98, 0x81, 0x7b, 0x77, 0xaa, 0xbd, 0x2e, 0xae,
0x3f, 0xd6, 0x27, 0x2e, 0xcd, 0x3d, 0x61, 0x19, 0xb1, 0x24, 0xc0, 0x2f, 0xbd, 0x2b, 0x36, 0x05,
0xd7, 0x06, 0x3d, 0xcf, 0x27, 0x8e, 0x50, 0x82, 0x16, 0xa6, 0xc0, 0x09, 0xa3, 0x9b, 0x8f, 0x4b,
0x4b, 0x6c, 0x56, 0x0e, 0x97, 0xb3, 0xf5, 0x3a, 0xe0, 0x38, 0xcf, 0x72, 0x93, 0x28, 0xa3, 0x85,
0x6b, 0x5f, 0xfe, 0x61, 0x10, 0x77, 0xb0, 0x15, 0x92, 0x4c, 0x00, 0x86, 0x92, 0xc3, 0x09, 0x68,
0xb9, 0xc8, 0xc5, 0x78, 0x01, 0xcb, 0x9e, 0x2f, 0xf4, 0xf3, 0xd8, 0x41, 0xcd, 0x88, 0xd6, 0x8b,
0xae, 0x5b, 0x1e, 0x54, 0x7e, 0xf1, 0xec, 0xa0, 0xef, 0x75, 0xd6, 0xd9, 0xce, 0x1f, 0x6c, 0x8e,
0xe6, 0x5a, 0x15, 0x46, 0x1e, 0x88, 0x2e, 0x2b, 0xbb, 0xb9, 0xb4, 0xbf, 0x4f, 0x63, 0xf9, 0x5e,
0x8e, 0xcd, 0xa3, 0xfd, 0x11, 0x53, 0x2c, 0xf3, 0x54, 0x57, 0x3a, 0xf7, 0x88, 0x18, 0x57, 0x11,
0x9d, 0x89, 0xb5, 0xff, 0xca, 0xb1, 0x66, 0x22, 0xbe, 0x24, 0x78, 0x13, 0x69, 0x47, 0x16, 0xa3,
0xc0, 0x09, 0x6e, 0x9f, 0x0f, 0xed, 0x5f, 0x2f, 0xab, 0x9c, 0x95, 0xaa, 0x6f, 0xea, 0x00, 0xcf,
0x55, 0xde, 0xbd, 0x8f, 0xc9, 0x18, 0xc7, 0xc1, 0x41, 0x9b, 0xb0, 0x72, 0x35, 0x1a, 0x0f, 0xb9,
0x19, 0x11, 0x97, 0x85, 0x07, 0xa5, 0xe1, 0xc0, 0xb2, 0xb3, 0x0c, 0x74, 0x2f, 0x33, 0xb0, 0xc0,
0xe9, 0xe2, 0x48, 0xe0, 0x42, 0xb0, 0x23, 0xe5, 0x58, 0x93, 0x60, 0x17, 0x30, 0xa8, 0x13, 0x2f,
0x3c, 0x48, 0x18, 0x3b, 0xb1, 0x1b, 0x16, 0xc1, 0x57, 0xd6, 0xed, 0xd5, 0x75, 0x78, 0x01, 0xc1,
0x23, 0x2f, 0x8e, 0xf3, 0x91, 0xcd, 0x72, 0x5e, 0x12, 0x53, 0x8d, 0xfa, 0xc0, 0x7d, 0xc5, 0x93,
0x3f, 0xcf, 0xff, 0xf1, 0xf5, 0xc3, 0x23, 0xeb, 0x2e, 0x4b, 0x92, 0x9a, 0xe5, 0xb8, 0x2f, 0x6c,
0xc3, 0x47, 0x07, 0xec, 0x83, 0x0d, 0xff, 0x9d, 0xa9, 0x4d, 0x09, 0x9d, 0x75, 0xb4, 0x24, 0x16,
0xaf, 0x00, 0xe7, 0xc6, 0x1a, 0x8f, 0xaf, 0x64, 0x47, 0xa9, 0x50, 0xbf, 0x08, 0xfb, 0x2c, 0xf8,
0x77, 0xff, 0x14, 0x6d, 0x58, 0xe0, 0x74, 0xb3, 0x02, 0x50, 0xa6, 0x0c, 0xaa, 0xb1, 0x0b, 0x2b,
0x1d, 0x15, 0xa0, 0x4d, 0xfc, 0x88, 0xb7, 0x0f, 0x57, 0xf4, 0x92, 0xcc, 0x50, 0x3e, 0x35, 0x09,
0x0a, 0x18, 0xab, 0xcc, 0xf8, 0x72, 0xfd, 0xf7, 0x9f, 0x1b, 0xdd, 0x5d, 0x2b, 0xcb, 0xcd, 0xbd,
0xb7, 0x1d, 0x96, 0xdd, 0xb0, 0xd3, 0xdb, 0x64, 0xbe, 0x33, 0xa8, 0x92, 0x01, 0x0f, 0x2c, 0xe5,
0x85, 0xd2, 0x79, 0x9d, 0x23, 0xfe, 0xf8, 0xda, 0x1d, 0x34, 0xd6, 0x95, 0x7f, 0x93, 0x4d, 0x7e,
0xec, 0x4f, 0x03, 0xfe, 0xc1, 0x9d, 0xc0, 0x54, 0xe0, 0xca, 0xea, 0x95, 0x4d, 0x34, 0x82, 0x8c,
0x94, 0xd7, 0x87, 0xd2, 0x24, 0x5a, 0x1f, 0x00, 0x9e, 0xd8, 0x85, 0xe3, 0xf3, 0xe0, 0x1c, 0xfb,
0x9b, 0xf0, 0x63, 0xb8, 0x64, 0x45, 0x70, 0x32, 0x3d, 0x69, 0x9a, 0xf6, 0x72, 0x59, 0x1a, 0xb0,
0x3a, 0xb5, 0xfb, 0x60, 0x38, 0x05, 0x0e, 0x52, 0x17, 0x3f, 0xfc, 0x09, 0x14, 0xee, 0x18, 0x04,
0xe3, 0x76, 0x13, 0xad, 0x09, 0x5c, 0x2c, 0x2c, 0x5c, 0xed, 0x5d, 0xd6, 0xae, 0xe0, 0xfb, 0x2d,
0x18, 0x47, 0x11, 0xb2, 0x15, 0x74, 0x67, 0x94, 0x05, 0x83, 0x9f, 0xe3, 0xd5, 0x53, 0x55, 0x9d,
0x72, 0xbe, 0xf0, 0x2f, 0x69, 0x48, 0x6f, 0x63, 0xf9, 0x5e, 0xf2, 0xc0, 0xb0, 0xee, 0x6f, 0x66,
0x51, 0x53, 0xbb, 0x4d, 0x28, 0xb6, 0x11, 0x36, 0x16, 0xc8, 0xea, 0xfd, 0x5e, 0xba, 0xda, 0x1e,
0x76, 0x99, 0xd0, 0x65, 0x54, 0xea, 0xc0, 0x72, 0x6e, 0x77, 0xe7, 0xee, 0xdb, 0xd7, 0xfb, 0x9b,
0x4d, 0xda, 0x37, 0x30, 0x6e, 0xf8, 0xc5, 0xef, 0x60, 0x13, 0x0e, 0x18, 0x58, 0x9c, 0x79, 0xda,
0xd0, 0xc2, 0xa3, 0x2c, 0x66, 0xb2, 0x8b, 0xa3, 0x73, 0x3b, 0x35, 0x5a, 0xb1, 0x39, 0x09, 0x26,
0x0d, 0xc0, 0x24, 0x89, 0xaf, 0x00, 0x53, 0xc4, 0xaa, 0x5c, 0x5e, 0xdd, 0x03, 0xb4, 0xf5, 0xfa,
0x91, 0x66, 0xac, 0x1c, 0xad, 0x46, 0x38, 0x0e, 0x80, 0xa8, 0xba, 0x74, 0xf5, 0xf5, 0x60, 0xbf,
0xfe, 0x7f, 0x0d, 0xb7, 0xad, 0x38, 0x3d, 0xb4, 0x23, 0xa3, 0xbc, 0x63, 0xc2, 0x08, 0xde, 0x9a,
0x00, 0x88, 0xbf, 0x83, 0xe6, 0xec, 0xfc, 0x57, 0xf0, 0xff, 0xd5, 0xb9, 0x07, 0x3d, 0x42, 0x4d,
0x82, 0xcb, 0xf9, 0x3d, 0xc8, 0x70, 0xea, 0xb5, 0x50, 0xb3, 0x11, 0x7c, 0x82, 0x3f, 0xfe, 0x57,
0xf0, 0x0e, 0xf3, 0x8a, 0xbd, 0x9f, 0x93, 0xb4, 0x96, 0xe5, 0xf1, 0x97, 0x96, 0x0c, 0xd1, 0x79,
0xdf, 0xf9, 0x6f, 0xc0, 0xd5, 0x54, 0x81, 0xbb, 0x97, 0x5f, 0xfb, 0xb6, 0x2e, 0x15, 0xb1, 0x7c,
0x5b, 0x8b, 0x4b, 0xa3, 0x9a, 0x30, 0xf5, 0x82, 0x1a, 0x7d, 0x1c, 0x16, 0x6c, 0x08, 0xf2, 0x5c,
0x17, 0x52, 0xbe, 0xff, 0x52, 0x3e, 0xf7, 0xbb, 0x25, 0x80, 0x5c, 0x46, 0x89, 0xe5, 0xc7, 0x62,
0x74, 0xea, 0xdc, 0xa0, 0xda, 0xa0, 0x3f, 0x08, 0xdf, 0xcf, 0xaf, 0x9b, 0xdf, 0x78, 0x91, 0x6b,
0x7c, 0x00, 0x77, 0x74, 0xb0, 0x3e, 0x0c, 0xd4, 0x6c, 0xaf, 0xdd, 0xed, 0xc7, 0x71, 0xa7, 0x16,
0x3f, 0x33, 0x8f, 0x5a, 0xc7, 0xbd, 0x47, 0x76, 0x07, 0x24, 0x11, 0xd7, 0xa6, 0x9a, 0x56, 0x69,
0x3b, 0x12, 0xb0, 0xe0, 0xd0, 0x12, 0x23, 0xc4, 0x8c, 0x32, 0xc4, 0x3a, 0x00, 0xba, 0x6e, 0xc3,
0x89, 0x7c, 0x96, 0x48, 0x0a, 0x08, 0x24, 0x30, 0x8d, 0x2c, 0x0b, 0x56, 0xe6, 0xd5, 0x55, 0x39,
0x5e, 0xf5, 0xed, 0x3c, 0xc0, 0x5b, 0x59, 0x0b, 0xe9, 0xbe, 0x2e, 0xb9, 0xb5, 0x7e, 0xff, 0xbd,
0x6f, 0x80, 0x1e, 0xc4, 0xe4, 0x1b, 0xaf, 0xfc, 0x55, 0x1f, 0x19, 0x7d, 0x4c, 0x4f, 0xcc, 0xbc,
0xee, 0xb2, 0x67, 0x02, 0xe9, 0x2f, 0x1b, 0xe4, 0xfa, 0x1b, 0xc5, 0xfc, 0x9a, 0x2e, 0x41, 0x82,
0xc6, 0xfe, 0x01, 0x8f, 0x4b, 0x1b, 0x79, 0x1b, 0x2b, 0x85, 0xe9, 0x5f, 0x92, 0xe1, 0xba, 0x97,
0xae, 0x9b, 0x68, 0x7c, 0xd2, 0x4c, 0x8d, 0xdc, 0xb2, 0xc0, 0x46, 0xd2, 0xe1, 0x5e, 0x6b, 0xf3,
0x79, 0x7a, 0xf3, 0xcb, 0x3c, 0x5d, 0x23, 0xb0, 0x93, 0x47, 0x39, 0xfd, 0x4e, 0x1a, 0xe5, 0x95,
0xc1, 0x47, 0x5f, 0xf9, 0xed, 0x0b, 0xdc, 0xcc, 0x75, 0x2a, 0x6a, 0x06, 0x00, 0x91, 0x19, 0x8c,
0x56, 0xfd, 0x00, 0x93, 0x18, 0xfc, 0xd2, 0x30, 0xbd, 0xa2, 0xa2, 0xa6, 0xd8, 0x96, 0x78, 0xf1,
0x8e, 0x7c, 0x46, 0xc8, 0x79, 0xf8, 0xda, 0xcb, 0x3b, 0x02, 0x6d, 0x5e, 0x01, 0x32, 0xf4, 0x86,
0x7f, 0xe6, 0x7c, 0xfd, 0xcf, 0x08, 0x0f, 0x09, 0x6f, 0x03, 0xfa, 0x11, 0x96, 0x7b, 0x73, 0x75,
0x90, 0xdc, 0xfe, 0x5e, 0x62, 0x61, 0x67, 0x1b, 0xc8, 0x8c, 0xb9, 0x32, 0x98, 0x4d, 0x9a, 0xd4,
0xa0, 0x39, 0x5b, 0xd6, 0x0b, 0x56, 0x56, 0xcf, 0x55, 0x43, 0x2f, 0x6f, 0xb8, 0xf6, 0x83, 0xff,
0xe7, 0x62, 0x7b, 0x73, 0xf9, 0x4e, 0xea, 0xec, 0x3e, 0x1e, 0xa6, 0xbf, 0xbf, 0x87, 0xdb, 0x1a,
0xdb, 0xca, 0xb4, 0xc7, 0xa5, 0x30, 0x2b, 0xcd, 0x79, 0xef, 0x9b, 0x58, 0xdc, 0x30, 0xf8, 0x70,
0x8e, 0x2f, 0x42, 0xde, 0x83, 0x44, 0x25, 0xf7, 0xdd, 0x55, 0x3e, 0x12, 0x55, 0x4e, 0x15, 0x5f,
0x37, 0x91, 0xe9, 0xe3, 0x1f, 0x20, 0xda, 0x51, 0xcb, 0xff, 0xfd, 0x6d, 0x72, 0xfc, 0x3f, 0x5b,
0x0f, 0xc7, 0x70, 0xcd, 0x84, 0xd9, 0x8e, 0x36, 0x92, 0x1d, 0x4a, 0x61, 0x70, 0x9b, 0x9b, 0x68,
0x9b, 0x27, 0xc6, 0x99, 0x10, 0xa3, 0xce, 0x53, 0x21, 0xf4, 0x31, 0x3c, 0x87, 0x84, 0x69, 0x81,
0x9d, 0xa5, 0xef, 0x0f, 0xf1, 0xca, 0xae, 0xf0, 0x63, 0x1c, 0x84, 0x5b, 0x8f, 0x77, 0xe5, 0x6b,
0x11, 0xec, 0x26, 0x59, 0x9f, 0x16, 0x89, 0xe1, 0xc2, 0xad, 0x56, 0xb8, 0x2e, 0x31, 0x68, 0x5d,
0x2a, 0x83, 0xf6, 0xef, 0xfe, 0x61, 0x76, 0x9d, 0x60, 0x2a, 0x87, 0xc5, 0x85, 0xff, 0x22, 0xe4,
0x4b, 0x08, 0x48, 0x13, 0x8a, 0x00, 0x3b, 0x48, 0xc9, 0x21, 0x06, 0xc1, 0xbb, 0xd9, 0xd1, 0x3d,
0x52, 0x49, 0x6a, 0x44, 0x93, 0x09, 0xc8, 0x8b, 0x8a, 0x5e, 0x7e, 0x8d, 0x0b, 0x26, 0xc3, 0x59,
0xbb, 0xad, 0x92, 0xaa, 0x46, 0x0b, 0xbe, 0xd6, 0xaa, 0xa0, 0x36, 0x5f, 0x34, 0x6a, 0x88, 0x84,
0x0a, 0xfc, 0xa8, 0xb8, 0xbd, 0xb4, 0xa4, 0xf4, 0xc6, 0xe1, 0x88, 0x55, 0x5d, 0x4e, 0xc9, 0xdc,
0x7d, 0xa2, 0x71, 0x0d, 0x18, 0x1e, 0x16, 0x1d, 0x17, 0x11, 0xb3, 0x91, 0x92, 0xf0, 0xc6, 0x1b,
0x3a, 0x54, 0x80, 0x8f, 0x11, 0x71, 0x9b, 0x02, 0x16, 0xf4, 0x8f, 0xf0, 0x37, 0xf2, 0xea, 0xa8,
0x95, 0x61, 0x8e, 0x8f, 0x22, 0x22, 0xc0, 0xef, 0x51, 0x0b, 0x18, 0x8d, 0xd8, 0xe8, 0x0b, 0x76,
0x3e, 0xa5, 0x28, 0xc3, 0x6f, 0xde, 0xf1, 0xf4, 0x55, 0x7d, 0x48, 0xfd, 0xe3, 0xa5, 0x64, 0x45,
0x70, 0x3c, 0xbe, 0x0f, 0x06, 0x22, 0xc8, 0x95, 0x89, 0x0a, 0x56, 0xd6, 0x8a, 0x36, 0x42, 0xbe,
0xe6, 0x20, 0x00, 0x5c, 0xaa, 0xc6, 0x2e, 0xea, 0x19, 0x11, 0xfc, 0x07, 0x38, 0x08, 0x30, 0x7f,
0xb1, 0xe7, 0xfd, 0x71, 0x1c, 0x37, 0xc1, 0xd1, 0x4c, 0x3f, 0x16, 0x4c, 0x4d, 0x06, 0x69, 0xca,
0x3b, 0x8b, 0x79, 0xfd, 0x05, 0x0c, 0x8c, 0x60, 0xee, 0xaa, 0x91, 0x16, 0xf1, 0xab, 0x8a, 0x55,
0xf1, 0xb2, 0x85, 0xfc, 0xa7, 0xde, 0x6f, 0x32, 0xd3, 0x1f, 0xfd, 0xc2, 0x0d, 0xc7, 0x74, 0x4d,
0xcb, 0xae, 0x77, 0x96, 0xd3, 0xf3, 0x0a, 0xa0, 0x97, 0xd1, 0xf5, 0x54, 0xb8, 0xba, 0x45, 0x63,
0x1f, 0xe4, 0xef, 0x80, 0x63, 0xd4, 0xef, 0xef, 0x74, 0x56, 0x93, 0xf3, 0xfe, 0x60, 0x97, 0xcb,
0xf5, 0x55, 0x89, 0x0b, 0x92, 0x4e, 0xb5, 0x22, 0xe3, 0x0e, 0xc4, 0x5e, 0xc9, 0x0b, 0xc8, 0x52,
0xc1, 0x43, 0x0a, 0x6e, 0x6e, 0xee, 0xe9, 0xd2, 0x58, 0xe1, 0x08, 0xd5, 0x93, 0xdd, 0x96, 0x57,
0x8b, 0x06, 0x2f, 0xb4, 0x3b, 0x0a, 0x00, 0x5f, 0x9d, 0x55, 0x14, 0x57, 0xfe, 0xee, 0xbe, 0x19,
0x61, 0xa5, 0x63, 0x81, 0x6a, 0xf0, 0x1e, 0xf8, 0xdc, 0xa0, 0xf4, 0x98, 0xaa, 0x0b, 0x40, 0xfa,
0xea, 0xb1, 0xb7, 0xce, 0xb7, 0x80, 0x3b, 0x56, 0x09, 0xb6, 0xa5, 0x92, 0x84, 0x0b, 0x01, 0xca,
0x51, 0x86, 0xad, 0xd5, 0x1a, 0xc5, 0x9f, 0xba, 0x2b, 0xc5, 0x30, 0x66, 0xd2, 0xbc, 0xa5, 0xb1,
0xcd, 0x3a, 0x73, 0x29, 0x49, 0x47, 0x58, 0xe3, 0xb7, 0x55, 0xeb, 0x15, 0x7f, 0x0d, 0xf8, 0x12,
0x55, 0x02, 0x92, 0x3a, 0xe7, 0x60, 0xdf, 0xfa, 0x3a, 0x3a, 0x01, 0x35, 0xf0, 0x06, 0xe5, 0x85,
0xdd, 0x21, 0xef, 0xe7, 0x84, 0xda, 0x08, 0x22, 0xa2, 0xab, 0x83, 0x92, 0x25, 0x15, 0x8b, 0x00,
0x4f, 0x6a, 0xc9, 0x01, 0xa4, 0xc1, 0x62, 0xf6, 0x07, 0x24, 0x19, 0xbb, 0x5d, 0xe0, 0x36, 0x69,
0xf5, 0x6a, 0xef, 0x22, 0x73, 0xc7, 0xb6, 0xc2, 0x03, 0xc9, 0x53, 0x9f, 0x4d, 0xd5, 0x1e, 0x03,
0x00, 0xd9, 0xa4, 0x44, 0x44, 0x45, 0x15, 0x2e, 0xdb, 0x91, 0x09, 0xf0, 0xd8, 0x30, 0x0b, 0x08,
0x29, 0x55, 0x1e, 0xd2, 0x58, 0xa1, 0xed, 0xda, 0xe0, 0x64, 0xec, 0x83, 0x2b, 0x5b, 0x30, 0x7a,
0x90, 0xb0, 0x5f, 0x1a, 0x55, 0x38, 0xa6, 0x5d, 0x6f, 0xaa, 0x4e, 0x03, 0x6e, 0x00, 0x0b, 0xc6,
0x35, 0x5d, 0x37, 0x23, 0xea, 0xa9, 0x81, 0xa2, 0x52, 0x0e, 0x9c, 0xd5, 0x7e, 0xaa, 0x98, 0x05,
0x5c, 0x0c, 0xf3, 0x41, 0x07, 0x3e, 0x81, 0x6b, 0xf1, 0x7f, 0x50, 0xef, 0x9b, 0x8f, 0xc2, 0xf0,
0x97, 0xaf, 0xbf, 0x00, 0x52, 0x02, 0x57, 0x59, 0xfc, 0x75, 0x0c, 0x90, 0x1a, 0x47, 0x17, 0x0e,
0xf0, 0x0d, 0x3f, 0x6e, 0x97, 0x80, 0x99, 0x60, 0x37, 0x6a, 0xcc, 0xe9, 0xd9, 0x11, 0x09, 0xc4,
0x09, 0x60, 0x00, 0x25, 0x06, 0x2f, 0xb6, 0x8e, 0xaf, 0x68, 0xa9, 0x29, 0xab, 0xa9, 0xaa, 0xa7,
0x45, 0xcb, 0x0c, 0xe4, 0x85, 0xbc, 0x27, 0xd5, 0xa6, 0x00, 0x72, 0xa5, 0xc0, 0x72, 0xf1, 0xef,
0x71, 0x42, 0x0c, 0x1b, 0x02, 0xdb, 0x44, 0x40, 0x0b, 0xde, 0x15, 0x7c, 0x43, 0xdf, 0xe2, 0x52,
0x0b, 0x2c, 0x9f, 0xf5, 0x24, 0xab, 0xc4, 0x69, 0xaa, 0xa2, 0xe0, 0x39, 0xe0, 0x2e, 0x11, 0x2e,
0x11, 0x39, 0x0f, 0x38, 0x5e, 0x6f, 0x2a, 0x70, 0xa3, 0x5e, 0x9c, 0xdc, 0xe8, 0xd5, 0x63, 0x6b,
0x70, 0x05, 0xf0, 0x27, 0xbd, 0x70, 0x0d, 0x4b, 0xf5, 0x54, 0xe0, 0x79, 0x11, 0x4a, 0x16, 0xb9,
0x03, 0x2f, 0x58, 0xf5, 0x39, 0xbc, 0xa9, 0xc9, 0xff, 0xc5, 0x94, 0x6a, 0xa8, 0x51, 0x05, 0x0b,
0x82, 0xfe, 0xa0, 0xd7, 0xd5, 0xc4, 0xae, 0xb7, 0x45, 0xfc, 0x4a, 0x2f, 0x07, 0x72, 0x87, 0x70,
0x1c, 0x88, 0x97, 0x8d, 0x59, 0x30, 0x3d, 0xfa, 0x80, 0xd3, 0xd8, 0x09, 0xaa, 0x8f, 0xbc, 0x03,
0xfc, 0x0a, 0xaf, 0x00, 0x70, 0x16, 0xa4, 0xec, 0xaa, 0xa3, 0xe3, 0x76, 0xd2, 0x85, 0x11, 0xef,
0x91, 0x91, 0x1c, 0xc0, 0x67, 0xbc, 0x50, 0xf8, 0x67, 0xbc, 0x32, 0x78, 0xf5, 0xde, 0xed, 0x27,
0x67, 0x01, 0x12, 0xf0, 0x46, 0x7d, 0x49, 0xe3, 0x75, 0xfc, 0x3a, 0x54, 0xe2, 0xf0, 0xe6, 0x71,
0xbf, 0x55, 0x14, 0xaa, 0x28, 0xe0, 0xec, 0x65, 0x5e, 0x7f, 0x16, 0x44, 0x97, 0x02, 0x92, 0x13,
0x34, 0x9f, 0x4b, 0xc4, 0xd3, 0x78, 0xe5, 0x6b, 0x99, 0x29, 0xa3, 0xa4, 0xad, 0xd1, 0xe5, 0x39,
0xc9, 0x07, 0x00, 0x24, 0x65, 0x03, 0x6b, 0x27, 0xb7, 0xb1, 0x48, 0xbf, 0x56, 0x0e, 0xe6, 0xa3,
0xb9, 0x46, 0x42, 0x18, 0xe8, 0xfa, 0x07, 0x80, 0x87, 0xee, 0xc8, 0x88, 0xc0, 0x21, 0x89, 0x8f,
0x58, 0xe9, 0x08, 0xb4, 0x6b, 0xbc, 0x79, 0xac, 0x56, 0x5f, 0xa6, 0x4b, 0x89, 0x6f, 0x88, 0xe2,
0xc0, 0x69, 0x93, 0x51, 0x1f, 0x63, 0xda, 0xaa, 0xe7, 0x8b, 0xfe, 0xf4, 0xf6, 0x92, 0x34, 0xb5,
0x55, 0x0a, 0x41, 0x5d, 0x8c, 0x48, 0x2d, 0x22, 0x64, 0x44, 0xf0, 0x22, 0xc0, 0x77, 0x44, 0x67,
0xb4, 0xc4, 0x2b, 0x97, 0x85, 0xe0, 0x4b, 0xe1, 0x77, 0x0e, 0x83, 0x04, 0x74, 0xe8, 0x6d, 0x4c,
0xf0, 0xcb, 0x84, 0xe9, 0x60, 0x4b, 0x21, 0x6a, 0x73, 0xa6, 0x49, 0x1d, 0x2c, 0x22, 0x72, 0xae,
0xd7, 0xb8, 0xc5, 0xf8, 0x22, 0x00, 0x76, 0xf2, 0x85, 0x7c, 0x8e, 0x30, 0x92, 0x1f, 0x02, 0xe1,
0x12, 0x83, 0xc5, 0x80, 0xc1, 0xc4, 0x0d, 0x53, 0x5b, 0xd8, 0xe8, 0x95, 0x4d, 0x24, 0x81, 0xc0,
0x2a, 0x34, 0x24, 0x6a, 0x47, 0x7f, 0xf3, 0x8c, 0x15, 0x6d, 0xda, 0xf8, 0x1d, 0xe1, 0x6d, 0x1c,
0x00, 0xb8, 0x4c, 0x74, 0xfa, 0xd1, 0xaa, 0x35, 0xce, 0xba, 0x51, 0xa8, 0xc3, 0x87, 0x22, 0x10,
0x21, 0xf2, 0xbc, 0xbc, 0xa6, 0x8d, 0x6f, 0x6d, 0x74, 0x66, 0xb8, 0x64, 0xe6, 0x7f, 0xe3, 0x50,
0xef, 0x59, 0x02, 0x31, 0xeb, 0x0c, 0x69, 0x2a, 0x5a, 0xe3, 0x73, 0xe4, 0x8d, 0xfe, 0xcd, 0xab,
0x00, 0x4b, 0x46, 0x06, 0x0d, 0xbf, 0x57, 0xb2, 0xe2, 0x4d, 0x69, 0xb0, 0xda, 0x69, 0x22, 0x17,
0xde, 0x65, 0xdf, 0xe4, 0x3b, 0x98, 0x18, 0x6b, 0xda, 0xd3, 0x25, 0xbd, 0x36, 0x1c, 0xa5, 0x56,
0x8e, 0x2c, 0x60, 0x17, 0x38, 0x70, 0x3b, 0xf6, 0x64, 0x44, 0x5e, 0x34, 0xd8, 0xe3, 0x6d, 0x74,
0xd8, 0xcd, 0xe1, 0x16, 0x31, 0xc2, 0x5c, 0x2d, 0xb6, 0x34, 0xb7, 0x95, 0x6e, 0x8e, 0x2e, 0x31,
0x02, 0xac, 0x20, 0x9e, 0x05, 0x45, 0x86, 0x01, 0x10, 0x54, 0xe3, 0x91, 0xad, 0xf0, 0x9e, 0x3d,
0x96, 0xda, 0x7d, 0xe1, 0x25, 0x06, 0x45, 0x00, 0x48, 0xe4, 0xcd, 0xe0, 0xec, 0x06, 0x4e, 0x2d,
0x92, 0x74, 0xa1, 0x26, 0x5e, 0xe9, 0x26, 0x8a, 0x30, 0x21, 0x34, 0x47, 0x44, 0x67, 0xea, 0xd6,
0x59, 0xa4, 0x39, 0xb5, 0x87, 0x22, 0x3f, 0x63, 0x52, 0xe3, 0xda, 0xbc, 0x80, 0x01, 0x45, 0x17,
0xdd, 0x32, 0xcd, 0x89, 0x8c, 0x22, 0xc2, 0xc8, 0x16, 0xa8, 0x44, 0x61, 0x18, 0x56, 0x15, 0x99,
0x66, 0x46, 0x64, 0x61, 0x18, 0x56, 0x22, 0xd5, 0xce, 0x8c, 0xe7, 0x0a, 0xcc, 0x8e, 0xba, 0xf2,
0x2d, 0x91, 0xd7, 0xd6, 0x37, 0xc7, 0xbb, 0xe1, 0xc9, 0x3d, 0xf1, 0xbf, 0x93, 0x8f, 0x28, 0xdb,
0xf4, 0x28, 0x32, 0x0e, 0xa2, 0xb3, 0x35, 0x5f, 0x02, 0x4e, 0xda, 0xb0, 0xf7, 0xe8, 0xcb, 0x50,
0x9a, 0xf6, 0xe7, 0x1a, 0xe3, 0x80, 0x4f, 0x29, 0xb5, 0x04, 0xfd, 0x8a, 0x95, 0x8c, 0x09, 0xa4,
0x0e, 0x58, 0x13, 0x47, 0xa8, 0x13, 0x34, 0xec, 0xf6, 0x7f, 0x2b, 0x30, 0xf2, 0xe2, 0xca, 0xcd,
0x44, 0xff, 0x75, 0xeb, 0xfd, 0xa7, 0x8f, 0x6e, 0x6f, 0xf4, 0x6e, 0x7c, 0x0f, 0x75, 0x5b, 0x9f,
0x97, 0x5d, 0xc6, 0x38, 0xaa, 0xe3, 0x2d, 0x5a, 0x22, 0x8f, 0xcc, 0xb0, 0x82, 0xcc, 0xa6, 0xbd,
0x00, 0xe6, 0xcf, 0x2a, 0x50, 0xd0, 0x0e, 0xca, 0x4d, 0xb8, 0x27, 0xec, 0x86, 0x8c, 0xe4, 0xaa,
0xc1, 0xec, 0xd9, 0x7e, 0x87, 0x4d, 0x51, 0xa3, 0x1d, 0x94, 0x88, 0xf0, 0x30, 0xac, 0x20, 0xfb,
0xbe, 0x76, 0xf2, 0xbb, 0x1f, 0xca, 0x0e, 0x83, 0x13, 0x78, 0x6c, 0xf0, 0x97, 0x4a, 0x62, 0x53,
0xd0, 0x21, 0xa5, 0xc8, 0x5f, 0x3b, 0xe0, 0x1a, 0x17, 0xdf, 0x9b, 0x01, 0x68, 0x1b, 0x6a, 0xcf,
0xa6, 0x41, 0xe3, 0x46, 0x70, 0x1c, 0xfb, 0x3c, 0xb0, 0x1c, 0x3b, 0xe5, 0x7c, 0xf4, 0x21, 0x77,
0x62, 0x2c, 0x2a, 0x5d, 0xee, 0xd2, 0xec, 0x70, 0x88, 0x57, 0x11, 0xc8, 0x42, 0xf8, 0x67, 0x8e,
0xc8, 0x8c, 0xd4, 0x1e, 0xce, 0x0d, 0xdb, 0x03, 0xf4, 0x73, 0x43, 0x05, 0xbf, 0xad, 0xa1, 0x4c,
0x6e, 0xa4, 0x55, 0x00, 0x0b, 0x5d, 0x07, 0xbc, 0x19, 0x6f, 0x3d, 0x4f, 0x80, 0xe4, 0x53, 0x7f,
0xdf, 0xd5, 0xc2, 0x0f, 0xc5, 0xde, 0xfa, 0x78, 0xf5, 0xd6, 0xfb, 0xda, 0xaa, 0xba, 0xae, 0xfe,
0xf3, 0xce, 0x8a, 0xa4, 0x41, 0x0b, 0xe3, 0xc7, 0x01, 0x1b, 0x35, 0xdb, 0x83, 0xfc, 0x91, 0x4d,
0xf2, 0xe7, 0x76, 0x0f, 0xc0, 0xaf, 0x68, 0xe1, 0x81, 0x18, 0xb3, 0xf0, 0x02, 0x8d, 0xcd, 0x36,
0x01, 0x4a, 0x94, 0xe2, 0x7e, 0xaa, 0xc2, 0x91, 0x44, 0xa5, 0xd5, 0x50, 0x45, 0x87, 0xf5, 0xe4,
0x3b, 0xc0, 0xd9, 0x81, 0x1b, 0x24, 0x29, 0x98, 0xd4, 0x9e, 0x7f, 0xe7, 0xea, 0xa9, 0x83, 0xab,
0xf7, 0xb4, 0x88, 0x98, 0x87, 0x9c, 0xf6, 0xb7, 0x35, 0xf8, 0xd8, 0x35, 0x4b, 0xfb, 0xde, 0xd1,
0x81, 0xc6, 0x6e, 0x0e, 0x9c, 0x76, 0x8e, 0x03, 0x95, 0x27, 0x74, 0x10, 0xc2, 0x9f, 0x82, 0x8f,
0x4d, 0x75, 0x78, 0xf3, 0x91, 0xeb, 0x8e, 0x1d, 0x91, 0xfd, 0x80, 0x80, 0x39, 0xc9, 0xb1, 0x07,
0xbe, 0x03, 0x63, 0x65, 0x58, 0x5f, 0x74, 0x2e, 0x4d, 0x35, 0xe6, 0xb4, 0x55, 0x5c, 0x62, 0xe7,
0x3e, 0x71, 0x55, 0x5a, 0x03, 0xe7, 0x6d, 0xf3, 0x6b, 0xd9, 0x0e, 0x56, 0x88, 0xe6, 0x35, 0x50,
0x09, 0x52, 0xdf, 0x41, 0xad, 0xbb, 0xad, 0x9f, 0xf4, 0xc1, 0x07, 0xfa, 0x84, 0x0f, 0x9e, 0x5e,
0xa0, 0xe3, 0x3c, 0xa5, 0x6e, 0x02, 0x6f, 0x11, 0xc1, 0x35, 0xc8, 0xc1, 0xce, 0xe2, 0x5f, 0x3c,
0x13, 0x58, 0xac, 0xa9, 0xc5, 0xf9, 0xc3, 0xb4, 0x8e, 0xcf, 0x71, 0xf4, 0x38, 0x0e, 0xeb, 0x1e,
0xe8, 0x21, 0x39, 0xad, 0x9a, 0xe5, 0x36, 0x68, 0x9d, 0xe0, 0xac, 0x76, 0x1c, 0x3b, 0xa5, 0x8d,
0xfe, 0xb8, 0x84, 0xf7, 0xf6, 0xd2, 0xc3, 0x89, 0x0c, 0xb2, 0x1c, 0xcb, 0x9a, 0x40, 0x83, 0x98,
0x97, 0xe4, 0xfa, 0xdb, 0x77, 0x39, 0x17, 0x69, 0xc1, 0x9d, 0x4d, 0xaa, 0x54, 0x7f, 0xff, 0xd5,
0xe9, 0x4a, 0x1c, 0x92, 0x84, 0x4d, 0x41, 0x0b, 0xb3, 0xc7, 0x19, 0x6f, 0xa5, 0x92, 0xe3, 0x46,
0x15, 0x8a, 0x6c, 0xdd, 0x66, 0x08, 0xbc, 0x68, 0xa7, 0x80, 0xea, 0xf3, 0xec, 0x70, 0x9c, 0x8f,
0x6f, 0x89, 0x5f, 0x05, 0x42, 0xd2, 0xfb, 0xfb, 0x0b, 0x3b, 0xf3, 0x3b, 0xb2, 0xdc, 0x8f, 0xb1,
0x6e, 0x1f, 0xe8, 0x89, 0xef, 0xc2, 0xfe, 0x02, 0x85, 0x3e, 0x72, 0x4b, 0x21, 0x34, 0x01, 0xce,
0x63, 0xc7, 0x04, 0x6d, 0x33, 0xbc, 0x7c, 0xa7, 0x55, 0x7a, 0x0f, 0x0f, 0x45, 0x04, 0xf3, 0xa5,
0xe5, 0xc0, 0xfb, 0x2e, 0xb1, 0xd9, 0x73, 0x1f, 0x7f, 0x96, 0x40, 0xe3, 0xc0, 0x76, 0xf6, 0xaf,
0x2b, 0x2b, 0x0a, 0x7b, 0x7f, 0xde, 0x05, 0xbd, 0xbf, 0x7e, 0x17, 0x23, 0x25, 0x2e, 0x62, 0x5b,
0xf0, 0x27, 0x55, 0x4e, 0xc5, 0x7d, 0x25, 0xf2, 0x9a, 0x3a, 0xe7, 0x14, 0xe3, 0x80, 0xea, 0x31,
0x0b, 0x80, 0x8b, 0xc0, 0x05, 0x5d, 0xdd, 0x1e, 0xe3, 0x93, 0x6b, 0xcb, 0x79, 0x7b, 0x3f, 0xb5,
0x06, 0x52, 0x2b, 0xe6, 0x3a, 0xaa, 0xf1, 0xaf, 0x1d, 0x96, 0x6b, 0x0e, 0x52, 0x04, 0xd7, 0x7e,
0x69, 0xf7, 0x2b, 0x58, 0x69, 0xb6, 0xf5, 0x50, 0xbc, 0x7a, 0x85, 0x79, 0x42, 0x9c, 0x95, 0x5e,
0x60, 0x5c, 0x72, 0x1b, 0xe4, 0xaa, 0xb6, 0xf1, 0xe6, 0x77, 0xec, 0xb4, 0x5f, 0x6c, 0xd1, 0xe9,
0x28, 0xb0, 0x25, 0x40, 0xc2, 0x5e, 0xbe, 0x73, 0x06, 0xcb, 0xd2, 0x3c, 0xd0, 0x71, 0x75, 0x20,
0xb9, 0xaa, 0x90, 0x1d, 0xc8, 0x88, 0xe0, 0x4a, 0xf0, 0x48, 0x07, 0x8a, 0x1c, 0xcc, 0x41, 0x92,
0x27, 0xd9, 0xa9, 0xd0, 0x2f, 0x78, 0x64, 0x6d, 0x79, 0x5f, 0x06, 0xbf, 0xa1, 0xf6, 0xe3, 0xc1,
0xf7, 0xd6, 0xb8, 0x69, 0xac, 0x1e, 0xdf, 0x9f, 0x17, 0x00, 0xd1, 0xb9, 0x63, 0xaf, 0x46, 0x7f,
0x2e, 0x44, 0x7e, 0x01, 0xf6, 0x96, 0x34, 0x75, 0xdd, 0x01, 0xf7, 0xc0, 0x2f, 0x80, 0x15, 0x14,
0x1c, 0xe4, 0x32, 0x8e, 0x01, 0xce, 0x20, 0xc7, 0x5f, 0x22, 0x3e, 0x02, 0xd9, 0x6f, 0x01, 0x6c,
0x18, 0x4f, 0x5a, 0xfe, 0x6d, 0x55, 0xa1, 0xce, 0xc3, 0x82, 0x48, 0x31, 0xe4, 0x44, 0xe0, 0x28,
0x3c, 0x19, 0xc0, 0x39, 0x91, 0x83, 0xc0, 0xe5, 0x81, 0x77, 0xa8, 0x1f, 0xf6, 0xea, 0x2f, 0xb4,
0xd1, 0xc6, 0x38, 0x9c, 0x1c, 0x07, 0x88, 0x83, 0x07, 0x4c, 0xc6, 0x9e, 0xe3, 0x5e, 0x08, 0xb1,
0x98, 0xf8, 0x3e, 0xaa, 0xc9, 0x6e, 0xc3, 0x9e, 0x1c, 0x61, 0x99, 0x11, 0xdc, 0x01, 0x1c, 0x07,
0xf6, 0x97, 0x18, 0xb5, 0x13, 0xa2, 0xe9, 0x58, 0xef, 0xfb, 0xe2, 0xe5, 0x97, 0xff, 0xea, 0xa9,
0x34, 0xca, 0xcc, 0x2f, 0x7f, 0xb1, 0x1c, 0xe0, 0x05, 0x70, 0x64, 0x44, 0xf0, 0x0c, 0xe0, 0x19,
0xc0, 0x63, 0x2a, 0x10, 0x9c, 0x44, 0xdc, 0x6b, 0xaa, 0x57, 0xbc, 0xef, 0x78, 0x00, 0x39, 0xc4,
0xc7, 0x5e, 0x8a, 0x81, 0x8c, 0x88, 0xde, 0x00, 0x8c, 0x06, 0xbe, 0xc3, 0xc9, 0xae, 0x51, 0x60,
0xd3, 0x8a, 0x78, 0xc0, 0x0e, 0x25, 0xff, 0xfd, 0xdb, 0x8f, 0x09, 0xd7, 0x52, 0xac, 0x4d, 0xd5,
0x5c, 0xa9, 0xe3, 0xad, 0xc1, 0x80, 0x60, 0x39, 0xc3, 0x87, 0x70, 0xe1, 0x52, 0x38, 0x15, 0xc0,
0x70, 0xe2, 0x1c, 0x38, 0x87, 0x0e, 0x05, 0x23, 0x03, 0x9c, 0x38, 0x76, 0x0e, 0x1c, 0x23, 0x87,
0x9c, 0x05, 0x76, 0x03, 0x1c, 0x38, 0x87, 0x0e, 0x05, 0x23, 0x03, 0x9c, 0x38, 0x76, 0x0e, 0x1c,
0x23, 0x87, 0x9c, 0x05, 0x76, 0x03, 0x1c, 0x38, 0x87, 0x0e, 0x05, 0x23, 0x03, 0x9c, 0x38, 0x76,
0x0e, 0x1c, 0x23, 0x87, 0x9c, 0x05, 0x76, 0x03, 0x1c, 0x38, 0x87, 0x0e, 0x05, 0x23, 0x03, 0x9c,
0x38, 0x76, 0x0e, 0x1c, 0x23, 0x87, 0x9c, 0x05, 0x76, 0x03, 0x1c, 0x38, 0x87, 0x0e, 0x05, 0x23,
0x03, 0x9c, 0x38, 0x76, 0x0e, 0x1c, 0x23, 0x87, 0x9c, 0x05, 0x76, 0x03, 0x1c, 0x38, 0x87, 0x0e,
0x05, 0x23, 0x03, 0x9c, 0x38, 0x76, 0x0e, 0x1c, 0x23, 0x87, 0x9c, 0x05, 0x76, 0x03, 0x1c, 0x38,
0x87, 0x0e, 0x05, 0x23, 0x0f, 0xfe, 0xf6, 0xa7, 0xbf, 0x78, 0xfb, 0x2d, 0xfb, 0xc5, 0x00, 0x00,
0x00, 0x00, 0x45, 0x49, 0x44, 0x4e, 0x42, 0xae, 0x82, 0x60,
],
disp: function()
{
// Do Nothing
throw "Does Nothing!";
}
};
try
{
LOGO.disp();
}
catch(e)
{
alert("Error: " + e + "\n");
}

View File

@ -0,0 +1 @@
77634b336741a98d2ef79484281245b12e9db939 big_script.js

View File

@ -0,0 +1,29 @@
//----------------------------------------------------------------------
//
// Silly / Pointless Javascript to test GZIP compression.
//
//----------------------------------------------------------------------
var LOGO = {
dat: [
0x50, 0x89, 0x47, 0x4e, 0x0a, 0x0d, 0x0a, 0x1a, 0x00, 0x00, 0x0d, 0x00, 0x48, 0x49, 0x52, 0x44,
0x00, 0x00, 0x45, 0x49, 0x44, 0x4e, 0x42, 0xae, 0x82, 0x60,
],
disp: function()
{
// Do Nothing
throw "Does Nothing!";
}
};
try
{
LOGO.disp();
}
catch(e)
{
alert("Error: " + e + "\n");
}

View File

@ -0,0 +1 @@
b8455ea37194b938cfc1773a73ecee2c994fa98e small_script.js

View File

@ -46,6 +46,12 @@
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import java.io.IOException;

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import java.io.IOException;
@ -87,7 +102,7 @@ public class DeflateFrameExtension extends AbstractExtension
catch(DataFormatException e)
{
LOG.warn(e);
getConnection().close(WebSocketConnectionD13.CLOSE_BAD_PAYLOAD,e.toString());
getConnection().close(WebSocketConnectionRFC6455.CLOSE_BAD_PAYLOAD,e.toString());
}
}

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import java.util.Map;

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import java.io.IOException;

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
public class IdentityExtension extends AbstractExtension

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
public interface MaskGen

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import java.util.Random;

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import java.io.IOException;
@ -317,27 +332,37 @@ public class WebSocketClient
{
if (!_factory.isStarted())
throw new IllegalStateException("Factory !started");
String scheme=uri.getScheme();
if (!("ws".equalsIgnoreCase(scheme) || "wss".equalsIgnoreCase(scheme)))
throw new IllegalArgumentException("Bad WebSocket scheme '"+scheme+"'");
if ("wss".equalsIgnoreCase(scheme))
throw new IOException("wss not supported");
InetSocketAddress address = toSocketAddress(uri);
SocketChannel channel = SocketChannel.open();
if (_bindAddress != null)
channel.socket().bind(_bindAddress);
channel.socket().setTcpNoDelay(true);
InetSocketAddress address=new InetSocketAddress(uri.getHost(),uri.getPort());
final WebSocketFuture holder=new WebSocketFuture(websocket,uri,this,channel);
WebSocketFuture holder = new WebSocketFuture(websocket, uri, this, channel);
channel.configureBlocking(false);
channel.connect(address);
_factory.getSelectorManager().register( channel, holder);
_factory.getSelectorManager().register(channel, holder);
return holder;
}
public static final InetSocketAddress toSocketAddress(URI uri)
{
String scheme = uri.getScheme();
if (!("ws".equalsIgnoreCase(scheme) || "wss".equalsIgnoreCase(scheme)))
throw new IllegalArgumentException("Bad WebSocket scheme: " + scheme);
int port = uri.getPort();
if (port == 0)
throw new IllegalArgumentException("Bad WebSocket port: " + port);
if (port < 0)
port = "ws".equals(scheme) ? 80 : 443;
InetSocketAddress address = new InetSocketAddress(uri.getHost(), port);
return address;
}
/* ------------------------------------------------------------ */
/** The Future Websocket Connection.
@ -422,9 +447,9 @@ public class WebSocketClient
if (channel!=null)
{
if (ex instanceof ProtocolException)
closeChannel(channel,WebSocketConnectionD13.CLOSE_PROTOCOL,ex.getMessage());
closeChannel(channel,WebSocketConnectionRFC6455.CLOSE_PROTOCOL,ex.getMessage());
else
closeChannel(channel,WebSocketConnectionD13.CLOSE_NO_CLOSE,ex.getMessage());
closeChannel(channel,WebSocketConnectionRFC6455.CLOSE_NO_CLOSE,ex.getMessage());
}
}
finally
@ -468,6 +493,7 @@ public class WebSocketClient
return _maskGen;
}
@Override
public String toString()
{
return "[" + _uri + ","+_websocket+"]@"+hashCode();
@ -489,7 +515,7 @@ public class WebSocketClient
if (channel!=null)
{
closeChannel(channel,WebSocketConnectionD13.CLOSE_NO_CLOSE,"cancelled");
closeChannel(channel,WebSocketConnectionRFC6455.CLOSE_NO_CLOSE,"cancelled");
return true;
}
return false;
@ -549,7 +575,7 @@ public class WebSocketClient
}
if (channel!=null)
closeChannel(channel,WebSocketConnectionD13.CLOSE_NO_CLOSE,"timeout");
closeChannel(channel,WebSocketConnectionRFC6455.CLOSE_NO_CLOSE,"timeout");
if (exception!=null)
throw new ExecutionException(exception);
if (connection!=null)

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import java.io.EOFException;
@ -5,7 +20,9 @@ import java.io.IOException;
import java.net.ProtocolException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.Map;
import java.util.Random;
import javax.net.ssl.SSLEngine;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpParser;
@ -20,30 +37,31 @@ import org.eclipse.jetty.io.SimpleBuffers;
import org.eclipse.jetty.io.nio.AsyncConnection;
import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
import org.eclipse.jetty.io.nio.SelectorManager;
import org.eclipse.jetty.io.nio.SslConnection;
import org.eclipse.jetty.util.B64Code;
import org.eclipse.jetty.util.QuotedStringTokenizer;
import org.eclipse.jetty.util.component.AggregateLifeCycle;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ThreadPool;
/* ------------------------------------------------------------ */
/**
* <p>WebSocketClientFactory contains the common components needed by multiple {@link WebSocketClient} instances
* (for example, a {@link ThreadPool}, a {@link SelectorManager NIO selector}, etc).</p>
* <p>WebSocketClients with different configurations should share the same factory to avoid to waste resources.</p>
* <p>If a ThreadPool or MaskGen is passed in the constructor, then it is not added with {@link AggregateLifeCycle#addBean(Object)},
* so it's lifecycle must be controlled externally.
* so it's lifecycle must be controlled externally.
*
* @see WebSocketClient
*/
public class WebSocketClientFactory extends AggregateLifeCycle
{
private final static Logger __log = org.eclipse.jetty.util.log.Log.getLogger(WebSocketClientFactory.class.getName());
private final static Random __random = new Random();
private final static ByteArrayBuffer __ACCEPT = new ByteArrayBuffer.CaseInsensitive("Sec-WebSocket-Accept");
private SslContextFactory _sslContextFactory = new SslContextFactory();
private final ThreadPool _threadPool;
private final WebSocketClientSelector _selector;
private MaskGen _maskGen;
@ -55,54 +73,67 @@ public class WebSocketClientFactory extends AggregateLifeCycle
*/
public WebSocketClientFactory()
{
_threadPool=new QueuedThreadPool();
addBean(_threadPool);
_buffers=new WebSocketBuffers(8*1024);
addBean(_buffers);
_maskGen=new RandomMaskGen();
addBean(_maskGen);
_selector=new WebSocketClientSelector();
addBean(_selector);
this(new QueuedThreadPool());
}
/* ------------------------------------------------------------ */
/**
* <p>Creates a WebSocketClientFactory with the given ThreadPool and the default configuration.</p>
*
* @param threadPool the ThreadPool instance to use
*/
public WebSocketClientFactory(ThreadPool threadPool)
{
_threadPool=threadPool;
addBean(threadPool);
_buffers=new WebSocketBuffers(8*1024);
addBean(_buffers);
_maskGen=new RandomMaskGen();
addBean(_maskGen);
_selector=new WebSocketClientSelector();
addBean(_selector);
this(threadPool, new RandomMaskGen());
}
/* ------------------------------------------------------------ */
/**
* <p>Creates a WebSocketClientFactory with the specified configuration.</p>
* <p>Creates a WebSocketClientFactory with the given ThreadPool and the given MaskGen.</p>
*
* @param threadPool the ThreadPool instance to use
* @param maskGen the mask generator to use
* @param maskGen the MaskGen instance to use
*/
public WebSocketClientFactory(ThreadPool threadPool, MaskGen maskGen)
{
this(threadPool, maskGen, 8192);
}
/* ------------------------------------------------------------ */
/**
* <p>Creates a WebSocketClientFactory with the specified configuration.</p>
*
* @param threadPool the ThreadPool instance to use
* @param maskGen the mask generator to use
* @param bufferSize the read buffer size
*/
public WebSocketClientFactory(ThreadPool threadPool,MaskGen maskGen,int bufferSize)
public WebSocketClientFactory(ThreadPool threadPool, MaskGen maskGen, int bufferSize)
{
_threadPool=threadPool;
_threadPool = threadPool;
addBean(threadPool);
_buffers=new WebSocketBuffers(bufferSize);
_buffers = new WebSocketBuffers(bufferSize);
addBean(_buffers);
_maskGen=maskGen;
_selector=new WebSocketClientSelector();
_maskGen = maskGen;
addBean(_maskGen);
_selector = new WebSocketClientSelector();
addBean(_selector);
addBean(_sslContextFactory);
}
/* ------------------------------------------------------------ */
/**
* @return the SslContextFactory used to configure SSL parameters
*/
public SslContextFactory getSslContextFactory()
{
return _sslContextFactory;
}
/* ------------------------------------------------------------ */
/**
* Get the selectorManager. Used to configure the manager.
*
* @return The {@link SelectorManager} instance.
*/
public SelectorManager getSelectorManager()
@ -111,8 +142,10 @@ public class WebSocketClientFactory extends AggregateLifeCycle
}
/* ------------------------------------------------------------ */
/** Get the ThreadPool.
/**
* Get the ThreadPool.
* Used to set/query the thread pool configuration.
*
* @return The {@link ThreadPool}
*/
public ThreadPool getThreadPool()
@ -123,7 +156,7 @@ public class WebSocketClientFactory extends AggregateLifeCycle
/* ------------------------------------------------------------ */
/**
* @return the shared mask generator, or null if no shared mask generator is used
* @see {@link WebSocketClient#getMaskGen()}
* @see WebSocketClient#getMaskGen()
*/
public MaskGen getMaskGen()
{
@ -133,15 +166,15 @@ public class WebSocketClientFactory extends AggregateLifeCycle
/* ------------------------------------------------------------ */
/**
* @param maskGen the shared mask generator, or null if no shared mask generator is used
* @see {@link WebSocketClient#setMaskGen(MaskGen)}
* @see WebSocketClient#setMaskGen(MaskGen)
*/
public void setMaskGen(MaskGen maskGen)
{
if (isRunning())
throw new IllegalStateException(getState());
if (removeBean(_maskGen))
addBean(maskGen);
_maskGen=maskGen;
removeBean(_maskGen);
_maskGen = maskGen;
addBean(maskGen);
}
/* ------------------------------------------------------------ */
@ -154,7 +187,7 @@ public class WebSocketClientFactory extends AggregateLifeCycle
if (isRunning())
throw new IllegalStateException(getState());
removeBean(_buffers);
_buffers=new WebSocketBuffers(bufferSize);
_buffers = new WebSocketBuffers(bufferSize);
addBean(_buffers);
}
@ -179,24 +212,28 @@ public class WebSocketClientFactory extends AggregateLifeCycle
return new WebSocketClient(this);
}
/* ------------------------------------------------------------ */
@Override
protected void doStart() throws Exception
protected SSLEngine newSslEngine(SocketChannel channel) throws IOException
{
super.doStart();
if (getThreadPool() instanceof LifeCycle && !((LifeCycle)getThreadPool()).isStarted())
((LifeCycle)getThreadPool()).start();
SSLEngine sslEngine;
if (channel != null)
{
String peerHost = channel.socket().getInetAddress().getHostAddress();
int peerPort = channel.socket().getPort();
sslEngine = _sslContextFactory.newSslEngine(peerHost, peerPort);
}
else
{
sslEngine = _sslContextFactory.newSslEngine();
}
sslEngine.setUseClientMode(true);
sslEngine.beginHandshake();
return sslEngine;
}
/* ------------------------------------------------------------ */
@Override
protected void doStop() throws Exception
{
super.doStop();
}
/* ------------------------------------------------------------ */
/** WebSocket Client Selector Manager
/**
* WebSocket Client Selector Manager
*/
class WebSocketClientSelector extends SelectorManager
{
@ -209,16 +246,33 @@ public class WebSocketClientFactory extends AggregateLifeCycle
@Override
protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, final SelectionKey key) throws IOException
{
SelectChannelEndPoint endp= new SelectChannelEndPoint(channel,selectSet,key,channel.socket().getSoTimeout());
endp.setConnection(selectSet.getManager().newConnection(channel,endp, key.attachment()));
return endp;
WebSocketClient.WebSocketFuture holder = (WebSocketClient.WebSocketFuture)key.attachment();
int maxIdleTime = holder.getMaxIdleTime();
if (maxIdleTime < 0)
maxIdleTime = (int)getMaxIdleTime();
SelectChannelEndPoint result = new SelectChannelEndPoint(channel, selectSet, key, maxIdleTime);
AsyncEndPoint endPoint = result;
// Detect if it is SSL, and wrap the connection if so
if ("wss".equals(holder.getURI().getScheme()))
{
SSLEngine sslEngine = newSslEngine(channel);
SslConnection sslConnection = new SslConnection(sslEngine, endPoint);
endPoint.setConnection(sslConnection);
endPoint = sslConnection.getSslEndPoint();
}
AsyncConnection connection = selectSet.getManager().newConnection(channel, endPoint, holder);
endPoint.setConnection(connection);
return result;
}
@Override
public AsyncConnection newConnection(SocketChannel channel, AsyncEndPoint endpoint, Object attachment)
{
WebSocketClient.WebSocketFuture holder = (WebSocketClient.WebSocketFuture) attachment;
return new HandshakeConnection(endpoint,holder);
WebSocketClient.WebSocketFuture holder = (WebSocketClient.WebSocketFuture)attachment;
return new HandshakeConnection(endpoint, holder);
}
@Override
@ -230,7 +284,7 @@ public class WebSocketClientFactory extends AggregateLifeCycle
@Override
protected void endPointUpgraded(ConnectedEndPoint endpoint, Connection oldConnection)
{
LOG.debug("upgrade {} -> {}",oldConnection,endpoint.getConnection());
LOG.debug("upgrade {} -> {}", oldConnection, endpoint.getConnection());
}
@Override
@ -243,7 +297,7 @@ public class WebSocketClientFactory extends AggregateLifeCycle
protected void connectionFailed(SocketChannel channel, Throwable ex, Object attachment)
{
if (!(attachment instanceof WebSocketClient.WebSocketFuture))
super.connectionFailed(channel,ex,attachment);
super.connectionFailed(channel, ex, attachment);
else
{
__log.debug(ex);
@ -254,9 +308,9 @@ public class WebSocketClientFactory extends AggregateLifeCycle
}
}
/* ------------------------------------------------------------ */
/** Handshake Connection.
/**
* Handshake Connection.
* Handles the connection until the handshake succeeds or fails.
*/
class HandshakeConnection extends AbstractConnection implements AsyncConnection
@ -267,29 +321,27 @@ public class WebSocketClientFactory extends AggregateLifeCycle
private final HttpParser _parser;
private String _accept;
private String _error;
private boolean _handshaken;
public HandshakeConnection(AsyncEndPoint endpoint, WebSocketClient.WebSocketFuture future)
{
super(endpoint,System.currentTimeMillis());
_endp=endpoint;
_future=future;
super(endpoint, System.currentTimeMillis());
_endp = endpoint;
_future = future;
byte[] bytes=new byte[16];
__random.nextBytes(bytes);
_key=new String(B64Code.encode(bytes));
byte[] bytes = new byte[16];
new Random().nextBytes(bytes);
_key = new String(B64Code.encode(bytes));
Buffers buffers = new SimpleBuffers(_buffers.getBuffer(),null);
_parser=new HttpParser(buffers,_endp,
new HttpParser.EventHandler()
Buffers buffers = new SimpleBuffers(_buffers.getBuffer(), null);
_parser = new HttpParser(buffers, _endp, new HttpParser.EventHandler()
{
@Override
public void startResponse(Buffer version, int status, Buffer reason) throws IOException
{
if (status!=101)
if (status != 101)
{
_error="Bad response status "+status+" "+reason;
_error = "Bad response status " + status + " " + reason;
_endp.close();
}
}
@ -298,65 +350,64 @@ public class WebSocketClientFactory extends AggregateLifeCycle
public void parsedHeader(Buffer name, Buffer value) throws IOException
{
if (__ACCEPT.equals(name))
_accept=value.toString();
_accept = value.toString();
}
@Override
public void startRequest(Buffer method, Buffer url, Buffer version) throws IOException
{
if (_error==null)
_error="Bad response: "+method+" "+url+" "+version;
if (_error == null)
_error = "Bad response: " + method + " " + url + " " + version;
_endp.close();
}
@Override
public void content(Buffer ref) throws IOException
{
if (_error==null)
_error="Bad response. "+ref.length()+"B of content?";
if (_error == null)
_error = "Bad response. " + ref.length() + "B of content?";
_endp.close();
}
});
}
String path=_future.getURI().getPath();
if (path==null || path.length()==0)
{
path="/";
}
if(_future.getURI().getRawQuery() != null)
{
private void handshake()
{
String path = _future.getURI().getPath();
if (path == null || path.length() == 0)
path = "/";
if (_future.getURI().getRawQuery() != null)
path += "?" + _future.getURI().getRawQuery();
}
String origin = future.getOrigin();
String origin = _future.getOrigin();
StringBuilder request = new StringBuilder(512);
request
.append("GET ").append(path).append(" HTTP/1.1\r\n")
.append("Host: ").append(future.getURI().getHost()).append(":").append(_future.getURI().getPort()).append("\r\n")
.append("Upgrade: websocket\r\n")
.append("Connection: Upgrade\r\n")
.append("Sec-WebSocket-Key: ")
.append(_key).append("\r\n");
if(origin!=null)
request.append("GET ").append(path).append(" HTTP/1.1\r\n")
.append("Host: ").append(_future.getURI().getHost()).append(":")
.append(_future.getURI().getPort()).append("\r\n")
.append("Upgrade: websocket\r\n")
.append("Connection: Upgrade\r\n")
.append("Sec-WebSocket-Key: ")
.append(_key).append("\r\n");
if (origin != null)
request.append("Origin: ").append(origin).append("\r\n");
request.append("Sec-WebSocket-Version: ").append(WebSocketConnectionD13.VERSION).append("\r\n");
if (future.getProtocol()!=null)
request.append("Sec-WebSocket-Protocol: ").append(future.getProtocol()).append("\r\n");
request.append("Sec-WebSocket-Version: ").append(WebSocketConnectionRFC6455.VERSION).append("\r\n");
if (future.getCookies()!=null && future.getCookies().size()>0)
if (_future.getProtocol() != null)
request.append("Sec-WebSocket-Protocol: ").append(_future.getProtocol()).append("\r\n");
Map<String, String> cookies = _future.getCookies();
if (cookies != null && cookies.size() > 0)
{
for (String cookie : future.getCookies().keySet())
request
.append("Cookie: ")
.append(QuotedStringTokenizer.quoteIfNeeded(cookie,HttpFields.__COOKIE_DELIM))
.append("=")
.append(QuotedStringTokenizer.quoteIfNeeded(future.getCookies().get(cookie),HttpFields.__COOKIE_DELIM))
.append("\r\n");
for (String cookie : cookies.keySet())
request.append("Cookie: ")
.append(QuotedStringTokenizer.quoteIfNeeded(cookie, HttpFields.__COOKIE_DELIM))
.append("=")
.append(QuotedStringTokenizer.quoteIfNeeded(cookies.get(cookie), HttpFields.__COOKIE_DELIM))
.append("\r\n");
}
request.append("\r\n");
@ -365,14 +416,18 @@ public class WebSocketClientFactory extends AggregateLifeCycle
try
{
Buffer handshake = new ByteArrayBuffer(request.toString(),false);
int len=handshake.length();
if (len!=_endp.flush(handshake))
Buffer handshake = new ByteArrayBuffer(request.toString(), false);
int len = handshake.length();
if (len != _endp.flush(handshake))
throw new IOException("incomplete");
}
catch(IOException e)
catch (IOException e)
{
future.handshakeFailed(e);
_future.handshakeFailed(e);
}
finally
{
_handshaken = true;
}
}
@ -380,6 +435,9 @@ public class WebSocketClientFactory extends AggregateLifeCycle
{
while (_endp.isOpen() && !_parser.isComplete())
{
if (!_handshaken)
handshake();
if (!_parser.parseAvailable())
{
if (_endp.isInputShutdown())
@ -387,25 +445,29 @@ public class WebSocketClientFactory extends AggregateLifeCycle
return this;
}
}
if (_error==null)
if (_error == null)
{
if (_accept==null)
_error="No Sec-WebSocket-Accept";
else if (!WebSocketConnectionD13.hashKey(_key).equals(_accept))
_error="Bad Sec-WebSocket-Accept";
if (_accept == null)
{
_error = "No Sec-WebSocket-Accept";
}
else if (!WebSocketConnectionRFC6455.hashKey(_key).equals(_accept))
{
_error = "Bad Sec-WebSocket-Accept";
}
else
{
Buffer header=_parser.getHeaderBuffer();
MaskGen maskGen=_future.getMaskGen();
WebSocketConnectionD13 connection =
new WebSocketConnectionD13(_future.getWebSocket(),
_endp,
_buffers,System.currentTimeMillis(),
_future.getMaxIdleTime(),
_future.getProtocol(),
null,
WebSocketConnectionD13.VERSION,
maskGen);
Buffer header = _parser.getHeaderBuffer();
MaskGen maskGen = _future.getMaskGen();
WebSocketConnectionRFC6455 connection =
new WebSocketConnectionRFC6455(_future.getWebSocket(),
_endp,
_buffers, System.currentTimeMillis(),
_future.getMaxIdleTime(),
_future.getProtocol(),
null,
WebSocketConnectionRFC6455.VERSION,
maskGen);
if (header.hasContent())
connection.fillBuffersFrom(header);
@ -438,15 +500,10 @@ public class WebSocketClientFactory extends AggregateLifeCycle
public void onClose()
{
if (_error!=null)
if (_error != null)
_future.handshakeFailed(new ProtocolException(_error));
else
_future.handshakeFailed(new EOFException());
}
public String toString()
{
return "HS"+super.toString();
}
}
}

View File

@ -1,12 +1,23 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.nio.AsyncConnection;
@ -14,8 +25,6 @@ public interface WebSocketConnection extends AsyncConnection
{
void fillBuffersFrom(Buffer buffer);
void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException;
List<Extension> getExtensions();
WebSocket.Connection getConnection();

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
@ -19,10 +34,6 @@ import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.io.Buffer;
@ -30,7 +41,6 @@ import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
import org.eclipse.jetty.util.QuotedStringTokenizer;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -43,13 +53,13 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc
public final static byte LENGTH_FRAME=(byte)0x80;
public final static byte SENTINEL_FRAME=(byte)0x00;
final WebSocketParser _parser;
final WebSocketGenerator _generator;
final WebSocket _websocket;
final String _protocol;
String _key1;
String _key2;
ByteArrayBuffer _hixieBytes;
private final WebSocketParser _parser;
private final WebSocketGenerator _generator;
private final WebSocket _websocket;
private final String _protocol;
private String _key1;
private String _key2;
private ByteArrayBuffer _hixieBytes;
public WebSocketConnectionD00(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol)
throws IOException
@ -140,11 +150,10 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc
progress = flushed>0 || filled>0;
if (filled<0 || flushed<0)
{
_endp.close();
break;
}
_endp.flush();
if (_endp instanceof AsyncEndPoint && ((AsyncEndPoint)_endp).hasProgressed())
progress=true;
}
}
catch(IOException e)
@ -152,7 +161,8 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc
LOG.debug(e);
try
{
_endp.close();
if (_endp.isOpen())
_endp.close();
}
catch(IOException e2)
{
@ -340,54 +350,6 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc
}
}
public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException
{
String uri=request.getRequestURI();
String query=request.getQueryString();
if (query!=null && query.length()>0)
uri+="?"+query;
uri=new HttpURI(uri).toString();
String host=request.getHeader("Host");
String origin=request.getHeader("Sec-WebSocket-Origin");
if (origin==null)
origin=request.getHeader("Origin");
if (origin!=null)
origin= QuotedStringTokenizer.quoteIfNeeded(origin, "\r\n");
String key1 = request.getHeader("Sec-WebSocket-Key1");
if (key1!=null)
{
String key2 = request.getHeader("Sec-WebSocket-Key2");
setHixieKeys(key1,key2);
response.setHeader("Upgrade","WebSocket");
response.addHeader("Connection","Upgrade");
if (origin!=null)
response.addHeader("Sec-WebSocket-Origin",origin);
response.addHeader("Sec-WebSocket-Location",(request.isSecure()?"wss://":"ws://")+host+uri);
if (subprotocol!=null)
response.addHeader("Sec-WebSocket-Protocol",subprotocol);
response.sendError(101,"WebSocket Protocol Handshake");
}
else
{
response.setHeader("Upgrade","WebSocket");
response.addHeader("Connection","Upgrade");
response.addHeader("WebSocket-Origin",origin);
response.addHeader("WebSocket-Location",(request.isSecure()?"wss://":"ws://")+host+uri);
if (subprotocol!=null)
response.addHeader("WebSocket-Protocol",subprotocol);
response.sendError(101,"Web Socket Protocol Handshake");
response.flushBuffer();
if (_websocket instanceof OnFrame)
((OnFrame)_websocket).onHandshake(this);
_websocket.onOpen(this);
}
}
public void setMaxTextMessageSize(int size)
{
}
@ -427,6 +389,19 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc
{
return _protocol;
}
protected void onFrameHandshake()
{
if (_websocket instanceof OnFrame)
{
((OnFrame)_websocket).onHandshake(this);
}
}
protected void onWebsocketOpen()
{
_websocket.onOpen(this);
}
static class FrameHandlerD00 implements WebSocketParser.FrameHandler
{

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
@ -19,9 +34,6 @@ import java.security.MessageDigest;
import java.util.Collections;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.io.Buffer;
@ -73,7 +85,6 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc
}
}
private final static byte[] MAGIC;
private final WebSocketParser _parser;
private final WebSocketGenerator _generator;
@ -101,10 +112,6 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc
}
private final WebSocketParser.FrameHandler _frameHandler= new FrameHandlerD06();
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
private final WebSocket.FrameConnection _connection = new FrameConnectionD06();
@ -199,7 +206,7 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc
/* ------------------------------------------------------------ */
@Override
public void onIdleExpired()
public void onIdleExpired(long idleForMs)
{
closeOut(WebSocketConnectionD06.CLOSE_NORMAL,"Idle");
}
@ -287,9 +294,20 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc
{
return Collections.emptyList();
}
protected void onFrameHandshake()
{
if (_onFrame!=null)
{
_onFrame.onHandshake(_connection);
}
}
protected void onWebSocketOpen()
{
_webSocket.onOpen(_connection);
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
private class FrameConnectionD06 implements WebSocket.FrameConnection
{
@ -487,6 +505,7 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc
}
/* ------------------------------------------------------------ */
@Override
public String toString()
{
return this.getClass().getSimpleName()+"@"+_endp.getLocalAddr()+":"+_endp.getLocalPort()+"<->"+_endp.getRemoteAddr()+":"+_endp.getRemotePort();
@ -693,29 +712,13 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc
_connection.close(code,message);
}
@Override
public String toString()
{
return WebSocketConnectionD06.this.toString()+"FH";
}
}
/* ------------------------------------------------------------ */
public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException
{
String key = request.getHeader("Sec-WebSocket-Key");
response.setHeader("Upgrade","WebSocket");
response.addHeader("Connection","Upgrade");
response.addHeader("Sec-WebSocket-Accept",hashKey(key));
if (subprotocol!=null)
response.addHeader("Sec-WebSocket-Protocol",subprotocol);
response.sendError(101);
if (_onFrame!=null)
_onFrame.onHandshake(_connection);
_webSocket.onOpen(_connection);
}
/* ------------------------------------------------------------ */
public static String hashKey(String key)
{

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
@ -19,16 +34,12 @@ import java.security.MessageDigest;
import java.util.Collections;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
import org.eclipse.jetty.util.B64Code;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Utf8StringBuilder;
@ -110,7 +121,6 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc
}
private final WebSocketParser.FrameHandler _frameHandler= new WSFrameHandler();
private final WebSocket.FrameConnection _connection = new WSFrameConnection();
@ -246,10 +256,9 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc
/* ------------------------------------------------------------ */
@Override
public void onIdleExpired()
public void onIdleExpired(long idleForMs)
{
long idle = System.currentTimeMillis()-((SelectChannelEndPoint)_endp).getIdleTimestamp();
closeOut(WebSocketConnectionD08.CLOSE_NORMAL,"Idle for "+idle+"ms > "+_endp.getMaxIdleTime()+"ms");
closeOut(WebSocketConnectionD08.CLOSE_NORMAL,"Idle for "+idleForMs+"ms > "+_endp.getMaxIdleTime()+"ms");
}
/* ------------------------------------------------------------ */
@ -376,8 +385,19 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc
}
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
protected void onFrameHandshake()
{
if (_onFrame != null)
{
_onFrame.onHandshake(_connection);
}
}
protected void onWebSocketOpen()
{
_webSocket.onOpen(_connection);
}
/* ------------------------------------------------------------ */
private class WSFrameConnection implements WebSocket.FrameConnection
{
@ -800,27 +820,6 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc
}
}
/* ------------------------------------------------------------ */
public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException
{
String key = request.getHeader("Sec-WebSocket-Key");
response.setHeader("Upgrade", "WebSocket");
response.addHeader("Connection","Upgrade");
response.addHeader("Sec-WebSocket-Accept",hashKey(key));
if (subprotocol!=null)
response.addHeader("Sec-WebSocket-Protocol",subprotocol);
for(Extension ext : _extensions)
response.addHeader("Sec-WebSocket-Extensions",ext.getParameterizedName());
response.sendError(101);
if (_onFrame!=null)
_onFrame.onHandshake(_connection);
_webSocket.onOpen(_connection);
}
/* ------------------------------------------------------------ */
public static String hashKey(String key)
{
@ -841,6 +840,6 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc
@Override
public String toString()
{
return "WS/D"+_draft+"-"+_endp;
return String.format("WS/D%d p=%s g=%s", _draft, _parser, _generator);
}
}

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
@ -19,16 +34,12 @@ import java.security.MessageDigest;
import java.util.Collections;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
import org.eclipse.jetty.util.B64Code;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Utf8Appendable;
@ -64,9 +75,9 @@ import org.eclipse.jetty.websocket.WebSocket.OnTextMessage;
* +---------------------------------------------------------------+
* </pre>
*/
public class WebSocketConnectionD13 extends AbstractConnection implements WebSocketConnection
public class WebSocketConnectionRFC6455 extends AbstractConnection implements WebSocketConnection
{
private static final Logger LOG = Log.getLogger(WebSocketConnectionD13.class);
private static final Logger LOG = Log.getLogger(WebSocketConnectionRFC6455.class);
final static byte OP_CONTINUATION = 0x00;
final static byte OP_TEXT = 0x01;
@ -90,9 +101,12 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
final static int CLOSE_POLICY_VIOLATION=1008;
final static int CLOSE_MESSAGE_TOO_LARGE=1009;
final static int CLOSE_REQUIRED_EXTENSION=1010;
final static int CLOSE_SERVER_ERROR=1011;
final static int CLOSE_FAILED_TLS_HANDSHAKE=1015;
final static int FLAG_FIN=0x8;
// Per RFC 6455, section 1.3 - Opening Handshake - this version is "13"
final static int VERSION=13;
static boolean isLastFrame(byte flags)
@ -108,7 +122,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
private final static byte[] MAGIC;
private final List<Extension> _extensions;
private final WebSocketParserD13 _parser;
private final WebSocketGeneratorD13 _generator;
private final WebSocketGeneratorRFC6455 _generator;
private final WebSocketGenerator _outbound;
private final WebSocket _webSocket;
private final OnFrame _onFrame;
@ -141,14 +155,14 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
/* ------------------------------------------------------------ */
public WebSocketConnectionD13(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol, List<Extension> extensions,int draft)
public WebSocketConnectionRFC6455(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol, List<Extension> extensions,int draft)
throws IOException
{
this(websocket,endpoint,buffers,timestamp,maxIdleTime,protocol,extensions,draft,null);
}
/* ------------------------------------------------------------ */
public WebSocketConnectionD13(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol, List<Extension> extensions,int draft, MaskGen maskgen)
public WebSocketConnectionRFC6455(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol, List<Extension> extensions,int draft, MaskGen maskgen)
throws IOException
{
super(endpoint,timestamp);
@ -163,7 +177,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
_onTextMessage=_webSocket instanceof OnTextMessage ? (OnTextMessage)_webSocket : null;
_onBinaryMessage=_webSocket instanceof OnBinaryMessage ? (OnBinaryMessage)_webSocket : null;
_onControl=_webSocket instanceof OnControl ? (OnControl)_webSocket : null;
_generator = new WebSocketGeneratorD13(buffers, _endp,maskgen);
_generator = new WebSocketGeneratorRFC6455(buffers, _endp,maskgen);
_extensions=extensions;
WebSocketParser.FrameHandler frameHandler = new WSFrameHandler();
@ -221,7 +235,6 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
int filled=_parser.parseNext();
progress = flushed>0 || filled>0;
_endp.flush();
if (_endp instanceof AsyncEndPoint && ((AsyncEndPoint)_endp).hasProgressed())
@ -274,10 +287,9 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
/* ------------------------------------------------------------ */
@Override
public void onIdleExpired()
public void onIdleExpired(long idleForMs)
{
long idle = System.currentTimeMillis()-((SelectChannelEndPoint)_endp).getIdleTimestamp();
closeOut(WebSocketConnectionD13.CLOSE_NORMAL,"Idle for "+idle+"ms > "+_endp.getMaxIdleTime()+"ms");
closeOut(WebSocketConnectionRFC6455.CLOSE_NORMAL,"Idle for "+idleForMs+"ms > "+_endp.getMaxIdleTime()+"ms");
}
/* ------------------------------------------------------------ */
@ -294,10 +306,10 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
{
closed=_closeCode==0;
if (closed)
_closeCode=WebSocketConnectionD13.CLOSE_NO_CLOSE;
_closeCode=WebSocketConnectionRFC6455.CLOSE_NO_CLOSE;
}
if (closed)
_webSocket.onClose(WebSocketConnectionD13.CLOSE_NO_CLOSE,"closed");
_webSocket.onClose(WebSocketConnectionRFC6455.CLOSE_NO_CLOSE,"closed");
}
/* ------------------------------------------------------------ */
@ -361,18 +373,23 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
{
if (!closed_out)
{
// Close code 1005/1006 are never to be sent as a status over
// Close code 1005/1006/1015 are never to be sent as a status over
// a Close control frame. Code<-1 also means no node.
if (code<0 || (code == WebSocketConnectionD13.CLOSE_NO_CODE) || code==WebSocketConnectionD13.CLOSE_NO_CLOSE)
code=-1;
else if (code==0)
code=WebSocketConnectionD13.CLOSE_NORMAL;
if (code < 0 || (code == WebSocketConnectionRFC6455.CLOSE_NO_CODE) || (code == WebSocketConnectionRFC6455.CLOSE_NO_CLOSE)
|| (code == WebSocketConnectionRFC6455.CLOSE_FAILED_TLS_HANDSHAKE))
{
code = -1;
}
else if (code == 0)
{
code = WebSocketConnectionRFC6455.CLOSE_NORMAL;
}
byte[] bytes = ("xx"+(message==null?"":message)).getBytes(StringUtil.__ISO_8859_1);
bytes[0]=(byte)(code/0x100);
bytes[1]=(byte)(code%0x100);
_outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD13.OP_CLOSE,bytes,0,code>0?bytes.length:0);
_outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionRFC6455.OP_CLOSE,bytes,0,code>0?bytes.length:0);
_outbound.flush();
}
}
@ -398,8 +415,19 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
}
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
protected void onFrameHandshake()
{
if (_onFrame != null)
{
_onFrame.onHandshake(_connection);
}
}
protected void onWebSocketOpen()
{
_webSocket.onOpen(_connection);
}
/* ------------------------------------------------------------ */
private class WSFrameConnection implements WebSocket.FrameConnection
{
@ -411,7 +439,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
if (_closedOut)
throw new IOException("closedOut "+_closeCode+":"+_closeMessage);
byte[] data = content.getBytes(StringUtil.__UTF8);
_outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD13.OP_TEXT,data,0,data.length);
_outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionRFC6455.OP_TEXT,data,0,data.length);
checkWriteable();
}
@ -420,7 +448,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
{
if (_closedOut)
throw new IOException("closedOut "+_closeCode+":"+_closeMessage);
_outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD13.OP_BINARY,content,offset,length);
_outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionRFC6455.OP_BINARY,content,offset,length);
checkWriteable();
}
@ -461,7 +489,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
if (_disconnecting)
return;
_disconnecting=true;
WebSocketConnectionD13.this.closeOut(code,message);
WebSocketConnectionRFC6455.this.closeOut(code,message);
}
/* ------------------------------------------------------------ */
@ -625,7 +653,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
{
boolean lastFrame = isLastFrame(flags);
synchronized(WebSocketConnectionD13.this)
synchronized(WebSocketConnectionRFC6455.this)
{
// Ignore incoming after a close
if (_closedIn)
@ -637,14 +665,14 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
if (isControlFrame(opcode) && buffer.length()>MAX_CONTROL_FRAME_PAYLOAD)
{
errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Control frame too large: " + buffer.length() + " > " + MAX_CONTROL_FRAME_PAYLOAD);
errorClose(WebSocketConnectionRFC6455.CLOSE_PROTOCOL,"Control frame too large: " + buffer.length() + " > " + MAX_CONTROL_FRAME_PAYLOAD);
return;
}
// TODO: check extensions for RSV bit(s) meanings
if ((flags&0x7)!=0)
{
errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"RSV bits set 0x"+Integer.toHexString(flags));
errorClose(WebSocketConnectionRFC6455.CLOSE_PROTOCOL,"RSV bits set 0x"+Integer.toHexString(flags));
return;
}
@ -669,16 +697,16 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
switch(opcode)
{
case WebSocketConnectionD13.OP_CONTINUATION:
case WebSocketConnectionRFC6455.OP_CONTINUATION:
{
if (_opcode==-1)
{
errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Bad Continuation");
errorClose(WebSocketConnectionRFC6455.CLOSE_PROTOCOL,"Bad Continuation");
return;
}
// If text, append to the message buffer
if (_onTextMessage!=null && _opcode==WebSocketConnectionD13.OP_TEXT)
if (_onTextMessage!=null && _opcode==WebSocketConnectionRFC6455.OP_TEXT)
{
if (_utf8.append(buffer.array(),buffer.getIndex(),buffer.length(),_connection.getMaxTextMessageSize()))
{
@ -718,39 +746,39 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
}
break;
}
case WebSocketConnectionD13.OP_PING:
case WebSocketConnectionRFC6455.OP_PING:
{
LOG.debug("PING {}",this);
if (!_closedOut)
{
_connection.sendControl(WebSocketConnectionD13.OP_PONG,buffer.array(),buffer.getIndex(),buffer.length());
_connection.sendControl(WebSocketConnectionRFC6455.OP_PONG,buffer.array(),buffer.getIndex(),buffer.length());
}
break;
}
case WebSocketConnectionD13.OP_PONG:
case WebSocketConnectionRFC6455.OP_PONG:
{
LOG.debug("PONG {}",this);
break;
}
case WebSocketConnectionD13.OP_CLOSE:
case WebSocketConnectionRFC6455.OP_CLOSE:
{
int code=WebSocketConnectionD13.CLOSE_NO_CODE;
int code=WebSocketConnectionRFC6455.CLOSE_NO_CODE;
String message=null;
if (buffer.length()>=2)
{
code=(0xff&buffer.array()[buffer.getIndex()])*0x100+(0xff&buffer.array()[buffer.getIndex()+1]);
// Validate close status codes.
if (code < WebSocketConnectionD13.CLOSE_NORMAL ||
code == WebSocketConnectionD13.CLOSE_UNDEFINED ||
code == WebSocketConnectionD13.CLOSE_NO_CLOSE ||
code == WebSocketConnectionD13.CLOSE_NO_CODE ||
( code > 1010 && code <= 2999 ) ||
if (code < WebSocketConnectionRFC6455.CLOSE_NORMAL ||
code == WebSocketConnectionRFC6455.CLOSE_UNDEFINED ||
code == WebSocketConnectionRFC6455.CLOSE_NO_CLOSE ||
code == WebSocketConnectionRFC6455.CLOSE_NO_CODE ||
( code > 1011 && code <= 2999 ) ||
code >= 5000 )
{
errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Invalid close code " + code);
errorClose(WebSocketConnectionRFC6455.CLOSE_PROTOCOL,"Invalid close code " + code);
return;
}
@ -766,18 +794,18 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
else if(buffer.length() == 1)
{
// Invalid length. use status code 1002 (Protocol error)
errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Invalid payload length of 1");
errorClose(WebSocketConnectionRFC6455.CLOSE_PROTOCOL,"Invalid payload length of 1");
return;
}
closeIn(code,message);
break;
}
case WebSocketConnectionD13.OP_TEXT:
case WebSocketConnectionRFC6455.OP_TEXT:
{
if (_opcode!=-1)
{
errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Expected Continuation"+Integer.toHexString(opcode));
errorClose(WebSocketConnectionRFC6455.CLOSE_PROTOCOL,"Expected Continuation"+Integer.toHexString(opcode));
return;
}
@ -791,7 +819,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
else
{
LOG.warn("Frame discarded. Text aggregation disabled for {}",_endp);
errorClose(WebSocketConnectionD13.CLOSE_POLICY_VIOLATION,"Text frame aggregation disabled");
errorClose(WebSocketConnectionRFC6455.CLOSE_POLICY_VIOLATION,"Text frame aggregation disabled");
}
}
// append bytes to message buffer (if they fit)
@ -805,7 +833,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
}
else
{
_opcode=WebSocketConnectionD13.OP_TEXT;
_opcode=WebSocketConnectionRFC6455.OP_TEXT;
}
}
else
@ -814,11 +842,11 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
break;
}
case WebSocketConnectionD13.OP_BINARY:
case WebSocketConnectionRFC6455.OP_BINARY:
{
if (_opcode!=-1)
{
errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Expected Continuation"+Integer.toHexString(opcode));
errorClose(WebSocketConnectionRFC6455.CLOSE_PROTOCOL,"Expected Continuation"+Integer.toHexString(opcode));
return;
}
@ -839,28 +867,28 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
else
{
LOG.warn("Frame discarded. Binary aggregation disabed for {}",_endp);
errorClose(WebSocketConnectionD13.CLOSE_POLICY_VIOLATION,"Binary frame aggregation disabled");
errorClose(WebSocketConnectionRFC6455.CLOSE_POLICY_VIOLATION,"Binary frame aggregation disabled");
}
}
break;
}
default:
errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Bad opcode 0x"+Integer.toHexString(opcode));
errorClose(WebSocketConnectionRFC6455.CLOSE_PROTOCOL,"Bad opcode 0x"+Integer.toHexString(opcode));
break;
}
}
catch(Utf8Appendable.NotUtf8Exception notUtf8)
{
LOG.warn("{} for {}",notUtf8,_endp);
LOG.warn("NOTUTF8 - {} for {}",notUtf8,_endp, notUtf8);
LOG.debug(notUtf8);
errorClose(WebSocketConnectionD13.CLOSE_BAD_PAYLOAD,"Invalid UTF-8");
errorClose(WebSocketConnectionRFC6455.CLOSE_BAD_PAYLOAD,"Invalid UTF-8");
}
catch(Throwable probablyNotUtf8)
catch(Throwable e)
{
LOG.warn("{} for {}",probablyNotUtf8,_endp);
LOG.debug(probablyNotUtf8);
errorClose(WebSocketConnectionD13.CLOSE_BAD_PAYLOAD,"Invalid Payload: "+probablyNotUtf8);
LOG.warn("{} for {}",e,_endp, e);
LOG.debug(e);
errorClose(WebSocketConnectionRFC6455.CLOSE_SERVER_ERROR,"Internal Server Error: "+e);
}
}
@ -886,7 +914,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
if (max>0 && (bufferLen+length)>max)
{
LOG.warn("Binary message too large > {}B for {}",_connection.getMaxBinaryMessageSize(),_endp);
_connection.close(WebSocketConnectionD13.CLOSE_MESSAGE_TOO_LARGE,"Message size > "+_connection.getMaxBinaryMessageSize());
_connection.close(WebSocketConnectionRFC6455.CLOSE_MESSAGE_TOO_LARGE,"Message size > "+_connection.getMaxBinaryMessageSize());
_opcode=-1;
if (_aggregate!=null)
_aggregate.clear();
@ -898,7 +926,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
private void textMessageTooLarge()
{
LOG.warn("Text message too large > {} chars for {}",_connection.getMaxTextMessageSize(),_endp);
_connection.close(WebSocketConnectionD13.CLOSE_MESSAGE_TOO_LARGE,"Text message size > "+_connection.getMaxTextMessageSize()+" chars");
_connection.close(WebSocketConnectionRFC6455.CLOSE_MESSAGE_TOO_LARGE,"Text message size > "+_connection.getMaxTextMessageSize()+" chars");
_opcode=-1;
_utf8.reset();
@ -914,30 +942,9 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
@Override
public String toString()
{
return WebSocketConnectionD13.this.toString()+"FH";
return WebSocketConnectionRFC6455.this.toString()+"FH";
}
}
/* ------------------------------------------------------------ */
public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException
{
String key = request.getHeader("Sec-WebSocket-Key");
response.setHeader("Upgrade","WebSocket");
response.addHeader("Connection","Upgrade");
response.addHeader("Sec-WebSocket-Accept",hashKey(key));
if (subprotocol!=null)
response.addHeader("Sec-WebSocket-Protocol",subprotocol);
for(Extension ext : _extensions)
response.addHeader("Sec-WebSocket-Extensions",ext.getParameterizedName());
response.sendError(101);
if (_onFrame!=null)
_onFrame.onHandshake(_connection);
_webSocket.onOpen(_connection);
}
/* ------------------------------------------------------------ */
public static String hashKey(String key)
@ -959,6 +966,6 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc
@Override
public String toString()
{
return "WS/D"+_draft+"-"+_endp;
return String.format("WS/D%d p=%s g=%s", _draft, _parser, _generator);
}
}

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
@ -193,53 +208,60 @@ public class WebSocketFactory
throw new IllegalStateException("!HTTP/1.1");
int draft = request.getIntHeader("Sec-WebSocket-Version");
if (draft < 0)
if (draft < 0) {
// Old pre-RFC version specifications (header not present in RFC-6455)
draft = request.getIntHeader("Sec-WebSocket-Draft");
}
AbstractHttpConnection http = AbstractHttpConnection.getCurrentConnection();
if (http instanceof BlockingHttpConnection)
throw new IllegalStateException("Websockets not supported on blocking connectors");
ConnectedEndPoint endp = (ConnectedEndPoint)http.getEndPoint();
List<String> extensions_requested = new ArrayList<String>();
for (Enumeration e=request.getHeaders("Sec-WebSocket-Extensions");e.hasMoreElements();)
@SuppressWarnings("unchecked")
Enumeration<String> e = request.getHeaders("Sec-WebSocket-Extensions");
while (e.hasMoreElements())
{
QuotedStringTokenizer tok = new QuotedStringTokenizer((String)e.nextElement(),",");
QuotedStringTokenizer tok = new QuotedStringTokenizer(e.nextElement(),",");
while (tok.hasMoreTokens())
{
extensions_requested.add(tok.nextToken());
}
}
final WebSocketConnection connection;
final WebSocketServletConnection connection;
final List<Extension> extensions;
switch (draft)
{
case -1:
case 0:
case -1: // unspecified draft/version
case 0: // Old school draft/version
extensions=Collections.emptyList();
connection = new WebSocketConnectionD00(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol);
connection = new WebSocketServletConnectionD00(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol);
break;
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 6:
extensions=Collections.emptyList();
connection = new WebSocketConnectionD06(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol);
connection = new WebSocketServletConnectionD06(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol);
break;
case 7:
case 8:
extensions= initExtensions(extensions_requested,8-WebSocketConnectionD08.OP_EXT_DATA, 16-WebSocketConnectionD08.OP_EXT_CTRL,3);
connection = new WebSocketConnectionD08(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol,extensions,draft);
connection = new WebSocketServletConnectionD08(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol,extensions,draft);
break;
case 13:
extensions= initExtensions(extensions_requested,8-WebSocketConnectionD13.OP_EXT_DATA, 16-WebSocketConnectionD13.OP_EXT_CTRL,3);
connection = new WebSocketConnectionD13(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol,extensions,draft);
case WebSocketConnectionRFC6455.VERSION: // RFC 6455 Version
extensions= initExtensions(extensions_requested,8-WebSocketConnectionRFC6455.OP_EXT_DATA, 16-WebSocketConnectionRFC6455.OP_EXT_CTRL,3);
connection = new WebSocketServletConnectionRFC6455(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol,extensions,draft);
break;
default:
LOG.warn("Unsupported Websocket version: "+draft);
response.setHeader("Sec-WebSocket-Version","0,6,8,13");
throw new HttpException(400, "Unsupported draft specification: " + draft);
// Per RFC 6455 - 4.4 - Supporting Multiple Versions of WebSocket Protocol
// Using the examples as outlined
response.setHeader("Sec-WebSocket-Version","13, 8, 6, 0");
throw new HttpException(400, "Unsupported websocket version specification: " + draft);
}
// Set the defaults
@ -292,7 +314,8 @@ public class WebSocketFactory
// Try each requested protocol
WebSocket websocket = null;
@SuppressWarnings("unchecked")
Enumeration<String> protocols = request.getHeaders("Sec-WebSocket-Protocol");
String protocol=null;
while (protocol==null && protocols!=null && protocols.hasMoreElements())
@ -314,7 +337,7 @@ public class WebSocketFactory
{
// Try with no protocol
websocket = _acceptor.doWebSocketConnect(request, null);
if (websocket==null)
{
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
@ -27,7 +42,7 @@ import org.eclipse.jetty.io.EofException;
* threads will call the addMessage methods while other
* threads are flushing the generator.
*/
public class WebSocketGeneratorD13 implements WebSocketGenerator
public class WebSocketGeneratorRFC6455 implements WebSocketGenerator
{
final private WebSocketBuffers _buffers;
final private EndPoint _endp;
@ -38,14 +53,14 @@ public class WebSocketGeneratorD13 implements WebSocketGenerator
private final MaskGen _maskGen;
private boolean _closed;
public WebSocketGeneratorD13(WebSocketBuffers buffers, EndPoint endp)
public WebSocketGeneratorRFC6455(WebSocketBuffers buffers, EndPoint endp)
{
_buffers=buffers;
_endp=endp;
_maskGen=null;
}
public WebSocketGeneratorD13(WebSocketBuffers buffers, EndPoint endp, MaskGen maskGen)
public WebSocketGeneratorRFC6455(WebSocketBuffers buffers, EndPoint endp, MaskGen maskGen)
{
_buffers=buffers;
_endp=endp;
@ -63,21 +78,21 @@ public class WebSocketGeneratorD13 implements WebSocketGenerator
if (_closed)
throw new EofException("Closed");
if (opcode==WebSocketConnectionD13.OP_CLOSE)
if (opcode==WebSocketConnectionRFC6455.OP_CLOSE)
_closed=true;
boolean mask=_maskGen!=null;
if (_buffer==null)
_buffer=mask?_buffers.getBuffer():_buffers.getDirectBuffer();
boolean last=WebSocketConnectionD13.isLastFrame(flags);
boolean last=WebSocketConnectionRFC6455.isLastFrame(flags);
int space=mask?14:10;
do
{
opcode = _opsent?WebSocketConnectionD13.OP_CONTINUATION:opcode;
opcode = _opsent?WebSocketConnectionRFC6455.OP_CONTINUATION:opcode;
opcode=(byte)(((0xf&flags)<<4)+(0xf&opcode));
_opsent=true;
@ -240,4 +255,17 @@ public class WebSocketGeneratorD13 implements WebSocketGenerator
}
}
@Override
public String toString()
{
// Do NOT use synchronized (this)
// because it's very easy to deadlock when debugging is enabled.
// We do a best effort to print the right toString() and that's it.
Buffer buffer = _buffer;
return String.format("%s@%x closed=%b buffer=%d",
getClass().getSimpleName(),
hashCode(),
_closed,
buffer == null ? -1 : buffer.length());
}
}

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
@ -18,6 +33,7 @@ import java.io.IOException;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.Buffers;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -86,7 +102,7 @@ public class WebSocketParserD00 implements WebSocketParser
if (_buffer==null)
_buffer=_buffers.getBuffer();
int total_filled=0;
int progress=0;
// Loop until an datagram call back or can't fill anymore
while(true)
@ -108,14 +124,14 @@ public class WebSocketParserD00 implements WebSocketParser
{
int filled=_endp.isOpen()?_endp.fill(_buffer):-1;
if (filled<=0)
return total_filled;
total_filled+=filled;
return progress;
progress+=filled;
length=_buffer.length();
}
catch(IOException e)
{
LOG.debug(e);
return total_filled>0?total_filled:-1;
return progress>0?progress:-1;
}
}
@ -147,6 +163,7 @@ public class WebSocketParserD00 implements WebSocketParser
{
_state=STATE_START;
int l=_buffer.getIndex()-_buffer.markIndex()-1;
progress++;
_handler.onFrame((byte)0,_opcode,_buffer.sliceFromMark(l));
_buffer.setMarkIndex(-1);
if (_buffer.length()==0)
@ -154,7 +171,7 @@ public class WebSocketParserD00 implements WebSocketParser
_buffers.returnBuffer(_buffer);
_buffer=null;
}
return total_filled;
return progress;
}
continue;
@ -175,6 +192,7 @@ public class WebSocketParserD00 implements WebSocketParser
Buffer data=_buffer.sliceFromMark(_length);
_buffer.skip(_length);
_state=STATE_START;
progress++;
_handler.onFrame((byte)0, _opcode, data);
if (_buffer.length()==0)
@ -183,7 +201,7 @@ public class WebSocketParserD00 implements WebSocketParser
_buffer=null;
}
return total_filled;
return progress;
}
}
}

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
@ -125,7 +140,7 @@ public class WebSocketParserD13 implements WebSocketParser
{
if (_buffer==null)
_buffer=_buffers.getBuffer();
boolean progress=false;
int filled=-1;
@ -161,9 +176,9 @@ public class WebSocketParserD13 implements WebSocketParser
// System.err.printf("%s %s %s >>\n",TypeUtil.toHexString(_flags),TypeUtil.toHexString(_opcode),data.length());
_bytesNeeded-=data.length();
progress=true;
_handler.onFrame((byte)(_flags&(0xff^WebSocketConnectionD13.FLAG_FIN)), _opcode, data);
_handler.onFrame((byte)(_flags&(0xff^WebSocketConnectionRFC6455.FLAG_FIN)), _opcode, data);
_opcode=WebSocketConnectionD13.OP_CONTINUATION;
_opcode=WebSocketConnectionRFC6455.OP_CONTINUATION;
}
if (_buffer.space() == 0)
@ -189,7 +204,7 @@ public class WebSocketParserD13 implements WebSocketParser
// Did we get enough?
if (available<(_state==State.SKIP?1:_bytesNeeded))
break;
// if we are here, then we have sufficient bytes to process the current state.
// Parse the buffer byte by byte (unless it is STATE_DATA)
byte b;
@ -199,7 +214,7 @@ public class WebSocketParserD13 implements WebSocketParser
{
case START:
_skip=false;
_state=_opcode==WebSocketConnectionD13.OP_CLOSE?State.SEEK_EOF:State.OPCODE;
_state=_opcode==WebSocketConnectionRFC6455.OP_CLOSE?State.SEEK_EOF:State.OPCODE;
_bytesNeeded=_state.getNeeds();
continue;
@ -209,10 +224,10 @@ public class WebSocketParserD13 implements WebSocketParser
_opcode=(byte)(b&0xf);
_flags=(byte)(0xf&(b>>4));
if (WebSocketConnectionD13.isControlFrame(_opcode)&&!WebSocketConnectionD13.isLastFrame(_flags))
if (WebSocketConnectionRFC6455.isControlFrame(_opcode)&&!WebSocketConnectionRFC6455.isLastFrame(_flags))
{
LOG.warn("Fragmented Control from "+_endp);
_handler.close(WebSocketConnectionD13.CLOSE_PROTOCOL,"Fragmented control");
_handler.close(WebSocketConnectionRFC6455.CLOSE_PROTOCOL,"Fragmented control");
progress=true;
_skip=true;
}
@ -254,7 +269,7 @@ public class WebSocketParserD13 implements WebSocketParser
if (_length>_buffer.capacity() && !_fragmentFrames)
{
progress=true;
_handler.close(WebSocketConnectionD13.CLOSE_POLICY_VIOLATION,"frame size "+_length+">"+_buffer.capacity());
_handler.close(WebSocketConnectionRFC6455.CLOSE_POLICY_VIOLATION,"frame size "+_length+">"+_buffer.capacity());
_skip=true;
}
@ -273,7 +288,7 @@ public class WebSocketParserD13 implements WebSocketParser
if (_length>=_buffer.capacity() && !_fragmentFrames)
{
progress=true;
_handler.close(WebSocketConnectionD13.CLOSE_POLICY_VIOLATION,"frame size "+_length+">"+_buffer.capacity());
_handler.close(WebSocketConnectionRFC6455.CLOSE_POLICY_VIOLATION,"frame size "+_length+">"+_buffer.capacity());
_skip=true;
}
@ -307,7 +322,7 @@ public class WebSocketParserD13 implements WebSocketParser
if (_bytesNeeded==0)
_state=State.START;
break;
case SEEK_EOF:
progress=true;
_buffer.skip(available);
@ -323,7 +338,7 @@ public class WebSocketParserD13 implements WebSocketParser
_buffer.skip(_bytesNeeded);
_state=State.START;
progress=true;
_handler.close(WebSocketConnectionD13.CLOSE_PROTOCOL,"Not masked");
_handler.close(WebSocketConnectionRFC6455.CLOSE_PROTOCOL,"Not masked");
}
else
{
@ -349,7 +364,7 @@ public class WebSocketParserD13 implements WebSocketParser
break;
}
}
return progress?1:filled;
}
@ -380,8 +395,10 @@ public class WebSocketParserD13 implements WebSocketParser
@Override
public String toString()
{
Buffer buffer=_buffer;
return WebSocketParserD13.class.getSimpleName()+"@"+ Integer.toHexString(hashCode())+"|"+_state+"|"+(buffer==null?"<>":buffer.toDetailString());
return String.format("%s@%x state=%s buffer=%s",
getClass().getSimpleName(),
hashCode(),
_state,
_buffer);
}
}

View File

@ -1,20 +1,34 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
// ========================================================================
// Copyright (c) 2010 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.websocket;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@ -39,12 +53,13 @@ import javax.servlet.http.HttpServletResponse;
* <p>
* The initParameter "maxBinaryMessagesSize" can be used to set the size in bytes
* that a websocket may be accept before closing.
*
*
*/
@SuppressWarnings("serial")
public abstract class WebSocketServlet extends HttpServlet implements WebSocketFactory.Acceptor
{
WebSocketFactory _webSocketFactory;
/* ------------------------------------------------------------ */
/**
* @see javax.servlet.GenericServlet#init()
@ -57,15 +72,15 @@ public abstract class WebSocketServlet extends HttpServlet implements WebSocketF
String max=getInitParameter("maxIdleTime");
if (max!=null)
_webSocketFactory.setMaxIdleTime(Integer.parseInt(max));
max=getInitParameter("maxTextMessageSize");
if (max!=null)
_webSocketFactory.setMaxTextMessageSize(Integer.parseInt(max));
max=getInitParameter("maxBinaryMessageSize");
if (max!=null)
_webSocketFactory.setMaxBinaryMessageSize(Integer.parseInt(max));
}
/* ------------------------------------------------------------ */
@ -74,7 +89,7 @@ public abstract class WebSocketServlet extends HttpServlet implements WebSocketF
*/
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
{
if (_webSocketFactory.acceptWebSocket(request,response) || response.isCommitted())
return;
super.service(request,response);
@ -85,7 +100,7 @@ public abstract class WebSocketServlet extends HttpServlet implements WebSocketF
{
return true;
}
}

View File

@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface WebSocketServletConnection extends WebSocketConnection
{
void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException;
}

View File

@ -0,0 +1,92 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.QuotedStringTokenizer;
public class WebSocketServletConnectionD00 extends WebSocketConnectionD00 implements WebSocketServletConnection
{
public WebSocketServletConnectionD00(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol)
throws IOException
{
super(websocket,endpoint,buffers,timestamp,maxIdleTime,protocol);
}
public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException
{
String uri = request.getRequestURI();
String query = request.getQueryString();
if (query != null && query.length() > 0)
{
uri += "?" + query;
}
uri = new HttpURI(uri).toString();
String host = request.getHeader("Host");
String origin = request.getHeader("Sec-WebSocket-Origin");
if (origin == null)
{
origin = request.getHeader("Origin");
}
if (origin != null)
{
origin = QuotedStringTokenizer.quoteIfNeeded(origin,"\r\n");
}
String key1 = request.getHeader("Sec-WebSocket-Key1");
if (key1 != null)
{
String key2 = request.getHeader("Sec-WebSocket-Key2");
setHixieKeys(key1,key2);
response.setHeader("Upgrade","WebSocket");
response.addHeader("Connection","Upgrade");
if (origin != null)
{
response.addHeader("Sec-WebSocket-Origin",origin);
}
response.addHeader("Sec-WebSocket-Location",(request.isSecure()?"wss://":"ws://") + host + uri);
if (subprotocol != null)
{
response.addHeader("Sec-WebSocket-Protocol",subprotocol);
}
response.sendError(101,"WebSocket Protocol Handshake");
}
else
{
response.setHeader("Upgrade","WebSocket");
response.addHeader("Connection","Upgrade");
response.addHeader("WebSocket-Origin",origin);
response.addHeader("WebSocket-Location",(request.isSecure()?"wss://":"ws://") + host + uri);
if (subprotocol != null)
{
response.addHeader("WebSocket-Protocol",subprotocol);
}
response.sendError(101,"Web Socket Protocol Handshake");
response.flushBuffer();
onFrameHandshake();
onWebsocketOpen();
}
}
}

View File

@ -0,0 +1,50 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.EndPoint;
public class WebSocketServletConnectionD06 extends WebSocketConnectionD06 implements WebSocketServletConnection
{
public WebSocketServletConnectionD06(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol)
throws IOException
{
super(websocket,endpoint,buffers,timestamp,maxIdleTime,protocol);
}
/* ------------------------------------------------------------ */
public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException
{
String key = request.getHeader("Sec-WebSocket-Key");
response.setHeader("Upgrade","WebSocket");
response.addHeader("Connection","Upgrade");
response.addHeader("Sec-WebSocket-Accept",hashKey(key));
if (subprotocol!=null)
{
response.addHeader("Sec-WebSocket-Protocol",subprotocol);
}
response.sendError(101);
onFrameHandshake();
onWebSocketOpen();
}
}

View File

@ -0,0 +1,62 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.EndPoint;
public class WebSocketServletConnectionD08 extends WebSocketConnectionD08 implements WebSocketServletConnection
{
public WebSocketServletConnectionD08(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol,
List<Extension> extensions, int draft, MaskGen maskgen) throws IOException
{
super(websocket,endpoint,buffers,timestamp,maxIdleTime,protocol,extensions,draft,maskgen);
}
public WebSocketServletConnectionD08(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol,
List<Extension> extensions, int draft) throws IOException
{
super(websocket,endpoint,buffers,timestamp,maxIdleTime,protocol,extensions,draft);
}
/* ------------------------------------------------------------ */
public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException
{
String key = request.getHeader("Sec-WebSocket-Key");
response.setHeader("Upgrade","WebSocket");
response.addHeader("Connection","Upgrade");
response.addHeader("Sec-WebSocket-Accept",hashKey(key));
if (subprotocol != null)
{
response.addHeader("Sec-WebSocket-Protocol",subprotocol);
}
for (Extension ext : getExtensions())
{
response.addHeader("Sec-WebSocket-Extensions",ext.getParameterizedName());
}
response.sendError(101);
onFrameHandshake();
onWebSocketOpen();
}
}

View File

@ -0,0 +1,62 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.EndPoint;
public class WebSocketServletConnectionRFC6455 extends WebSocketConnectionRFC6455 implements WebSocketServletConnection
{
public WebSocketServletConnectionRFC6455(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol,
List<Extension> extensions, int draft, MaskGen maskgen) throws IOException
{
super(websocket,endpoint,buffers,timestamp,maxIdleTime,protocol,extensions,draft,maskgen);
}
public WebSocketServletConnectionRFC6455(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol,
List<Extension> extensions, int draft) throws IOException
{
super(websocket,endpoint,buffers,timestamp,maxIdleTime,protocol,extensions,draft);
}
/* ------------------------------------------------------------ */
public void handshake(HttpServletRequest request, HttpServletResponse response, String subprotocol) throws IOException
{
String key = request.getHeader("Sec-WebSocket-Key");
response.setHeader("Upgrade","WebSocket");
response.addHeader("Connection","Upgrade");
response.addHeader("Sec-WebSocket-Accept",hashKey(key));
if (subprotocol != null)
{
response.addHeader("Sec-WebSocket-Protocol",subprotocol);
}
for (Extension ext : getExtensions())
{
response.addHeader("Sec-WebSocket-Extensions",ext.getParameterizedName());
}
response.sendError(101);
onFrameHandshake();
onWebSocketOpen();
}
}

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;

View File

@ -0,0 +1,124 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import static org.hamcrest.Matchers.*;
import java.net.URI;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.log.StdErrLog;
import org.eclipse.jetty.websocket.helper.CaptureSocket;
import org.eclipse.jetty.websocket.helper.SafariD00;
import org.eclipse.jetty.websocket.helper.WebSocketCaptureServlet;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
public class SafariWebsocketDraft0Test
{
private Server server;
private WebSocketCaptureServlet servlet;
private URI serverUri;
@BeforeClass
public static void initLogging()
{
// Configure Logging
// System.setProperty("org.eclipse.jetty.util.log.class",StdErrLog.class.getName());
// System.setProperty("org.eclipse.jetty.LEVEL","DEBUG");
}
@Before
public void startServer() throws Exception
{
// Configure Server
server = new Server(0);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
server.setHandler(context);
// Serve capture servlet
servlet = new WebSocketCaptureServlet();
context.addServlet(new ServletHolder(servlet),"/");
// Start Server
server.start();
Connector conn = server.getConnectors()[0];
String host = conn.getHost();
if (host == null)
{
host = "localhost";
}
int port = conn.getLocalPort();
serverUri = new URI(String.format("ws://%s:%d/",host,port));
// System.out.printf("Server URI: %s%n",serverUri);
}
@Test
public void testSendTextMessages() throws Exception
{
SafariD00 safari = new SafariD00(serverUri);
try
{
safari.connect();
safari.issueHandshake();
// Send 5 short messages, using technique seen in Safari.
safari.sendMessage("aa-0"); // single msg
safari.sendMessage("aa-1", "aa-2", "aa-3", "aa-4");
// Servlet should show only 1 connection.
Assert.assertThat("Servlet.captureSockets.size",servlet.captures.size(),is(1));
CaptureSocket socket = servlet.captures.get(0);
Assert.assertThat("CaptureSocket",socket,notNullValue());
Assert.assertThat("CaptureSocket.isConnected", socket.isConnected(), is(true));
// Give servlet 500 millisecond to process messages
threadSleep(1,TimeUnit.SECONDS);
// Should have captured 5 messages.
Assert.assertThat("CaptureSocket.messages.size",socket.messages.size(),is(5));
}
finally
{
// System.out.println("Closing client socket");
safari.disconnect();
}
}
public static void threadSleep(int dur, TimeUnit unit) throws InterruptedException
{
long ms = TimeUnit.MILLISECONDS.convert(dur,unit);
Thread.sleep(ms);
}
@After
public void stopServer() throws Exception
{
server.stop();
}
}

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import java.net.InetSocketAddress;
@ -15,8 +30,6 @@ import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
/**
* @version $Revision$ $Date$
*
* This is not a general purpose websocket client.
* It's only for testing the websocket server and is hardwired to a specific draft version of the protocol.
*/
@ -140,7 +153,7 @@ public class TestClient implements WebSocket.OnFrame
{
__framesSent++;
byte flags= (byte)(off+len==data.length?0x8:0);
byte op=(byte)(off==0?opcode:WebSocketConnectionD13.OP_CONTINUATION);
byte op=(byte)(off==0?opcode:WebSocketConnectionRFC6455.OP_CONTINUATION);
if (_verbose)
System.err.printf("%s#sendFrame %s|%s %s\n",this.getClass().getSimpleName(),TypeUtil.toHexString(flags),TypeUtil.toHexString(op),TypeUtil.toHexString(data,off,len));
@ -158,7 +171,9 @@ public class TestClient implements WebSocket.OnFrame
public void disconnect() throws Exception
{
if (_connection!=null)
_connection.disconnect();
{
_connection.close();
}
}
@ -245,11 +260,11 @@ public class TestClient implements WebSocket.OnFrame
{
long next = System.currentTimeMillis()+delay;
byte opcode=binary?WebSocketConnectionD13.OP_BINARY:WebSocketConnectionD13.OP_TEXT;
byte opcode=binary?WebSocketConnectionRFC6455.OP_BINARY:WebSocketConnectionRFC6455.OP_TEXT;
byte data[]=null;
if (opcode==WebSocketConnectionD13.OP_TEXT)
if (opcode==WebSocketConnectionRFC6455.OP_TEXT)
{
StringBuilder b = new StringBuilder();
while (b.length()<size)
@ -263,7 +278,7 @@ public class TestClient implements WebSocket.OnFrame
}
for (int i=0;i<clients;i++)
client[i].ping(opcode,data,opcode==WebSocketConnectionD13.OP_PING?-1:fragment);
client[i].ping(opcode,data,opcode==WebSocketConnectionRFC6455.OP_PING?-1:fragment);
while(System.currentTimeMillis()<next)
Thread.sleep(10);

View File

@ -1,3 +1,18 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import java.io.IOException;
@ -207,7 +222,7 @@ public class TestServer extends Server
{
System.err.println("Ping "+connection);
byte[] data = { (byte)1, (byte) 2, (byte) 3 };
connection.sendControl(WebSocketConnectionD13.OP_PING,data,0,data.length);
connection.sendControl(WebSocketConnectionRFC6455.OP_PING,data,0,data.length);
}
}
catch (Exception e)
@ -223,7 +238,7 @@ public class TestServer extends Server
@Override
public boolean onControl(byte controlCode, byte[] data, int offset, int length)
{
if (controlCode==WebSocketConnectionD13.OP_PONG)
if (controlCode==WebSocketConnectionRFC6455.OP_PONG)
System.err.println("Pong "+getConnection());
return super.onControl(controlCode,data,offset,length);
}

View File

@ -1,13 +1,27 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import static org.hamcrest.Matchers.greaterThan;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URI;
@ -30,6 +44,9 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.is;
public class WebSocketClientTest
{
private WebSocketClientFactory _factory = new WebSocketClientFactory();
@ -133,7 +150,6 @@ public class WebSocketClientTest
Assert.assertFalse(open.get());
}
@Test
public void testAsyncConnectionRefused() throws Exception
{
@ -167,13 +183,11 @@ public class WebSocketClientTest
}
Assert.assertFalse(open.get());
Assert.assertEquals(WebSocketConnectionD13.CLOSE_NO_CLOSE,close.get());
Assert.assertEquals(WebSocketConnectionRFC6455.CLOSE_NO_CLOSE,close.get());
Assert.assertTrue(error instanceof ConnectException);
}
@Test
public void testConnectionNotAccepted() throws Exception
{
@ -207,7 +221,7 @@ public class WebSocketClientTest
}
Assert.assertFalse(open.get());
Assert.assertEquals(WebSocketConnectionD13.CLOSE_NO_CLOSE,close.get());
Assert.assertEquals(WebSocketConnectionRFC6455.CLOSE_NO_CLOSE,close.get());
Assert.assertTrue(error instanceof TimeoutException);
}
@ -246,12 +260,11 @@ public class WebSocketClientTest
}
Assert.assertFalse(open.get());
Assert.assertEquals(WebSocketConnectionD13.CLOSE_NO_CLOSE,close.get());
Assert.assertEquals(WebSocketConnectionRFC6455.CLOSE_NO_CLOSE,close.get());
Assert.assertTrue(error instanceof TimeoutException);
}
@Test
public void testBadHandshake() throws Exception
{
@ -287,7 +300,7 @@ public class WebSocketClientTest
}
Assert.assertFalse(open.get());
Assert.assertEquals(WebSocketConnectionD13.CLOSE_PROTOCOL,close.get());
Assert.assertEquals(WebSocketConnectionRFC6455.CLOSE_PROTOCOL,close.get());
Assert.assertTrue(error instanceof IOException);
Assert.assertTrue(error.getMessage().indexOf("404 NOT FOUND")>0);
@ -330,7 +343,7 @@ public class WebSocketClientTest
error=e.getCause();
}
Assert.assertFalse(open.get());
Assert.assertEquals(WebSocketConnectionD13.CLOSE_PROTOCOL,close.get());
Assert.assertEquals(WebSocketConnectionRFC6455.CLOSE_PROTOCOL,close.get());
Assert.assertTrue(error instanceof IOException);
Assert.assertTrue(error.getMessage().indexOf("Bad Sec-WebSocket-Accept")>=0);
}
@ -368,7 +381,7 @@ public class WebSocketClientTest
socket.close();
_latch.await(10,TimeUnit.SECONDS);
Assert.assertEquals(WebSocketConnectionD13.CLOSE_NO_CLOSE,close.get());
Assert.assertEquals(WebSocketConnectionRFC6455.CLOSE_NO_CLOSE,close.get());
}
@ -406,10 +419,9 @@ public class WebSocketClientTest
long start=System.currentTimeMillis();
_latch.await(10,TimeUnit.SECONDS);
Assert.assertTrue(System.currentTimeMillis()-start<5000);
Assert.assertEquals(WebSocketConnectionD13.CLOSE_NORMAL,close.get());
Assert.assertEquals(WebSocketConnectionRFC6455.CLOSE_NORMAL,close.get());
}
@Test
public void testNotIdle() throws Exception
{
@ -484,7 +496,6 @@ public class WebSocketClientTest
Assert.assertEquals("Invalid close code 1111", closeMessage.toString());
}
@Test
public void testBlockSending() throws Exception
{
@ -566,18 +577,17 @@ public class WebSocketClientTest
long writeDur = (System.currentTimeMillis() - start);
// wait for consumer to complete
while (totalB.get()<messages*(mesg.length()+6L))
while (totalB.get()<messages*(mesg.length()+6L))
{
Thread.sleep(10);
}
Assert.assertThat("write duration", writeDur, greaterThan(1000L)); // writing was blocked
Assert.assertEquals(messages*(mesg.length()+6L),totalB.get());
consumer.interrupt();
}
@Test
public void testBlockReceiving() throws Exception
{
@ -677,12 +687,12 @@ public class WebSocketClientTest
socket.getOutputStream().write(send,0,send.length);
socket.getOutputStream().flush();
}
while(consumer.isAlive())
while(consumer.isAlive())
{
Thread.sleep(10);
}
// Duration of the read operation.
long readDur = (System.currentTimeMillis() - start);
@ -700,6 +710,24 @@ public class WebSocketClientTest
Assert.assertEquals("Invalid close code 1111", closeMessage.toString());
}
@Test
public void testURIWithDefaultPort() throws Exception
{
URI uri = new URI("ws://localhost");
InetSocketAddress addr = WebSocketClient.toSocketAddress(uri);
Assert.assertThat("URI (" + uri + ").host", addr.getHostName(), is("localhost"));
Assert.assertThat("URI (" + uri + ").port", addr.getPort(), is(80));
}
@Test
public void testURIWithDefaultWSSPort() throws Exception
{
URI uri = new URI("wss://localhost");
InetSocketAddress addr = WebSocketClient.toSocketAddress(uri);
Assert.assertThat("URI (" + uri + ").host", addr.getHostName(), is("localhost"));
Assert.assertThat("URI (" + uri + ").port", addr.getPort(), is(443));
}
private void respondToClient(Socket connection, String serverResponse) throws IOException
{
InputStream in = null;
@ -748,7 +776,7 @@ public class WebSocketClientTest
}
connection.getOutputStream().write((
"HTTP/1.1 101 Upgrade\r\n" +
"Sec-WebSocket-Accept: "+ WebSocketConnectionD13.hashKey(key) +"\r\n" +
"Sec-WebSocket-Accept: "+ WebSocketConnectionRFC6455.hashKey(key) +"\r\n" +
"\r\n").getBytes());
}
}

View File

@ -0,0 +1,126 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import static org.hamcrest.Matchers.*;
import java.net.URI;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.websocket.helper.CaptureSocket;
import org.eclipse.jetty.websocket.helper.MessageSender;
import org.eclipse.jetty.websocket.helper.WebSocketCaptureServlet;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* WebSocketCommTest - to test reported undelivered messages in bug <a
* href="https://jira.codehaus.org/browse/JETTY-1463">JETTY-1463</a>
*/
public class WebSocketCommTest
{
private Server server;
private WebSocketCaptureServlet servlet;
private URI serverUri;
@Before
public void startServer() throws Exception
{
// Configure Server
server = new Server(0);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
server.setHandler(context);
// Serve capture servlet
servlet = new WebSocketCaptureServlet();
context.addServlet(new ServletHolder(servlet),"/");
// Start Server
server.start();
Connector conn = server.getConnectors()[0];
String host = conn.getHost();
if (host == null)
{
host = "localhost";
}
int port = conn.getLocalPort();
serverUri = new URI(String.format("ws://%s:%d/",host,port));
System.out.printf("Server URI: %s%n",serverUri);
}
@After
public void stopServer()
{
try
{
server.stop();
}
catch (Exception e)
{
e.printStackTrace(System.err);
}
}
@Test
public void testSendTextMessages() throws Exception
{
WebSocketClientFactory clientFactory = new WebSocketClientFactory();
clientFactory.start();
WebSocketClient wsc = clientFactory.newWebSocketClient();
MessageSender sender = new MessageSender();
wsc.open(serverUri,sender);
try
{
sender.awaitConnect();
// Send 5 short messages
for (int i = 0; i < 5; i++)
{
System.out.printf("Sending msg-%d%n",i);
sender.sendMessage("msg-%d",i);
}
// Servlet should show only 1 connection.
Assert.assertThat("Servlet.captureSockets.size",servlet.captures.size(),is(1));
CaptureSocket socket = servlet.captures.get(0);
Assert.assertThat("CaptureSocket",socket,notNullValue());
Assert.assertThat("CaptureSocket.isConnected",socket.isConnected(),is(true));
// Give servlet 500 millisecond to process messages
TimeUnit.MILLISECONDS.sleep(500);
// Should have captured 5 messages.
Assert.assertThat("CaptureSocket.messages.size",socket.messages.size(),is(5));
}
finally
{
System.out.println("Closing client socket");
sender.close();
}
}
}

View File

@ -1,6 +1,21 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.*;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.ByteArrayEndPoint;
@ -8,9 +23,6 @@ import org.eclipse.jetty.util.StringUtil;
import org.junit.Before;
import org.junit.Test;
/**
* @version $Revision$ $Date$
*/
public class WebSocketGeneratorD00Test
{
private ByteArrayBuffer _out;

View File

@ -1,6 +1,21 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.*;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.ByteArrayEndPoint;
@ -8,9 +23,6 @@ import org.eclipse.jetty.util.StringUtil;
import org.junit.Before;
import org.junit.Test;
/**
* @version $Revision$ $Date$
*/
public class WebSocketGeneratorD06Test
{
private ByteArrayBuffer _out;

View File

@ -1,6 +1,21 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.*;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.ByteArrayEndPoint;
@ -8,9 +23,6 @@ import org.eclipse.jetty.util.StringUtil;
import org.junit.Before;
import org.junit.Test;
/**
* @version $Revision$ $Date$
*/
public class WebSocketGeneratorD08Test
{
private ByteArrayBuffer _out;

View File

@ -1,7 +1,21 @@
/*******************************************************************************
* Copyright (c) 2011 Intalio, Inc.
* ======================================================================
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*******************************************************************************/
package org.eclipse.jetty.websocket;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.*;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.ByteArrayEndPoint;
@ -11,9 +25,8 @@ import org.junit.Before;
import org.junit.Test;
/**
* @version $Revision$ $Date$
*/
public class WebSocketGeneratorD13Test
public class WebSocketGeneratorRFC6455Test
{
private ByteArrayBuffer _out;
private WebSocketGenerator _generator;
@ -44,7 +57,7 @@ public class WebSocketGeneratorD13Test
@Test
public void testOneString() throws Exception
{
_generator = new WebSocketGeneratorD13(_buffers, _endPoint,null);
_generator = new WebSocketGeneratorRFC6455(_buffers, _endPoint,null);
byte[] data = "Hell\uFF4F W\uFF4Frld".getBytes(StringUtil.__UTF8);
_generator.addFrame((byte)0x8,(byte)0x04,data,0,data.length);
@ -71,7 +84,7 @@ public class WebSocketGeneratorD13Test
@Test
public void testOneBuffer() throws Exception
{
_generator = new WebSocketGeneratorD13(_buffers, _endPoint,null);
_generator = new WebSocketGeneratorRFC6455(_buffers, _endPoint,null);
String string = "Hell\uFF4F W\uFF4Frld";
byte[] bytes=string.getBytes(StringUtil.__UTF8);
@ -99,7 +112,7 @@ public class WebSocketGeneratorD13Test
@Test
public void testOneLongBuffer() throws Exception
{
_generator = new WebSocketGeneratorD13(_buffers, _endPoint,null);
_generator = new WebSocketGeneratorRFC6455(_buffers, _endPoint,null);
byte[] b=new byte[150];
for (int i=0;i<b.length;i++)
@ -120,7 +133,7 @@ public class WebSocketGeneratorD13Test
@Test
public void testOneStringMasked() throws Exception
{
_generator = new WebSocketGeneratorD13(_buffers, _endPoint,_maskGen);
_generator = new WebSocketGeneratorRFC6455(_buffers, _endPoint,_maskGen);
byte[] data = "Hell\uFF4F W\uFF4Frld".getBytes(StringUtil.__UTF8);
_generator.addFrame((byte)0x8,(byte)0x04,data,0,data.length);
@ -149,7 +162,7 @@ public class WebSocketGeneratorD13Test
@Test
public void testOneBufferMasked() throws Exception
{
_generator = new WebSocketGeneratorD13(_buffers, _endPoint,_maskGen);
_generator = new WebSocketGeneratorRFC6455(_buffers, _endPoint,_maskGen);
String string = "Hell\uFF4F W\uFF4Frld";
byte[] bytes=string.getBytes(StringUtil.__UTF8);
@ -179,7 +192,7 @@ public class WebSocketGeneratorD13Test
@Test
public void testOneLongBufferMasked() throws Exception
{
_generator = new WebSocketGeneratorD13(_buffers, _endPoint,_maskGen);
_generator = new WebSocketGeneratorRFC6455(_buffers, _endPoint,_maskGen);
byte[] b=new byte[150];
for (int i=0;i<b.length;i++)
@ -203,7 +216,7 @@ public class WebSocketGeneratorD13Test
@Test
public void testClose() throws Exception
{
_generator = new WebSocketGeneratorD13(_buffers, _endPoint,null);
_generator = new WebSocketGeneratorRFC6455(_buffers, _endPoint,null);
byte[] data = "xxGame Over".getBytes(StringUtil.__UTF8);
data[0]=(byte)(1000/0x100);

Some files were not shown because too many files have changed in this diff Show More