diff --git a/VERSION.txt b/VERSION.txt index de57855c17f..b260a487bb3 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1,9 +1,15 @@ +jetty-8.0.1-SNAPSHOT + jetty-8.0.0.v20110901 - 01 September 2011 + 352565 cookie httponly flag ignored + 353073 better warnings + 353285 ServletSecurity annotation ignored + 356421 Upgraded websocket to draft 13 support +jetty-7.5.0.v20110901 - 01 September 2011 + + 356421 Upgraded websocket to draft 13 support + + 353073 better warnings + jetty-7.5.0.RC2 - 30 August 2011 + 293739 Hide stacks in named log testing. Various other minor log cleanups in output. @@ -73,6 +79,12 @@ jetty-7.5.0.RC0 - 15 August 2011 + 354397 RewriteRegexRule handles special characters in regex group + 354466 Typo in example config of jetty-plus.xml +jetty-7.4.5.v20110725 - 25 July 2011 + + 347484 / - > ${/} in some paths in grant codebases + + 352133 resolve some 1.5isms + + 352421 HttpURI paths beginning with '.' + + 352786 GzipFilter fails to pass parameters to GzipResponseWrapper + jetty-7.4.4.v20110707 - 07 July 2011 + 308851 Converted all jetty-client module tests to JUnit 4 + 345268 JDBCSessionManager does not work with maxInactiveInterval = -1 diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java index 896ff0720ad..e1fe41149a5 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java @@ -336,7 +336,9 @@ public class HttpFields { if (__cache.size()>__cacheSize) __cache.clear(); - __cache.putIfAbsent(value,buffer); + Buffer b=__cache.putIfAbsent(value,buffer); + if (b!=null) + buffer=b; } return buffer; diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/gzip/GzipResponseWrapper.java b/jetty-http/src/main/java/org/eclipse/jetty/http/gzip/GzipResponseWrapper.java index ce8f766d9f2..5b331e149c2 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/gzip/GzipResponseWrapper.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/gzip/GzipResponseWrapper.java @@ -35,8 +35,8 @@ import org.eclipse.jetty.util.StringUtil; */ public class GzipResponseWrapper extends HttpServletResponseWrapper { - public static int DEFAULT_BUFFER_SIZE = 8192; - public static int DEFAULT_MIN_GZIP_SIZE = 256; + public static final int DEFAULT_BUFFER_SIZE = 8192; + public static final int DEFAULT_MIN_GZIP_SIZE = 256; private HttpServletRequest _request; private Set _mimeTypes; diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/security/Constraint.java b/jetty-http/src/main/java/org/eclipse/jetty/http/security/Constraint.java index 70e2608595b..984a8aa4719 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/security/Constraint.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/security/Constraint.java @@ -14,6 +14,7 @@ package org.eclipse.jetty.http.security; import java.io.Serializable; +import java.util.Arrays; /* ------------------------------------------------------------ */ /** @@ -209,7 +210,7 @@ public class Constraint implements Cloneable, Serializable { return "SC{" + _name + "," - + (_anyRole ? "*" : (_roles == null ? "-" : _roles.toString())) + + (_anyRole ? "*" : (_roles == null ? "-" : Arrays.asList(_roles).toString())) + "," + (_dataConstraint == DC_UNSET ? "DC_UNSET}" : (_dataConstraint == DC_NONE ? "NONE}" : (_dataConstraint == DC_INTEGRAL ? "INTEGRAL}" : "CONFIDENTIAL}"))); } diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/security/Password.java b/jetty-http/src/main/java/org/eclipse/jetty/http/security/Password.java index 8e2699854aa..fdaf58741ec 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/security/Password.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/security/Password.java @@ -152,12 +152,22 @@ public class Password extends Credential { case 1: buf.append('0'); + buf.append('0'); + buf.append('0'); + buf.append(x); + break; case 2: buf.append('0'); + buf.append('0'); + buf.append(x); + break; case 3: buf.append('0'); + buf.append(x); + break; default: buf.append(x); + break; } } return buf.toString(); diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectorManager.java b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectorManager.java index 9ca19c3956e..5b80a68d9df 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectorManager.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectorManager.java @@ -387,7 +387,7 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa private final ConcurrentLinkedQueue _changes = new ConcurrentLinkedQueue(); - private Selector _selector; + private volatile Selector _selector; private volatile Thread _selecting; private int _jvmBug; @@ -975,7 +975,8 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa // close endpoints and selector synchronized (this) { - for (SelectionKey key:_selector.keys()) + Selector selector=_selector; + for (SelectionKey key:selector.keys()) { if (key==null) continue; @@ -998,8 +999,9 @@ public abstract class SelectorManager extends AbstractLifeCycle implements Dumpa _timeout.cancelAll(); try { - if (_selector != null) - _selector.close(); + selector=_selector; + if (selector != null) + selector.close(); } catch (IOException e) { diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SslSelectChannelEndPoint.java b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SslSelectChannelEndPoint.java index 751da449985..0866534a935 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SslSelectChannelEndPoint.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SslSelectChannelEndPoint.java @@ -300,7 +300,12 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint } } } - catch (Exception x) + catch (ThreadDeath x) + { + super.close(); + throw x; + } + catch (Throwable x) { LOG.debug(x); super.close(); @@ -444,8 +449,11 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint case BUFFER_OVERFLOW: case BUFFER_UNDERFLOW: LOG.warn("wrap {}",_result); + _closing=true; + break; case CLOSED: _closing=true; + break; } _outNIOBuffer.setPutIndex(put+_result.bytesProduced()); @@ -500,7 +508,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException { int consumed=0; - int available=header.length(); + int available=header==null?0:header.length(); if (buffer!=null) available+=buffer.length(); @@ -599,8 +607,11 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint case BUFFER_OVERFLOW: case BUFFER_UNDERFLOW: LOG.warn("unwrap {}",_result); + _closing=true; + break; case CLOSED: _closing=true; + break; } _outNIOBuffer.setPutIndex(put+_result.bytesProduced()); } @@ -895,6 +906,8 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint case BUFFER_OVERFLOW: case BUFFER_UNDERFLOW: LOG.warn("unwrap {}",_result); + _closing=true; + return _result.bytesConsumed()>0?_result.bytesConsumed():-1; case OK: return _result.bytesConsumed(); @@ -965,6 +978,8 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint case BUFFER_OVERFLOW: case BUFFER_UNDERFLOW: LOG.warn("unwrap {}",_result); + _closing=true; + return _result.bytesConsumed()>0?_result.bytesConsumed():-1; case OK: return _result.bytesConsumed(); diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/IOTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/IOTest.java index d2759a6f84a..72fd8e35d00 100644 --- a/jetty-io/src/test/java/org/eclipse/jetty/io/IOTest.java +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/IOTest.java @@ -42,33 +42,4 @@ public class IOTest out.toString(), "The quick brown fox jumped over the lazy dog"); } - - @Test - public void testStringSpeed() - { - String s="012345678901234567890000000000000000000000000"; - char[] ca = new char[s.length()]; - int loops=1000000; - - long start=System.currentTimeMillis(); - long result=0; - for (int loop=0;loop0;) - result+=s.charAt(c); - } - long end=System.currentTimeMillis(); - System.err.println("charAt "+(end-start)+" "+result); - - start=System.currentTimeMillis(); - result=0; - for (int loop=0;loop0;) - result+=ca[c]; - } - end=System.currentTimeMillis(); - System.err.println("getChars "+(end-start)+" "+result); - } } diff --git a/jetty-monitor/pom.xml b/jetty-monitor/pom.xml index ee74472ace6..87cdd3f0cd1 100644 --- a/jetty-monitor/pom.xml +++ b/jetty-monitor/pom.xml @@ -124,6 +124,12 @@ ${project.version} test + + org.eclipse.jetty + jetty-websocket + ${project.version} + test + org.eclipse.jetty jetty-server diff --git a/jetty-nosql/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java b/jetty-nosql/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java index 5bda287bf29..269cf4c710a 100644 --- a/jetty-nosql/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java +++ b/jetty-nosql/src/test/java/org/eclipse/jetty/nosql/mongodb/MongoTestServer.java @@ -52,7 +52,7 @@ public class MongoTestServer extends AbstractTestServer _saveAllAttributes = saveAllAttributes; } - public SessionIdManager newSessionIdManager() + public SessionIdManager newSessionIdManager(String config) { if ( _idManager != null ) { diff --git a/jetty-policy/src/test/java/org/eclipse/jetty/policy/PolicyMonitorTest.java b/jetty-policy/src/test/java/org/eclipse/jetty/policy/PolicyMonitorTest.java index ba5c2de8de7..06008534282 100644 --- a/jetty-policy/src/test/java/org/eclipse/jetty/policy/PolicyMonitorTest.java +++ b/jetty-policy/src/test/java/org/eclipse/jetty/policy/PolicyMonitorTest.java @@ -45,7 +45,7 @@ public class PolicyMonitorTest { Thread.sleep(100); } - + Assert.assertEquals(1,count.get()); monitor.stop(); } @@ -73,17 +73,14 @@ public class PolicyMonitorTest monitor.setScanInterval(1); monitor.start(); - - while (! monitor.isInitialized() ) - { - Thread.sleep(100); - } - + monitor.waitForScan(); + monitor.waitForScan(); + File permFile =new File(MavenTestingUtils.getTargetDir(), "test-classes/monitor-test-2/global-all-permission.policy"); - // Wait so that time is definitely different - Thread.sleep(10); + // Wait so that time is definitely different + monitor.waitForScan(); permFile.setLastModified(System.currentTimeMillis()); monitor.waitForScan(); diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java index 65471e2881c..7fa563a8502 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/DigestAuthenticator.java @@ -115,6 +115,7 @@ public class DigestAuthenticator extends LoginAuthenticator break; case ',': name = null; + break; case ' ': break; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java index dc929eed3e0..2b97c66bedb 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionManager.java @@ -704,7 +704,7 @@ public abstract class AbstractSessionManager extends AbstractLifeCycle implement { HttpSessionEvent event=new HttpSessionEvent(session); for (HttpSessionListener listener : _sessionListeners) - listener.sessionCreated(event); + listener.sessionDestroyed(event); } } } diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java index 55cecada02e..50e175dae19 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java @@ -177,7 +177,7 @@ public class HttpConnectionTest response=connector.getResponses("GET /foo/bar%c0%00 HTTP/1.1\n"+ "Host: localhost\n"+ "\015\012"); - checkContains(response,0,"pathInfo=/foo/bar?"); + checkContains(response,0,"HTTP/1.1 400"); response=connector.getResponses("GET /bad/utf8%c1 HTTP/1.1\n"+ "Host: localhost\n"+ diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java index 3061d9429be..d6aeaad2974 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java @@ -148,8 +148,10 @@ public class ServletContextHandler extends ContextHandler protected void doStop() throws Exception { super.doStop(); - _decorators.clear(); - _wrapper.setHandler(null); + if (_decorators != null) + _decorators.clear(); + if (_wrapper != null) + _wrapper.setHandler(null); } /* ------------------------------------------------------------ */ diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8Appendable.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8Appendable.java index 92bdb5671bb..ccad4fed295 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8Appendable.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8Appendable.java @@ -1,6 +1,7 @@ package org.eclipse.jetty.util; import java.io.IOException; +import java.util.IllegalFormatCodePointException; public abstract class Utf8Appendable { @@ -69,6 +70,7 @@ public abstract class Utf8Appendable _appendable.append('?'); _more=0; _bits=0; + throw new NotUtf8Exception(); } else _appendable.append((char)(0x7f&b)); @@ -81,6 +83,7 @@ public abstract class Utf8Appendable _appendable.append('?'); _more=0; _bits=0; + throw new NotUtf8Exception(); } else { @@ -116,7 +119,7 @@ public abstract class Utf8Appendable } else { - throw new IllegalArgumentException("!utf8"); + throw new NotUtf8Exception(); } } } @@ -127,7 +130,7 @@ public abstract class Utf8Appendable _appendable.append('?'); _more=0; _bits=0; - throw new IllegalArgumentException("!utf8"); + throw new NotUtf8Exception(); } else { @@ -139,4 +142,12 @@ public abstract class Utf8Appendable } } + + public static class NotUtf8Exception extends IllegalStateException + { + public NotUtf8Exception() + { + super("!UTF-8"); + } + } } \ No newline at end of file diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8StringBuffer.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8StringBuffer.java index b6051d59435..40de5306dce 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8StringBuffer.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8StringBuffer.java @@ -60,7 +60,7 @@ public class Utf8StringBuffer extends Utf8Appendable public StringBuffer getStringBuffer() { if (_more!=0) - throw new IllegalStateException("!utf8"); + throw new NotUtf8Exception(); return _buffer; } @@ -68,7 +68,7 @@ public class Utf8StringBuffer extends Utf8Appendable public String toString() { if (_more!=0) - throw new IllegalStateException("!utf8"); + throw new NotUtf8Exception(); return _buffer.toString(); } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8StringBuilder.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8StringBuilder.java index f270021d957..7e4477163c3 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8StringBuilder.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Utf8StringBuilder.java @@ -59,7 +59,7 @@ public class Utf8StringBuilder extends Utf8Appendable public StringBuilder getStringBuilder() { if (_more!=0) - throw new IllegalStateException("!utf8"); + throw new NotUtf8Exception(); return _buffer; } @@ -67,7 +67,7 @@ public class Utf8StringBuilder extends Utf8Appendable public String toString() { if (_more!=0) - throw new IllegalStateException("!utf8"); + throw new NotUtf8Exception(); return _buffer.toString(); } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/FileResource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/FileResource.java index 1cf2ce18b49..64e6a4d1adc 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/FileResource.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/FileResource.java @@ -114,9 +114,9 @@ public class FileResource extends URLResource if (_urlString.endsWith("/")) _urlString=_urlString.substring(0,_urlString.length()-1); } - + } - + /* -------------------------------------------------------- */ FileResource(URL url, URLConnection connection, File file) { diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java index 4cb78891eb1..93be57c7747 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java @@ -166,8 +166,7 @@ public abstract class Resource implements ResourceFactory URLConnection connection=url.openConnection(); connection.setUseCaches(useCaches); - FileResource fileResource= new FileResource(url,connection,file); - return fileResource; + return new FileResource(url,connection,file); } catch(Exception e2) { @@ -199,6 +198,18 @@ public abstract class Resource implements ResourceFactory return newResource(url); } + /* ------------------------------------------------------------ */ + public static Resource newResource (File file) + throws MalformedURLException, IOException + { + file = file.getCanonicalFile(); + URL url = Resource.toURL(file); + + URLConnection connection = url.openConnection(); + FileResource fileResource = new FileResource(url, connection, file); + return fileResource; + } + /* ------------------------------------------------------------ */ /** Construct a system resource from a string. * The resource is tried as classloader resource before being diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/Utf8StringBufferTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/Utf8StringBufferTest.java index 555ea45bbca..5b0c3afae54 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/Utf8StringBufferTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/Utf8StringBufferTest.java @@ -50,7 +50,7 @@ public class Utf8StringBufferTest } catch(IllegalStateException e) { - assertTrue(e.toString().indexOf("!utf8")>=0); + assertTrue(e.toString().indexOf("!UTF-8")>=0); } } @@ -64,8 +64,16 @@ public class Utf8StringBufferTest bytes[4]=(byte)0x00; Utf8StringBuffer buffer = new Utf8StringBuffer(); - for (int i=0;i=0); + } assertEquals("abc?",buffer.toString()); } diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/Utf8StringBuilderTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/Utf8StringBuilderTest.java index 20ca6674a73..1ee751627fe 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/Utf8StringBuilderTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/Utf8StringBuilderTest.java @@ -33,7 +33,7 @@ public class Utf8StringBuilderTest assertEquals(source, buffer.toString()); assertTrue(buffer.toString().endsWith("jetty")); } - + @Test public void testShort() throws Exception @@ -50,7 +50,7 @@ public class Utf8StringBuilderTest } catch(IllegalStateException e) { - assertTrue(e.toString().indexOf("!utf8")>=0); + assertTrue(e.toString().indexOf("!UTF-8")>=0); } } @@ -64,9 +64,17 @@ public class Utf8StringBuilderTest bytes[4]=(byte)0x00; Utf8StringBuilder buffer = new Utf8StringBuilder(); - for (int i=0;i=0); + } + assertEquals("abc?", buffer.toString()); } diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java index e88ae66ac98..2ddd8cd9910 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java @@ -24,7 +24,6 @@ import java.util.EventListener; import java.util.HashMap; import java.util.List; import java.util.Map; - import javax.servlet.ServletContext; import javax.servlet.http.HttpSessionActivationListener; import javax.servlet.http.HttpSessionAttributeListener; @@ -60,9 +59,9 @@ import org.eclipse.jetty.util.resource.ResourceCollection; * The handlers are configured by pluggable configuration classes, with * the default being {@link org.eclipse.jetty.webapp.WebXmlConfiguration} and * {@link org.eclipse.jetty.webapp.JettyWebXmlConfiguration}. - * + * * @org.apache.xbean.XBean description="Creates a servlet web application at a given context from a resource base" - * + * */ public class WebAppContext extends ServletContextHandler implements WebAppClassLoader.Context { @@ -75,7 +74,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL public final static String SERVER_CONFIG = "org.eclipse.jetty.webapp.configuration"; public final static String SERVER_SYS_CLASSES = "org.eclipse.jetty.webapp.systemClasses"; public final static String SERVER_SRV_CLASSES = "org.eclipse.jetty.webapp.serverClasses"; - + private static String[] __dftConfigurationClasses = { "org.eclipse.jetty.webapp.WebInfConfiguration", @@ -85,38 +84,40 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL "org.eclipse.jetty.webapp.JettyWebXmlConfiguration"//, //"org.eclipse.jetty.webapp.TagLibConfiguration" } ; - - // System classes are classes that cannot be replaced by - // the web application, and they are *always* loaded via + + // System classes are classes that cannot be replaced by + // the web application, and they are *always* loaded via // system classloader. - public final static String[] __dftSystemClasses = + public final static String[] __dftSystemClasses = { - "java.", // Java SE classes (per servlet spec v2.5 / SRV.9.7.2) + "java.", // Java SE classes (per servlet spec v2.5 / SRV.9.7.2) "javax.", // Java SE classes (per servlet spec v2.5 / SRV.9.7.2) "org.xml.", // needed by javax.xml "org.w3c.", // needed by javax.xml - "org.apache.commons.logging.", // TODO: review if special case still needed + "org.apache.commons.logging.", // TODO: review if special case still needed "org.eclipse.jetty.continuation.", // webapp cannot change continuation classes "org.eclipse.jetty.jndi.", // webapp cannot change naming classes "org.eclipse.jetty.plus.jaas.", // webapp cannot change jaas classes - "org.eclipse.jetty.websocket.", // WebSocket is a jetty extension - "org.eclipse.jetty.servlet.DefaultServlet" // webapp cannot change default servlets + "org.eclipse.jetty.websocket.WebSocket", // WebSocket is a jetty extension + "org.eclipse.jetty.websocket.WebSocketFactory", // WebSocket is a jetty extension + "org.eclipse.jetty.servlet.DefaultServlet" // webapp cannot change default servlets } ; - + // Server classes are classes that are hidden from being // loaded by the web application using system classloader, // so if web application needs to load any of such classes, // it has to include them in its distribution. - public final static String[] __dftServerClasses = + public final static String[] __dftServerClasses = { "-org.eclipse.jetty.continuation.", // don't hide continuation classes "-org.eclipse.jetty.jndi.", // don't hide naming classes "-org.eclipse.jetty.plus.jaas.", // don't hide jaas classes - "-org.eclipse.jetty.websocket.", // don't hide websocket extension + "-org.eclipse.jetty.websocket.WebSocket", // WebSocket is a jetty extension + "-org.eclipse.jetty.websocket.WebSocketFactory", // WebSocket is a jetty extension "-org.eclipse.jetty.servlet.DefaultServlet", // don't hide default servlet - "-org.eclipse.jetty.servlet.listener.", //don't hide useful listeners + "-org.eclipse.jetty.servlet.listener.", // don't hide useful listeners "org.eclipse.jetty." // hide other jetty classes - } ; + } ; private String[] _configurationClasses = __dftConfigurationClasses; private ClasspathPattern _systemClasses = null; @@ -135,12 +136,12 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL private PermissionCollection _permissions; private String[] _contextWhiteList = null; - + private File _tmpDir; private String _war; private String _extraClasspath; private Throwable _unavailableException; - + private Map _resourceAliases; private boolean _ownClassLoader=false; private boolean _configurationDiscovered=true; @@ -148,7 +149,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL private boolean _configurationsSet=false; private boolean _allowDuplicateFragmentNames = false; private boolean _throwUnavailableOnStartupException = false; - + private MetaData _metadata=new MetaData(); public static WebAppContext getCurrentWebAppContext() @@ -162,15 +163,15 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL } return null; } - + /* ------------------------------------------------------------ */ public WebAppContext() { - super(SESSIONS|SECURITY); + super(SESSIONS|SECURITY); _scontext=new Context(); - setErrorHandler(new ErrorPageErrorHandler()); + setErrorHandler(new ErrorPageErrorHandler()); } - + /* ------------------------------------------------------------ */ /** * @param contextPath The context path @@ -182,9 +183,9 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL _scontext=new Context(); setContextPath(contextPath); setWar(webApp); - setErrorHandler(new ErrorPageErrorHandler()); + setErrorHandler(new ErrorPageErrorHandler()); } - + /* ------------------------------------------------------------ */ /** * @param parent The parent HandlerContainer. @@ -196,7 +197,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL super(parent,contextPath,SESSIONS|SECURITY); _scontext=new Context(); setWar(webApp); - setErrorHandler(new ErrorPageErrorHandler()); + setErrorHandler(new ErrorPageErrorHandler()); } /* ------------------------------------------------------------ */ @@ -214,7 +215,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL _scontext = new Context(); setErrorHandler(errorHandler != null ? errorHandler : new ErrorPageErrorHandler()); } - + /* ------------------------------------------------------------ */ /** * @param servletContextName The servletContextName to set. @@ -227,7 +228,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL if (cl!=null && cl instanceof WebAppClassLoader && servletContextName!=null) ((WebAppClassLoader)cl).setName(servletContextName); } - + /* ------------------------------------------------------------ */ /** Get an exception that caused the webapp to be unavailable * @return A throwable if the webapp is unavailable or null @@ -237,7 +238,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL return _unavailableException; } - + /* ------------------------------------------------------------ */ /** Set Resource Alias. * Resource aliases map resource uri's within a context. @@ -260,13 +261,13 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL return null; return _resourceAliases; } - + /* ------------------------------------------------------------ */ public void setResourceAliases(Map map) { _resourceAliases = map; } - + /* ------------------------------------------------------------ */ public String getResourceAlias(String alias) { @@ -291,16 +292,16 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL public void setClassLoader(ClassLoader classLoader) { super.setClassLoader(classLoader); - + // if ( !(classLoader instanceof WebAppClassLoader) ) // { // LOG.info("NOTE: detected a classloader which is not an instance of WebAppClassLoader being set on WebAppContext, some typical class and resource locations may be missing on: " + toString() ); // } - + if (classLoader!=null && classLoader instanceof WebAppClassLoader && getDisplayName()!=null) ((WebAppClassLoader)classLoader).setName(getDisplayName()); } - + /* ------------------------------------------------------------ */ @Override public Resource getResource(String uriInContext) throws MalformedURLException @@ -318,7 +319,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL resource= super.getResource(uriInContext); if (resource != null && resource.exists()) return resource; - + uriInContext = getResourceAlias(uriInContext); } catch (IOException e) @@ -334,11 +335,11 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL return resource; } - + /* ------------------------------------------------------------ */ /** Is the context Automatically configured. - * + * * @return true if configuration discovery. */ public boolean isConfigurationDiscovered() @@ -360,7 +361,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL { _configurationDiscovered = discovered; } - + /* ------------------------------------------------------------ */ /** Pre configure the web application. *

@@ -371,7 +372,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL *

  • Setup the default System classes by calling {@link #loadSystemClasses()} *
  • Setup the default Server classes by calling loadServerClasses() *
  • Instantiates a classload (if one is not already set) - *
  • Calls the {@link Configuration#preConfigure(WebAppContext)} method of all + *
  • Calls the {@link Configuration#preConfigure(WebAppContext)} method of all * Configuration instances. * * @throws Exception @@ -383,7 +384,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL // Setup system classes loadSystemClasses(); - + // Setup server classes loadServerClasses(); @@ -407,8 +408,8 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL loader=loader.getParent(); } } - - // Prepare for configuration + + // Prepare for configuration for (int i=0;i<_configurations.length;i++) { LOG.debug("preConfigure {} with {}",this,_configurations[i]); @@ -426,10 +427,10 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL _configurations[i].configure(this); } } - + /* ------------------------------------------------------------ */ public void postConfigure() throws Exception - { + { // Clean up after configuration for (int i=0;i<_configurations.length;i++) { @@ -437,7 +438,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL _configurations[i].postConfigure(this); } } - + /* ------------------------------------------------------------ */ /* * @see org.eclipse.thread.AbstractLifeCycle#doStart() @@ -465,7 +466,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL throw e; } } - + /* ------------------------------------------------------------ */ /* * @see org.eclipse.thread.AbstractLifeCycle#doStop() @@ -479,11 +480,11 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL { for (int i=_configurations.length;i-->0;) _configurations[i].deconfigure(this); - + if (_metadata != null) _metadata.clear(); _metadata=new MetaData(); - + } finally { @@ -494,19 +495,19 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL _unavailableException=null; } } - + /* ------------------------------------------------------------ */ @Override public void destroy() { - // Prepare for configuration + // Prepare for configuration MultiException mx=new MultiException(); if (_configurations!=null) { for (int i=_configurations.length;i-->0;) { try - { + { _configurations[i].destroy(this); } catch(Exception e) @@ -534,7 +535,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL String displayName = getDisplayName(); if (displayName == null) displayName = "WebApp@"+connectors.hashCode(); - + LOG.info(displayName + " at http://" + connectorName + getContextPath()); } } @@ -547,7 +548,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL { return _configurationClasses; } - + /* ------------------------------------------------------------ */ /** * @return Returns the configurations. @@ -556,7 +557,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL { return _configurations; } - + /* ------------------------------------------------------------ */ /** * The default descriptor is a web.xml format file that is applied to the context before the standard WEB-INF/web.xml @@ -566,7 +567,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL { return _defaultsDescriptor; } - + /* ------------------------------------------------------------ */ /** * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml @@ -579,7 +580,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL return null; return _overrideDescriptors.get(0); } - + /* ------------------------------------------------------------ */ /** * An override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml @@ -589,7 +590,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL { return Collections.unmodifiableList(_overrideDescriptors); } - + /* ------------------------------------------------------------ */ /** * @return Returns the permissions. @@ -608,7 +609,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL { if (_serverClasses == null) loadServerClasses(); - + return _serverClasses.getPatterns(); } @@ -616,10 +617,10 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL { if (_serverClasses == null) loadServerClasses(); - + _serverClasses.addPattern(classname); } - + /* ------------------------------------------------------------ */ /** * @see #setSystemClasses(String[]) @@ -629,25 +630,25 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL { if (_systemClasses == null) loadSystemClasses(); - + return _systemClasses.getPatterns(); } - + /* ------------------------------------------------------------ */ public void addSystemClass(String classname) { if (_systemClasses == null) loadSystemClasses(); - + _systemClasses.addPattern(classname); } - + /* ------------------------------------------------------------ */ public boolean isServerClass(String name) { if (_serverClasses == null) loadServerClasses(); - + return _serverClasses.match(name); } @@ -656,16 +657,16 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL { if (_systemClasses == null) loadSystemClasses(); - + return _systemClasses.match(name); } - + /* ------------------------------------------------------------ */ protected void loadSystemClasses() { if (_systemClasses != null) return; - + //look for a Server attribute with the list of System classes //to apply to every web application. If not present, use our defaults. Server server = getServer(); @@ -675,17 +676,17 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL if (systemClasses != null && systemClasses instanceof String[]) _systemClasses = new ClasspathPattern((String[])systemClasses); } - + if (_systemClasses == null) _systemClasses = new ClasspathPattern(__dftSystemClasses); } - + /* ------------------------------------------------------------ */ private void loadServerClasses() { if (_serverClasses != null) return; - + //look for a Server attribute with the list of Server classes //to apply to every web application. If not present, use our defaults. Server server = getServer(); @@ -695,11 +696,11 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL if (serverClasses != null || serverClasses instanceof String[]) _serverClasses = new ClasspathPattern((String[])serverClasses); } - + if (_serverClasses == null) _serverClasses = new ClasspathPattern(__dftServerClasses); } - + /* ------------------------------------------------------------ */ /** * @return Returns the war as a file or URL string (Resource) @@ -721,10 +722,10 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL Resource web_inf= super.getBaseResource().addPath("WEB-INF/"); if (!web_inf.exists() || !web_inf.isDirectory()) return null; - + return web_inf; } - + /* ------------------------------------------------------------ */ /** * @return Returns the distributable. @@ -763,35 +764,35 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL /* ------------------------------------------------------------ */ /** - * @return True if the classloader should delegate first to the parent - * classloader (standard java behaviour) or false if the classloader - * should first try to load from WEB-INF/lib or WEB-INF/classes (servlet + * @return True if the classloader should delegate first to the parent + * classloader (standard java behaviour) or false if the classloader + * should first try to load from WEB-INF/lib or WEB-INF/classes (servlet * spec recommendation). */ public boolean isParentLoaderPriority() { return _parentLoaderPriority; } - - + + /* ------------------------------------------------------------ */ public String[] getDefaultConfigurationClasses () { return __dftConfigurationClasses; } - + /* ------------------------------------------------------------ */ public String[] getDefaultServerClasses () { return __dftServerClasses; } - + /* ------------------------------------------------------------ */ public String[] getDefaultSystemClasses () { return __dftSystemClasses; } - + /* ------------------------------------------------------------ */ protected void loadConfigurations() throws Exception @@ -810,17 +811,17 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL _configurations[i]=(Configuration)Loader.loadClass(this.getClass(), _configurationClasses[i]).newInstance(); } } - + /* ------------------------------------------------------------ */ @Override protected boolean isProtectedTarget(String target) { while (target.startsWith("//")) target=URIUtil.compactPath(target); - + return StringUtil.startsWithIgnoreCase(target, "/web-inf") || StringUtil.startsWithIgnoreCase(target, "/meta-inf"); } - + /* ------------------------------------------------------------ */ @Override @@ -842,7 +843,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL _configurationClassesSet = true; _configurations=null; } - + /* ------------------------------------------------------------ */ /** * @param configurations The configurations to set. @@ -876,7 +877,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL _overrideDescriptors.clear(); _overrideDescriptors.add(overrideDescriptor); } - + /* ------------------------------------------------------------ */ /** * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml @@ -887,7 +888,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL _overrideDescriptors.clear(); _overrideDescriptors.addAll(overrideDescriptors); } - + /* ------------------------------------------------------------ */ /** * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml @@ -915,7 +916,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL { _descriptor=descriptor; } - + /* ------------------------------------------------------------ */ /** * @param distributable The distributable to set. @@ -931,13 +932,13 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL { if (_sessionHandler!=null) _sessionHandler.clearEventListeners(); - + super.setEventListeners(eventListeners); - + for (int i=0; eventListeners!=null && i jetty-project org.eclipse.jetty 8.0.1-SNAPSHOT - 4.0.0 - org.eclipse.jetty - jetty-websocket - Jetty :: Websocket - - ${project.groupId}.websocket - - - - org.eclipse.jetty - jetty-server - ${project.version} - - + + 4.0.0 + jetty-websocket + Jetty :: Websocket + + + ${project.groupId}.websocket + + + + ${servlet.spec.groupId} ${servlet.spec.artifactId} provided - org.eclipse.jetty.toolchain - jetty-test-helper - test + org.eclipse.jetty + jetty-server + ${project.version} + provided + + org.eclipse.jetty + jetty-util + ${project.version} + + + org.eclipse.jetty + jetty-io + ${project.version} + + + org.eclipse.jetty + jetty-http + ${project.version} + + + + junit + junit + test + - - - - org.apache.felix - maven-bundle-plugin - true - - - - manifest - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - artifact-jar - - jar - - - - test-jar - - test-jar - - - - - - ${project.build.outputDirectory}/META-INF/MANIFEST.MF - - - - - org.codehaus.mojo - findbugs-maven-plugin - - org.eclipse.jetty.websocket.* - - - - + + + + + org.apache.felix + maven-bundle-plugin + true + + + + manifest + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + artifact-jar + + jar + + + + test-jar + + test-jar + + + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + org.codehaus.mojo + findbugs-maven-plugin + + org.eclipse.jetty.websocket.* + + + + diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/DeflateFrameExtension.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/DeflateFrameExtension.java index e07e9b6da9e..b3d6f44b4d2 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/DeflateFrameExtension.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/DeflateFrameExtension.java @@ -11,6 +11,9 @@ import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +/** + * @TODO Implement proposed deflate frame draft + */ public class DeflateFrameExtension extends AbstractExtension { private static final Logger LOG = Log.getLogger(DeflateFrameExtension.class); @@ -18,7 +21,7 @@ public class DeflateFrameExtension extends AbstractExtension private int _minLength=8; private Deflater _deflater; private Inflater _inflater; - + public DeflateFrameExtension() { super("x-deflate-frame"); @@ -32,10 +35,10 @@ public class DeflateFrameExtension extends AbstractExtension if(super.init(parameters)) { _minLength=getInitParameter("minLength",_minLength); - + _deflater=new Deflater(); _inflater=new Inflater(); - + return true; } return false; @@ -46,7 +49,7 @@ public class DeflateFrameExtension extends AbstractExtension */ @Override public void onFrame(byte flags, byte opcode, Buffer buffer) - { + { if (getConnection().isControl(opcode) || !isFlag(flags,1)) { super.onFrame(flags,opcode,buffer); @@ -55,7 +58,7 @@ public class DeflateFrameExtension extends AbstractExtension if (buffer.array()==null) buffer=buffer.asMutableBuffer(); - + int length=0xff&buffer.get(); if (length>=0x7e) { @@ -63,10 +66,10 @@ public class DeflateFrameExtension extends AbstractExtension length=0; while(b-->0) length=0x100*length+(0xff&buffer.get()); - } - + } + // TODO check a max framesize - + _inflater.setInput(buffer.array(),buffer.getIndex(),buffer.length()); ByteArrayBuffer buf = new ByteArrayBuffer(length); try @@ -99,12 +102,12 @@ public class DeflateFrameExtension extends AbstractExtension super.addFrame(clearFlag(flags,1),opcode,content,offset,length); return; } - + // prepare the uncompressed input _deflater.reset(); _deflater.setInput(content,offset,length); _deflater.finish(); - + // prepare the output buffer byte[] out= new byte[length]; int out_offset=0; @@ -113,10 +116,10 @@ public class DeflateFrameExtension extends AbstractExtension if (length>0xffff) { out[out_offset++]=0x7f; - out[out_offset++]=(byte)((length>>56)&0x7f); - out[out_offset++]=(byte)((length>>48)&0xff); - out[out_offset++]=(byte)((length>>40)&0xff); - out[out_offset++]=(byte)((length>>32)&0xff); + out[out_offset++]=(byte)0; + out[out_offset++]=(byte)0; + out[out_offset++]=(byte)0; + out[out_offset++]=(byte)0; out[out_offset++]=(byte)((length>>24)&0xff); out[out_offset++]=(byte)((length>>16)&0xff); out[out_offset++]=(byte)((length>>8)&0xff); @@ -125,7 +128,7 @@ public class DeflateFrameExtension extends AbstractExtension else if (length >=0x7e) { out[out_offset++]=0x7e; - out[out_offset++]=(byte)(byte)(length>>8); + out[out_offset++]=(byte)(length>>8); out[out_offset++]=(byte)(length&0xff); } else @@ -134,7 +137,7 @@ public class DeflateFrameExtension extends AbstractExtension } int l = _deflater.deflate(out,out_offset,length-out_offset); - + if (_deflater.finished()) super.addFrame(setFlag(flags,1),opcode,out,0,l+out_offset); else diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/FixedMaskGen.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/FixedMaskGen.java index 1c7c0c55459..da59dcdc7b2 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/FixedMaskGen.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/FixedMaskGen.java @@ -3,22 +3,23 @@ package org.eclipse.jetty.websocket; public class FixedMaskGen implements MaskGen { - final byte[] _mask; + private final byte[] _mask; + public FixedMaskGen() { - _mask=new byte[]{(byte)0xff,(byte)0xff,(byte)0xff,(byte)0xff}; + this(new byte[]{(byte)0xff,(byte)0xff,(byte)0xff,(byte)0xff}); } - + public FixedMaskGen(byte[] mask) { - _mask=mask; + _mask=new byte[4]; + // Copy to avoid that external code keeps a reference + // to the array parameter to modify masking on-the-fly + System.arraycopy(mask, 0, _mask, 0, 4); } - + public void genMask(byte[] mask) { - mask[0]=_mask[0]; - mask[1]=_mask[1]; - mask[2]=_mask[2]; - mask[3]=_mask[3]; + System.arraycopy(_mask, 0, mask, 0, 4); } -} \ No newline at end of file +} diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/RandomMaskGen.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/RandomMaskGen.java index 889bb0d31e1..c9340fc4d7e 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/RandomMaskGen.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/RandomMaskGen.java @@ -5,19 +5,23 @@ import java.util.Random; public class RandomMaskGen implements MaskGen { - final Random _random; + private final Random _random; + public RandomMaskGen() { - _random=new Random(); + this(new Random()); } - + public RandomMaskGen(Random random) { _random=random; } - + public void genMask(byte[] mask) { + // The assumption is that this code is always called + // with an external lock held to prevent concurrent access + // Otherwise we need to synchronize on the _random. _random.nextBytes(mask); } -} \ No newline at end of file +} diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketBuffers.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketBuffers.java index ac42f55939c..fb9f53bf962 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketBuffers.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketBuffers.java @@ -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.websocket; @@ -21,7 +21,7 @@ import org.eclipse.jetty.io.BuffersFactory; /* ------------------------------------------------------------ */ /** The WebSocket Buffer Pool. - * + * * The normal buffers are byte array buffers so that user processes * can access directly. However the generator uses direct buffers * for the final output stage as they are filled in bulk and are more @@ -31,24 +31,23 @@ public class WebSocketBuffers { final private int _bufferSize; final private Buffers _buffers; - final private int _maxBuffers=-1; - + public WebSocketBuffers(final int bufferSize) { _bufferSize=bufferSize; - _buffers = BuffersFactory.newBuffers(Type.DIRECT,bufferSize,Type.INDIRECT,bufferSize,Type.INDIRECT,_maxBuffers); + _buffers = BuffersFactory.newBuffers(Type.DIRECT,bufferSize,Type.INDIRECT,bufferSize,Type.INDIRECT,-1); } - + public Buffer getBuffer() { return _buffers.getBuffer(); } - + public Buffer getDirectBuffer() { return _buffers.getHeader(); } - + public void returnBuffer(Buffer buffer) { _buffers.returnBuffer(buffer); diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClient.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClient.java index 50bad287b68..4bde98307e9 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClient.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClient.java @@ -262,7 +262,7 @@ public class WebSocketClient /** * Set the initial maximum binary message size for a connection. This can be changed by * the application calling {@link WebSocket.Connection#setMaxBinaryMessageSize(int)}. - * @param maxTextMessageSize The default maximum binary message size (in bytes) for a connection + * @param maxBinaryMessageSize The default maximum binary message size (in bytes) for a connection */ public void setMaxBinaryMessageSize(int maxBinaryMessageSize) { @@ -381,20 +381,21 @@ public class WebSocketClient { connection.getConnection().setMaxTextMessageSize(_maxTextMessageSize); connection.getConnection().setMaxBinaryMessageSize(_maxBinaryMessageSize); - + + WebSocketConnection con; synchronized (this) { if (_channel!=null) _connection=connection; + con=_connection; } - if (_connection!=null) + if (con!=null) { if (_websocket instanceof WebSocket.OnFrame) - ((WebSocket.OnFrame)_websocket).onHandshake((WebSocket.FrameConnection)connection.getConnection()); - - _websocket.onOpen(connection.getConnection()); + ((WebSocket.OnFrame)_websocket).onHandshake((WebSocket.FrameConnection)con.getConnection()); + _websocket.onOpen(con.getConnection()); } } finally diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClientFactory.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClientFactory.java index fdd03feaf3e..0b64d340234 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClientFactory.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketClientFactory.java @@ -320,34 +320,41 @@ public class WebSocketClientFactory extends AggregateLifeCycle String origin = future.getOrigin(); - String request= - "GET "+path+" HTTP/1.1\r\n"+ - "Host: "+future.getURI().getHost()+":"+_future.getURI().getPort()+"\r\n"+ - "Upgrade: websocket\r\n"+ - "Connection: Upgrade\r\n"+ - "Sec-WebSocket-Key: "+_key+"\r\n"+ - (origin==null?"":"Origin: "+origin+"\r\n")+ - "Sec-WebSocket-Version: "+WebSocketConnectionD13.VERSION+"\r\n"; + 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("Origin: ").append(origin).append("\r\n"); + + request.append("Sec-WebSocket-Version: ").append(WebSocketConnectionD13.VERSION).append("\r\n"); if (future.getProtocol()!=null) - request+="Sec-WebSocket-Protocol: "+future.getProtocol()+"\r\n"; + request.append("Sec-WebSocket-Protocol: ").append(future.getProtocol()).append("\r\n"); if (future.getCookies()!=null && future.getCookies().size()>0) { for (String cookie : future.getCookies().keySet()) - request+="Cookie: "+QuotedStringTokenizer.quoteIfNeeded(cookie,HttpFields.__COOKIE_DELIM)+ - "="+ - QuotedStringTokenizer.quoteIfNeeded(future.getCookies().get(cookie),HttpFields.__COOKIE_DELIM)+ - "\r\n"; + request + .append("Cookie: ") + .append(QuotedStringTokenizer.quoteIfNeeded(cookie,HttpFields.__COOKIE_DELIM)) + .append("=") + .append(QuotedStringTokenizer.quoteIfNeeded(future.getCookies().get(cookie),HttpFields.__COOKIE_DELIM)) + .append("\r\n"); } - request+="\r\n"; + request.append("\r\n"); // TODO extensions try { - Buffer handshake = new ByteArrayBuffer(request,false); + Buffer handshake = new ByteArrayBuffer(request.toString(),false); int len=handshake.length(); if (len!=_endp.flush(handshake)) throw new IOException("incomplete"); diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD00.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD00.java index b8e69c8e506..e19e9ef0ca1 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD00.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD00.java @@ -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.websocket; @@ -18,10 +18,10 @@ import java.security.MessageDigest; 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,8 +30,8 @@ import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.nio.IndirectNIOBuffer; import org.eclipse.jetty.io.nio.SelectChannelEndPoint; +import org.eclipse.jetty.util.QuotedStringTokenizer; import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.Utf8StringBuilder; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.websocket.WebSocket.OnFrame; @@ -51,21 +51,21 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc String _key1; String _key2; ByteArrayBuffer _hixieBytes; - + public WebSocketConnectionD00(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol) throws IOException { super(endpoint,timestamp); if (endpoint instanceof AsyncEndPoint) ((AsyncEndPoint)endpoint).cancelIdle(); - + _endp.setMaxIdleTime(maxIdleTime); - + _websocket = websocket; _protocol=protocol; _generator = new WebSocketGeneratorD00(buffers, _endp); - _parser = new WebSocketParserD00(buffers, endpoint, new FrameHandlerD0(_websocket)); + _parser = new WebSocketParserD00(buffers, endpoint, new FrameHandlerD00(_websocket)); if (_endp instanceof SelectChannelEndPoint) { @@ -112,8 +112,8 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc { // handle stupid hixie random bytes if (_hixieBytes!=null) - { - + { + // take any available bytes from the parser buffer, which may have already been read Buffer buffer=_parser.getBuffer(); if (buffer!=null && buffer.length()>0) @@ -124,7 +124,7 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc _hixieBytes.put(buffer.peek(buffer.getIndex(),l)); buffer.skip(l); } - + // while we are not blocked while(_endp.isOpen()) { @@ -154,7 +154,7 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc _websocket.onOpen(this); return this; } - + // handle the framing protocol boolean progress=true; @@ -195,7 +195,7 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc _endp.close(); else checkWriteable(); - + checkWriteable(); } } @@ -204,7 +204,7 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc /* ------------------------------------------------------------ */ private void doTheHixieHixieShake() - { + { byte[] result=WebSocketConnectionD00.doTheHixieHixieShake( WebSocketConnectionD00.hixieCrypt(_key1), WebSocketConnectionD00.hixieCrypt(_key2), @@ -334,12 +334,12 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc } public static byte[] doTheHixieHixieShake(long key1,long key2,byte[] key3) - { + { try { MessageDigest md = MessageDigest.getInstance("MD5"); byte [] fodder = new byte[16]; - + fodder[0]=(byte)(0xff&(key1>>24)); fodder[1]=(byte)(0xff&(key1>>16)); fodder[2]=(byte)(0xff&(key1>>8)); @@ -348,11 +348,9 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc fodder[5]=(byte)(0xff&(key2>>16)); fodder[6]=(byte)(0xff&(key2>>8)); fodder[7]=(byte)(0xff&key2); - for (int i=0;i<8;i++) - fodder[8+i]=key3[i]; + System.arraycopy(key3, 0, fodder, 8, 8); md.update(fodder); - byte[] result=md.digest(); - return result; + return md.digest(); } catch (NoSuchAlgorithmException e) { @@ -371,14 +369,18 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc 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"); @@ -413,7 +415,7 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc { } - public void setMaxIdleTime(int ms) + public void setMaxIdleTime(int ms) { try { @@ -424,7 +426,7 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc LOG.warn(e); } } - + public void setMaxBinaryMessageSize(int size) { } @@ -448,23 +450,22 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc { return _protocol; } - - class FrameHandlerD0 implements WebSocketParser.FrameHandler + + static class FrameHandlerD00 implements WebSocketParser.FrameHandler { final WebSocket _websocket; - final Utf8StringBuilder _utf8 = new Utf8StringBuilder(); - FrameHandlerD0(WebSocket websocket) + FrameHandlerD00(WebSocket websocket) { _websocket=websocket; } - + public void onFrame(byte flags, byte opcode, Buffer buffer) { try { byte[] array=buffer.array(); - + if (opcode==0) { if (_websocket instanceof WebSocket.OnTextMessage) @@ -485,10 +486,9 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc LOG.warn(th); } } - + public void close(int code,String message) { - close(code,message); } } diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD06.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD06.java index 7cfd493e87e..fdd7c77de8d 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD06.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD06.java @@ -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.websocket; @@ -18,7 +18,6 @@ import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.util.Collections; import java.util.List; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -49,18 +48,18 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc final static byte OP_PONG = 0x03; final static byte OP_TEXT = 0x04; final static byte OP_BINARY = 0x05; - + final static int CLOSE_NORMAL=1000; final static int CLOSE_SHUTDOWN=1001; final static int CLOSE_PROTOCOL=1002; final static int CLOSE_BADDATA=1003; final static int CLOSE_LARGE=1004; - + static boolean isLastFrame(int flags) { return (flags&0x8)!=0; } - + static boolean isControlFrame(int opcode) { switch(opcode) @@ -73,8 +72,8 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc return false; } } - - + + private final static byte[] MAGIC; private final IdleCheck _idle; private final WebSocketParser _parser; @@ -85,8 +84,8 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc private final OnTextMessage _onTextMessage; private final OnControl _onControl; private final String _protocol; - private boolean _closedIn; - private boolean _closedOut; + private volatile boolean _closedIn; + private volatile boolean _closedOut; private int _maxTextMessageSize; private int _maxBinaryMessageSize=-1; @@ -101,27 +100,26 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc throw new RuntimeException(e); } } - + private final WebSocketParser.FrameHandler _frameHandler= new FrameHandlerD06(); /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ private final WebSocket.FrameConnection _connection = new FrameConnectionD06(); - + /* ------------------------------------------------------------ */ public WebSocketConnectionD06(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol) throws IOException { super(endpoint,timestamp); - - // TODO - can we use the endpoint idle mechanism? + if (endpoint instanceof AsyncEndPoint) ((AsyncEndPoint)endpoint).cancelIdle(); - + _endp.setMaxIdleTime(maxIdleTime); - + _webSocket = websocket; _onFrame=_webSocket instanceof OnFrame ? (OnFrame)_webSocket : null; _onTextMessage=_webSocket instanceof OnTextMessage ? (OnTextMessage)_webSocket : null; @@ -131,7 +129,6 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc _parser = new WebSocketParserD06(buffers, endpoint, _frameHandler,true); _protocol=protocol; - // TODO should these be AsyncEndPoint checks/calls? if (_endp instanceof SelectChannelEndPoint) { final SelectChannelEndPoint scep=(SelectChannelEndPoint)_endp; @@ -153,8 +150,8 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc {} }; } - - _maxTextMessageSize=buffers.getBufferSize(); + + _maxTextMessageSize=buffers.getBufferSize(); _maxBinaryMessageSize=-1; } @@ -163,7 +160,7 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc { return _connection; } - + /* ------------------------------------------------------------ */ public Connection handle() throws IOException { @@ -178,7 +175,7 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc int filled=_parser.parseNext(); progress = flushed>0 || filled>0; - + if (filled<0 || flushed<0) { _endp.close(); @@ -210,7 +207,7 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc else checkWriteable(); } - + } return this; } @@ -248,7 +245,7 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc { if (_closedOut) _endp.close(); - else + else closeOut(code,message); } catch(IOException e) @@ -269,7 +266,7 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc { if (_closedIn || _closedOut) _endp.close(); - else + else { if (code<=0) code=WebSocketConnectionD06.CLOSE_NORMAL; @@ -279,7 +276,7 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc _generator.addFrame((byte)0x8,WebSocketConnectionD06.OP_CLOSE,bytes,0,bytes.length); } _generator.flush(); - + } catch(IOException e) { @@ -388,7 +385,7 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc } /* ------------------------------------------------------------ */ - public void setMaxIdleTime(int ms) + public void setMaxIdleTime(int ms) { try { @@ -459,7 +456,7 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc { return 0x8; } - + /* ------------------------------------------------------------ */ public boolean isControl(byte opcode) { @@ -535,14 +532,14 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc public void onFrame(byte flags, byte opcode, Buffer buffer) { - boolean lastFrame = isLastFrame(flags); - + boolean lastFrame = isLastFrame(flags); + synchronized(WebSocketConnectionD06.this) { // Ignore incoming after a close if (_closedIn) return; - + try { byte[] array=buffer.array(); @@ -553,13 +550,13 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc if (_onFrame.onFrame(flags,opcode,array,buffer.getIndex(),buffer.length())) return; } - + if (_onControl!=null && isControlFrame(opcode)) { if (_onControl.onControl(opcode,array,buffer.getIndex(),buffer.length())) return; } - + switch(opcode) { case WebSocketConnectionD06.OP_CONTINUATION: @@ -583,7 +580,7 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc _connection.close(WebSocketConnectionD06.CLOSE_LARGE,"Text message size > "+_connection.getMaxTextMessageSize()+" chars"); _utf8.reset(); _opcode=-1; - } + } } else if (_opcode>=0 && _connection.getMaxBinaryMessageSize()>=0) { @@ -652,7 +649,7 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc // Deliver the message _onTextMessage.onMessage(buffer.toString(StringUtil.__UTF8)); } - else + else { if (_connection.getMaxTextMessageSize()>=0) { @@ -662,7 +659,7 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc else { _utf8.reset(); - _opcode=-1; + _opcode=-1; _connection.close(WebSocketConnectionD06.CLOSE_LARGE,"Text message size > "+_connection.getMaxTextMessageSize()+" chars"); } } @@ -679,7 +676,7 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc { _onBinaryMessage.onMessage(array,buffer.getIndex(),buffer.length()); } - else + else { if (_connection.getMaxBinaryMessageSize()>=0) { @@ -700,7 +697,7 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc } } } - } + } } } catch(ThreadDeath th) @@ -734,12 +731,8 @@ public class WebSocketConnectionD06 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; String key = request.getHeader("Sec-WebSocket-Key"); - + response.setHeader("Upgrade","WebSocket"); response.addHeader("Connection","Upgrade"); response.addHeader("Sec-WebSocket-Accept",hashKey(key)); diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD12.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD12.java index d8d5e618596..1f86bdd851d 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD12.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD12.java @@ -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.websocket; @@ -18,7 +18,6 @@ import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.util.Collections; import java.util.List; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -53,7 +52,7 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc final static byte OP_PING = 0x09; final static byte OP_PONG = 0x0A; final static byte OP_EXT_CTRL = 0x0B; - + final static int CLOSE_NORMAL=1000; final static int CLOSE_SHUTDOWN=1001; final static int CLOSE_PROTOCOL=1002; @@ -61,21 +60,21 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc final static int CLOSE_NOCODE=1005; final static int CLOSE_NOCLOSE=1006; final static int CLOSE_NOTUTF8=1007; - + final static int FLAG_FIN=0x8; - + final static int VERSION=8; - + static boolean isLastFrame(byte flags) { return (flags&FLAG_FIN)!=0; } - + static boolean isControlFrame(byte opcode) { return (opcode&OP_CONTROL)!=0; } - + private final static byte[] MAGIC; private final IdleCheck _idle; private final List _extensions; @@ -97,7 +96,7 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc private volatile boolean _closedOut; private int _maxTextMessageSize=-1; private int _maxBinaryMessageSize=-1; - + static { try @@ -109,11 +108,11 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc throw new RuntimeException(e); } } - + private final WebSocketParser.FrameHandler _frameHandler= new WSFrameHandler(); private final WebSocket.FrameConnection _connection = new WSFrameConnection(); - + /* ------------------------------------------------------------ */ public WebSocketConnectionD12(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol, List extensions,int draft) @@ -121,28 +120,28 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc { this(websocket,endpoint,buffers,timestamp,maxIdleTime,protocol,extensions,draft,null); } - + /* ------------------------------------------------------------ */ public WebSocketConnectionD12(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol, List extensions,int draft, MaskGen maskgen) throws IOException { super(endpoint,timestamp); - + _context=Thread.currentThread().getContextClassLoader(); - + if (endpoint instanceof AsyncEndPoint) ((AsyncEndPoint)endpoint).cancelIdle(); - + _draft=draft; _endp.setMaxIdleTime(maxIdleTime); - + _webSocket = websocket; _onFrame=_webSocket instanceof OnFrame ? (OnFrame)_webSocket : null; _onTextMessage=_webSocket instanceof OnTextMessage ? (OnTextMessage)_webSocket : null; _onBinaryMessage=_webSocket instanceof OnBinaryMessage ? (OnBinaryMessage)_webSocket : null; _onControl=_webSocket instanceof OnControl ? (OnControl)_webSocket : null; _generator = new WebSocketGeneratorD12(buffers, _endp,maskgen); - + _extensions=extensions; if (_extensions!=null) { @@ -159,12 +158,11 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc _outbound=(_extensions==null||_extensions.size()==0)?_generator:extensions.get(extensions.size()-1); _inbound=(_extensions==null||_extensions.size()==0)?_frameHandler:extensions.get(0); - + _parser = new WebSocketParserD12(buffers, endpoint,_inbound,maskgen==null); - + _protocol=protocol; - // TODO should these be AsyncEndPoint checks/calls? if (_endp instanceof SelectChannelEndPoint) { final SelectChannelEndPoint scep=(SelectChannelEndPoint)_endp; @@ -193,16 +191,16 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc { return _connection; } - + /* ------------------------------------------------------------ */ public List getExtensions() { if (_extensions==null) return Collections.emptyList(); - + return _extensions; } - + /* ------------------------------------------------------------ */ public Connection handle() throws IOException { @@ -220,7 +218,7 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc int filled=_parser.parseNext(); progress = flushed>0 || filled>0; - + if (filled<0 || flushed<0) { _endp.close(); @@ -297,7 +295,7 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc public void closeIn(int code,String message) { LOG.debug("ClosedIn {} {}",this,message); - + final boolean closedOut; final boolean closed; synchronized (this) @@ -323,7 +321,7 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc { if (closedOut) _endp.close(); - else + else closeOut(code,message); } catch(IOException e) @@ -337,7 +335,7 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc public void closeOut(int code,String message) { LOG.debug("ClosedOut {} {}",this,message); - + final boolean close; final boolean closed; synchronized (this) @@ -351,7 +349,7 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc _closeMessage=message; } } - + try { if (closed) @@ -363,7 +361,7 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc { if (close) _endp.close(); - else + else { if (code<=0) code=WebSocketConnectionD12.CLOSE_NORMAL; @@ -467,7 +465,7 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc } /* ------------------------------------------------------------ */ - public void setMaxIdleTime(int ms) + public void setMaxIdleTime(int ms) { try { @@ -496,7 +494,7 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc { return _endp.getMaxIdleTime(); } - + /* ------------------------------------------------------------ */ public int getMaxTextMessageSize() { @@ -538,7 +536,7 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc { return FLAG_FIN; } - + /* ------------------------------------------------------------ */ public boolean isControl(byte opcode) { @@ -586,7 +584,7 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc { close(CLOSE_NORMAL,null); } - + /* ------------------------------------------------------------ */ public void setAllowFrameFragmentation(boolean allowFragmentation) { @@ -618,8 +616,8 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc public void onFrame(final byte flags, final byte opcode, final Buffer buffer) { - boolean lastFrame = isLastFrame(flags); - + boolean lastFrame = isLastFrame(flags); + synchronized(WebSocketConnectionD12.this) { // Ignore incoming after a close @@ -664,7 +662,7 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc else textMessageTooLarge(); } - + if (_opcode>=0 && _connection.getMaxBinaryMessageSize()>=0) { if (checkBinaryMessageSize(_aggregate.length(),buffer.length())) @@ -762,7 +760,6 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc else if (_connection.getMaxBinaryMessageSize()>=0) { _opcode=opcode; - // TODO use a growing buffer rather than a fixed one. if (_aggregate==null) _aggregate=new ByteArrayBuffer(_connection.getMaxBinaryMessageSize()); _aggregate.put(buffer); @@ -773,7 +770,7 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc _connection.close(WebSocketConnectionD12.CLOSE_BADDATA,"Binary frame aggregation disabled"); } } - } + } } } catch(ThreadDeath th) @@ -800,7 +797,7 @@ public class WebSocketConnectionD12 extends AbstractConnection implements WebSoc } return true; } - + private void textMessageTooLarge() { LOG.warn("Text message too large > {} chars for {}",_connection.getMaxTextMessageSize(),_endp); @@ -833,23 +830,19 @@ public class WebSocketConnectionD12 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; String key = request.getHeader("Sec-WebSocket-Key"); - - response.setHeader("Upgrade","WebSocket"); + + 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); diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD13.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD13.java index ef3b6503876..c01037865f3 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD13.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD13.java @@ -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.websocket; @@ -18,7 +18,6 @@ import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.util.Collections; import java.util.List; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -31,6 +30,7 @@ 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; import org.eclipse.jetty.util.Utf8StringBuilder; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -53,7 +53,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc final static byte OP_PING = 0x09; final static byte OP_PONG = 0x0A; final static byte OP_EXT_CTRL = 0x0B; - + final static int CLOSE_NORMAL=1000; final static int CLOSE_SHUTDOWN=1001; final static int CLOSE_PROTOCOL=1002; @@ -65,26 +65,25 @@ 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 FLAG_FIN=0x8; - + final static int VERSION=13; - + static boolean isLastFrame(byte flags) { return (flags&FLAG_FIN)!=0; } - + static boolean isControlFrame(byte opcode) { return (opcode&OP_CONTROL)!=0; } - + private final static byte[] MAGIC; private final IdleCheck _idle; private final List _extensions; private final WebSocketParserD13 _parser; - private final WebSocketParser.FrameHandler _inbound; private final WebSocketGeneratorD13 _generator; private final WebSocketGenerator _outbound; private final WebSocket _webSocket; @@ -101,7 +100,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc private volatile boolean _closedOut; private int _maxTextMessageSize=-1; private int _maxBinaryMessageSize=-1; - + static { try @@ -113,11 +112,9 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc throw new RuntimeException(e); } } - - private final WebSocketParser.FrameHandler _frameHandler= new WSFrameHandler(); private final WebSocket.FrameConnection _connection = new WSFrameConnection(); - + /* ------------------------------------------------------------ */ public WebSocketConnectionD13(WebSocket websocket, EndPoint endpoint, WebSocketBuffers buffers, long timestamp, int maxIdleTime, String protocol, List extensions,int draft) @@ -125,29 +122,30 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc { 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 extensions,int draft, MaskGen maskgen) throws IOException { super(endpoint,timestamp); - + _context=Thread.currentThread().getContextClassLoader(); - + if (endpoint instanceof AsyncEndPoint) ((AsyncEndPoint)endpoint).cancelIdle(); - + _draft=draft; _endp.setMaxIdleTime(maxIdleTime); - + _webSocket = websocket; _onFrame=_webSocket instanceof OnFrame ? (OnFrame)_webSocket : null; _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); - + _extensions=extensions; + WebSocketParser.FrameHandler frameHandler = new WSFrameHandler(); if (_extensions!=null) { int e=0; @@ -155,17 +153,17 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc { extension.bind( _connection, - e==extensions.size()-1?_frameHandler:extensions.get(e+1), + e==extensions.size()-1? frameHandler :extensions.get(e+1), e==0?_generator:extensions.get(e-1)); e++; } } _outbound=(_extensions==null||_extensions.size()==0)?_generator:extensions.get(extensions.size()-1); - _inbound=(_extensions==null||_extensions.size()==0)?_frameHandler:extensions.get(0); - - _parser = new WebSocketParserD13(buffers, endpoint,_inbound,maskgen==null); - + WebSocketParser.FrameHandler inbound = (_extensions == null || _extensions.size() == 0) ? frameHandler : extensions.get(0); + + _parser = new WebSocketParserD13(buffers, endpoint, inbound,maskgen==null); + _protocol=protocol; // TODO should these be AsyncEndPoint checks/calls? @@ -197,16 +195,16 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc { return _connection; } - + /* ------------------------------------------------------------ */ public List getExtensions() { if (_extensions==null) return Collections.emptyList(); - + return _extensions; } - + /* ------------------------------------------------------------ */ public Connection handle() throws IOException { @@ -224,7 +222,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc int filled=_parser.parseNext(); progress = flushed>0 || filled>0; - + if (filled<0 || flushed<0) { _endp.close(); @@ -301,15 +299,15 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc public void closeIn(int code,String message) { LOG.debug("ClosedIn {} {}",this,message); - - final boolean closedOut; - final boolean closed; + + final boolean close; + final boolean tell_app; synchronized (this) { - closedOut=_closedOut; + close=_closedOut; _closedIn=true; - closed=_closeCode==0; - if (closed) + tell_app=_closeCode==0; + if (tell_app) { _closeCode=code; _closeMessage=message; @@ -318,16 +316,16 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc try { - if (closed) + if (tell_app) _webSocket.onClose(code,message); } finally { try { - if (closedOut) + if (close) _endp.close(); - else + else closeOut(code,message); } catch(IOException e) @@ -341,33 +339,33 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc public void closeOut(int code,String message) { LOG.debug("ClosedOut {} {}",this,message); - + final boolean close; - final boolean closed; + final boolean tell_app; + final boolean send_close; synchronized (this) { - close=_closedIn || _closedOut; + close=_closedIn; + send_close=!_closedOut; _closedOut=true; - closed=_closeCode==0; - if (closed) + tell_app=_closeCode==0; + if (tell_app) { _closeCode=code; _closeMessage=message; } } - + try { - if (closed) + if (tell_app) _webSocket.onClose(code,message); } finally { try { - if (close) - _endp.close(); - else + if (send_close) { if (code<=0) code=WebSocketConnectionD13.CLOSE_NORMAL; @@ -375,8 +373,12 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc bytes[0]=(byte)(code/0x100); bytes[1]=(byte)(code%0x100); _outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD13.OP_CLOSE,bytes,0,bytes.length); + _outbound.flush(); + if (close) + _endp.shutdownOutput(); } - _outbound.flush(); + else if (close) + _endp.close(); } catch(IOException e) @@ -471,7 +473,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc } /* ------------------------------------------------------------ */ - public void setMaxIdleTime(int ms) + public void setMaxIdleTime(int ms) { try { @@ -500,7 +502,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc { return _endp.getMaxIdleTime(); } - + /* ------------------------------------------------------------ */ public int getMaxTextMessageSize() { @@ -542,7 +544,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc { return FLAG_FIN; } - + /* ------------------------------------------------------------ */ public boolean isControl(byte opcode) { @@ -590,7 +592,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc { close(CLOSE_NORMAL,null); } - + /* ------------------------------------------------------------ */ public void setAllowFrameFragmentation(boolean allowFragmentation) { @@ -622,8 +624,8 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc public void onFrame(final byte flags, final byte opcode, final Buffer buffer) { - boolean lastFrame = isLastFrame(flags); - + boolean lastFrame = isLastFrame(flags); + synchronized(WebSocketConnectionD13.this) { // Ignore incoming after a close @@ -668,7 +670,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc else textMessageTooLarge(); } - + if (_opcode>=0 && _connection.getMaxBinaryMessageSize()>=0) { if (checkBinaryMessageSize(_aggregate.length(),buffer.length())) @@ -712,7 +714,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc String message=null; if (buffer.length()>=2) { - code=buffer.array()[buffer.getIndex()]*0x100+buffer.array()[buffer.getIndex()+1]; + code=(0xff&buffer.array()[buffer.getIndex()])*0x100+(0xff&buffer.array()[buffer.getIndex()+1]); if (buffer.length()>2) message=new String(buffer.array(),buffer.getIndex()+2,buffer.length()-2,StringUtil.__UTF8); } @@ -777,9 +779,15 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc _connection.close(WebSocketConnectionD13.CLOSE_POLICY_VIOLATION,"Binary frame aggregation disabled"); } } - } + } } } + catch(Utf8Appendable.NotUtf8Exception notUtf8) + { + LOG.warn("{} for {}",notUtf8,_endp); + LOG.debug(notUtf8); + _connection.close(WebSocketConnectionD13.CLOSE_NOT_UTF8,"Invalid UTF-8"); + } catch(ThreadDeath th) { throw th; @@ -804,7 +812,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc } return true; } - + private void textMessageTooLarge() { LOG.warn("Text message too large > {} chars for {}",_connection.getMaxTextMessageSize(),_endp); @@ -837,23 +845,19 @@ public class WebSocketConnectionD13 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; 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); diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketFactory.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketFactory.java index a2798bf9988..4752ac67d28 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketFactory.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketFactory.java @@ -20,7 +20,6 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -43,16 +42,19 @@ public class WebSocketFactory { /* ------------------------------------------------------------ */ /** - * @param request - * @param protocol - * @returns + *

    Factory method that applications needs to implement to return a + * {@link WebSocket} object.

    + * @param request the incoming HTTP upgrade request + * @param protocol the websocket sub protocol + * @return a new {@link WebSocket} object that will handle websocket events. */ WebSocket doWebSocketConnect(HttpServletRequest request, String protocol); /* ------------------------------------------------------------ */ - /** Check the origin of an incoming WebSocket handshake request - * @param request - * @param origin + /** + *

    Checks the origin of an incoming WebSocket handshake request.

    + * @param request the incoming HTTP upgrade request + * @param origin the origin URI * @return boolean to indicate that the origin is acceptable. */ boolean checkOrigin(HttpServletRequest request, String origin); @@ -64,7 +66,7 @@ public class WebSocketFactory _extensionClasses.put("fragment",FragmentExtension.class); _extensionClasses.put("x-deflate-frame",DeflateFrameExtension.class); } - + private final Acceptor _acceptor; private WebSocketBuffers _buffers; private int _maxIdleTime = 300000; @@ -90,7 +92,7 @@ public class WebSocketFactory { return _extensionClasses; } - + /** * Get the maxIdleTime. * @@ -161,7 +163,7 @@ public class WebSocketFactory /** * Set the initial maximum binary message size for a connection. This can be changed by * the application calling {@link WebSocket.Connection#setMaxBinaryMessageSize(int)}. - * @param maxTextMessageSize The default maximum binary message size (in bytes) for a connection + * @param maxBinaryMessageSize The default maximum binary message size (in bytes) for a connection */ public void setMaxBinaryMessageSize(int maxBinaryMessageSize) { @@ -201,7 +203,7 @@ public class WebSocketFactory while (tok.hasMoreTokens()) extensions_requested.add(tok.nextToken()); } - + final WebSocketConnection connection; final List extensions; switch (draft) @@ -215,16 +217,16 @@ public class WebSocketFactory extensions=Collections.emptyList(); connection = new WebSocketConnectionD06(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol); break; - case 7: - case 8: - case 9: - case 10: - case 11: - case 12: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: extensions= initExtensions(extensions_requested,8-WebSocketConnectionD12.OP_EXT_DATA, 16-WebSocketConnectionD13.OP_EXT_CTRL,3); connection = new WebSocketConnectionD12(websocket, endp, _buffers, http.getTimeStamp(), _maxIdleTime, protocol,extensions,draft); break; - case 13: + 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); break; @@ -275,7 +277,7 @@ public class WebSocketFactory response.sendError(HttpServletResponse.SC_FORBIDDEN); return false; } - + // Try each requested protocol WebSocket websocket = null; String protocol = request.getHeader("Sec-WebSocket-Protocol"); @@ -305,7 +307,7 @@ public class WebSocketFactory return false; } - + public List initExtensions(List requested,int maxDataOpcodes,int maxControlOpcodes,int maxReservedBits) { List extensions = new ArrayList(); @@ -321,9 +323,9 @@ public class WebSocketFactory String value=nv.hasMoreTokens()?nv.nextToken().trim():null; parameters.put(name,value); } - + Extension extension = newExtension(extName); - + if (extension==null) continue; @@ -349,9 +351,9 @@ public class WebSocketFactory { LOG.warn(e); } - + return null; } - - + + } diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD00.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD00.java index e74725dc5e9..d0f6f06db16 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD00.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD00.java @@ -149,8 +149,6 @@ public class WebSocketGeneratorD00 implements WebSocketGenerator { while (_buffer.space()==0) { - // TODO: in case the I/O system signals write ready, but when we attempt to write we cannot - // TODO: we should decrease the blockFor timeout instead of waiting again the whole timeout boolean ready = _endp.blockWritable(blockFor); if (!ready) throw new IOException("Write timeout"); diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD06.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD06.java index a181b8cc508..c511a5a2db4 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD06.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD06.java @@ -4,18 +4,16 @@ // 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 java.security.SecureRandom; -import java.util.Random; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.EndPoint; @@ -38,14 +36,14 @@ public class WebSocketGeneratorD06 implements WebSocketGenerator private int _m; private boolean _opsent; private final MaskGen _maskGen; - + public WebSocketGeneratorD06(WebSocketBuffers buffers, EndPoint endp) { _buffers=buffers; _endp=endp; _maskGen=null; } - + public WebSocketGeneratorD06(WebSocketBuffers buffers, EndPoint endp, MaskGen maskGen) { _buffers=buffers; @@ -56,22 +54,22 @@ public class WebSocketGeneratorD06 implements WebSocketGenerator public synchronized void addFrame(byte flags, byte opcode, byte[] content, int offset, int length) throws IOException { // System.err.printf("<< %s %s %s\n",TypeUtil.toHexString(flags),TypeUtil.toHexString(opcode),length); - + long blockFor=_endp.getMaxIdleTime(); - + if (_buffer==null) _buffer=(_maskGen!=null)?_buffers.getBuffer():_buffers.getDirectBuffer(); - + boolean last=WebSocketConnectionD06.isLastFrame(flags); opcode=(byte)(((0xf&flags)<<4)+0xf&opcode); - + int space=(_maskGen!=null)?14:10; - + do { opcode = _opsent?WebSocketConnectionD06.OP_CONTINUATION:opcode; _opsent=true; - + int payload=length; if (payload+space>_buffer.capacity()) { @@ -85,7 +83,7 @@ public class WebSocketGeneratorD06 implements WebSocketGenerator // ensure there is space for header if (_buffer.space() <= space) expelBuffer(blockFor); - + // write mask if ((_maskGen!=null)) { @@ -100,10 +98,10 @@ public class WebSocketGeneratorD06 implements WebSocketGenerator bufferPut(new byte[]{ opcode, (byte)0x7f, - (byte)((payload>>56)&0x7f), - (byte)((payload>>48)&0xff), - (byte)((payload>>40)&0xff), - (byte)((payload>>32)&0xff), + (byte)0, + (byte)0, + (byte)0, + (byte)0, (byte)((payload>>24)&0xff), (byte)((payload>>16)&0xff), (byte)((payload>>8)&0xff), @@ -129,7 +127,7 @@ public class WebSocketGeneratorD06 implements WebSocketGenerator { _buffer.compact(); int chunk = remaining < _buffer.space() ? remaining : _buffer.space(); - + if ((_maskGen!=null)) { for (int i=0;i 0) { @@ -169,7 +167,7 @@ public class WebSocketGeneratorD06 implements WebSocketGenerator data[i]^=_mask[+_m++%4]; _buffer.put(data); } - + private synchronized void bufferPut(byte data) throws IOException { _buffer.put((byte)(data^_mask[+_m++%4])); @@ -212,8 +210,6 @@ public class WebSocketGeneratorD06 implements WebSocketGenerator { while (_buffer.space()==0) { - // TODO: in case the I/O system signals write ready, but when we attempt to write we cannot - // TODO: we should decrease the blockFor timeout instead of waiting again the whole timeout boolean ready = _endp.blockWritable(blockFor); if (!ready) throw new IOException("Write timeout"); diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD12.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD12.java index e0d510c5e92..12731393652 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD12.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD12.java @@ -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.websocket; @@ -43,7 +43,7 @@ public class WebSocketGeneratorD12 implements WebSocketGenerator _endp=endp; _maskGen=null; } - + public WebSocketGeneratorD12(WebSocketBuffers buffers, EndPoint endp, MaskGen maskGen) { _buffers=buffers; @@ -55,27 +55,26 @@ public class WebSocketGeneratorD12 implements WebSocketGenerator { return _buffer; } - + public synchronized void addFrame(byte flags, byte opcode, byte[] content, int offset, int length) throws IOException { // System.err.printf("<< %s %s %s\n",TypeUtil.toHexString(flags),TypeUtil.toHexString(opcode),length); - + boolean mask=_maskGen!=null; - + if (_buffer==null) _buffer=mask?_buffers.getBuffer():_buffers.getDirectBuffer(); - + boolean last=WebSocketConnectionD12.isLastFrame(flags); - byte orig=opcode; - + int space=mask?14:10; - + do { opcode = _opsent?WebSocketConnectionD12.OP_CONTINUATION:opcode; opcode=(byte)(((0xf&flags)<<4)+(0xf&opcode)); _opsent=true; - + int payload=length; if (payload+space>_buffer.capacity()) { @@ -85,7 +84,7 @@ public class WebSocketGeneratorD12 implements WebSocketGenerator } else if (last) opcode= (byte)(opcode|0x80); // Set the FIN bit - + // ensure there is space for header if (_buffer.space() <= space) { @@ -93,17 +92,17 @@ public class WebSocketGeneratorD12 implements WebSocketGenerator if (_buffer.space() <= space) flush(); } - + // write the opcode and length if (payload>0xffff) { _buffer.put(new byte[]{ opcode, mask?(byte)0xff:(byte)0x7f, - (byte)((payload>>56)&0x7f), - (byte)((payload>>48)&0xff), - (byte)((payload>>40)&0xff), - (byte)((payload>>32)&0xff), + (byte)0, + (byte)0, + (byte)0, + (byte)0, (byte)((payload>>24)&0xff), (byte)((payload>>16)&0xff), (byte)((payload>>8)&0xff), @@ -119,7 +118,7 @@ public class WebSocketGeneratorD12 implements WebSocketGenerator } else { - _buffer.put(new byte[]{ + _buffer.put(new byte[]{ opcode, (byte)(mask?(0x80|payload):payload)}); } @@ -132,14 +131,14 @@ public class WebSocketGeneratorD12 implements WebSocketGenerator _buffer.put(_mask); } - + // write payload int remaining = payload; while (remaining > 0) { _buffer.compact(); int chunk = remaining < _buffer.space() ? remaining : _buffer.space(); - + if (mask) { for (int i=0;i 0) { @@ -170,7 +169,7 @@ public class WebSocketGeneratorD12 implements WebSocketGenerator } while (length>0); _opsent=!last; - + if (_buffer!=null && _buffer.length()==0) { _buffers.returnBuffer(_buffer); @@ -194,7 +193,7 @@ public class WebSocketGeneratorD12 implements WebSocketGenerator if (_buffer==null) return 0; int result = flushBuffer(); - + if (!_endp.isBlocking()) { long now = System.currentTimeMillis(); diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD13.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD13.java index 003d711ca99..2f923520fe7 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD13.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketGeneratorD13.java @@ -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.websocket; @@ -43,7 +43,7 @@ public class WebSocketGeneratorD13 implements WebSocketGenerator _endp=endp; _maskGen=null; } - + public WebSocketGeneratorD13(WebSocketBuffers buffers, EndPoint endp, MaskGen maskGen) { _buffers=buffers; @@ -55,27 +55,26 @@ public class WebSocketGeneratorD13 implements WebSocketGenerator { return _buffer; } - + public synchronized void addFrame(byte flags, byte opcode, byte[] content, int offset, int length) throws IOException { // System.err.printf("<< %s %s %s\n",TypeUtil.toHexString(flags),TypeUtil.toHexString(opcode),length); - + boolean mask=_maskGen!=null; - + if (_buffer==null) _buffer=mask?_buffers.getBuffer():_buffers.getDirectBuffer(); - + boolean last=WebSocketConnectionD13.isLastFrame(flags); - byte orig=opcode; - + int space=mask?14:10; - + do { opcode = _opsent?WebSocketConnectionD13.OP_CONTINUATION:opcode; opcode=(byte)(((0xf&flags)<<4)+(0xf&opcode)); _opsent=true; - + int payload=length; if (payload+space>_buffer.capacity()) { @@ -85,7 +84,7 @@ public class WebSocketGeneratorD13 implements WebSocketGenerator } else if (last) opcode= (byte)(opcode|0x80); // Set the FIN bit - + // ensure there is space for header if (_buffer.space() <= space) { @@ -93,17 +92,17 @@ public class WebSocketGeneratorD13 implements WebSocketGenerator if (_buffer.space() <= space) flush(); } - + // write the opcode and length if (payload>0xffff) { _buffer.put(new byte[]{ opcode, mask?(byte)0xff:(byte)0x7f, - (byte)((payload>>56)&0x7f), - (byte)((payload>>48)&0xff), - (byte)((payload>>40)&0xff), - (byte)((payload>>32)&0xff), + (byte)0, + (byte)0, + (byte)0, + (byte)0, (byte)((payload>>24)&0xff), (byte)((payload>>16)&0xff), (byte)((payload>>8)&0xff), @@ -119,7 +118,7 @@ public class WebSocketGeneratorD13 implements WebSocketGenerator } else { - _buffer.put(new byte[]{ + _buffer.put(new byte[]{ opcode, (byte)(mask?(0x80|payload):payload)}); } @@ -132,14 +131,14 @@ public class WebSocketGeneratorD13 implements WebSocketGenerator _buffer.put(_mask); } - + // write payload int remaining = payload; while (remaining > 0) { _buffer.compact(); int chunk = remaining < _buffer.space() ? remaining : _buffer.space(); - + if (mask) { for (int i=0;i 0) { @@ -170,7 +169,7 @@ public class WebSocketGeneratorD13 implements WebSocketGenerator } while (length>0); _opsent=!last; - + if (_buffer!=null && _buffer.length()==0) { _buffers.returnBuffer(_buffer); @@ -194,7 +193,7 @@ public class WebSocketGeneratorD13 implements WebSocketGenerator if (_buffer==null) return 0; int result = flushBuffer(); - + if (!_endp.isBlocking()) { long now = System.currentTimeMillis(); diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD00.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD00.java index 10582a5b996..f277f754a3d 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD00.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD00.java @@ -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.websocket; @@ -51,8 +51,8 @@ public class WebSocketParserD00 implements WebSocketParser * @param buffers The buffers to use for parsing. Only the {@link Buffers#getBuffer()} is used. * This should be a direct buffer if binary data is mostly used or an indirect buffer if utf-8 data * is mostly used. - * @param endp - * @param handler + * @param endp the endpoint + * @param handler the handler to notify when a parse event occurs */ public WebSocketParserD00(WebSocketBuffers buffers, EndPoint endp, FrameHandler handler) { @@ -89,7 +89,6 @@ public class WebSocketParserD00 implements WebSocketParser int total_filled=0; // Loop until an datagram call back or can't fill anymore - boolean progress=true; while(true) { int length=_buffer.length(); diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD06.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD06.java index 38a923a2cbd..6436663454e 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD06.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD06.java @@ -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.websocket; @@ -31,9 +31,9 @@ import org.eclipse.jetty.util.log.Logger; public class WebSocketParserD06 implements WebSocketParser { private static final Logger LOG = Log.getLogger(WebSocketParserD06.class); - - public enum State { - + + public enum State { + START(0), MASK(4), OPCODE(1), LENGTH_7(1), LENGTH_16(2), LENGTH_63(8), DATA(0), SKIP(1); int _needs; @@ -47,7 +47,7 @@ public class WebSocketParserD06 implements WebSocketParser { return _needs; } - }; + } private final WebSocketBuffers _buffers; @@ -68,8 +68,9 @@ public class WebSocketParserD06 implements WebSocketParser * @param buffers The buffers to use for parsing. Only the {@link Buffers#getBuffer()} is used. * This should be a direct buffer if binary data is mostly used or an indirect buffer if utf-8 data * is mostly used. - * @param endp - * @param handler + * @param endp the endpoint + * @param handler the handler to notify when a parse event occurs + * @param masked whether masking should be handled */ public WebSocketParserD06(WebSocketBuffers buffers, EndPoint endp, FrameHandler handler, boolean masked) { @@ -140,7 +141,7 @@ public class WebSocketParserD06 implements WebSocketParser } // 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; while (_state!=State.DATA && available>=(_state==State.SKIP?1:_bytesNeeded)) @@ -151,7 +152,7 @@ public class WebSocketParserD06 implements WebSocketParser _state=_masked?State.MASK:State.OPCODE; _bytesNeeded=_state.getNeeds(); continue; - + case MASK: _buffer.get(_mask,0,4); available-=4; @@ -159,7 +160,7 @@ public class WebSocketParserD06 implements WebSocketParser _bytesNeeded=_state.getNeeds(); _m=0; continue; - + case OPCODE: b=_buffer.get(); available--; @@ -167,7 +168,7 @@ public class WebSocketParserD06 implements WebSocketParser b^=_mask[_m++%4]; _opcode=(byte)(b&0xf); _flags=(byte)(0xf&(b>>4)); - + if (WebSocketConnectionD06.isControlFrame(_opcode)&&!WebSocketConnectionD06.isLastFrame(_flags)) { _state=State.SKIP; @@ -200,7 +201,7 @@ public class WebSocketParserD06 implements WebSocketParser default: _length=(0x7f&b); _bytesNeeded=(int)_length; - _state=State.DATA; + _state=State.DATA; } continue; @@ -247,7 +248,7 @@ public class WebSocketParserD06 implements WebSocketParser } } continue; - + case SKIP: int skip=Math.min(available,_bytesNeeded); _buffer.skip(skip); @@ -255,7 +256,7 @@ public class WebSocketParserD06 implements WebSocketParser _bytesNeeded-=skip; if (_bytesNeeded==0) _state=State.START; - + } } diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD12.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD12.java index 1ab7f982de8..ba8c8bf8bdc 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD12.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD12.java @@ -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.websocket; @@ -31,9 +31,9 @@ import org.eclipse.jetty.util.log.Logger; public class WebSocketParserD12 implements WebSocketParser { private static final Logger LOG = Log.getLogger(WebSocketParserD12.class); - - public enum State { - + + public enum State { + START(0), OPCODE(1), LENGTH_7(1), LENGTH_16(2), LENGTH_63(8), MASK(4), PAYLOAD(0), DATA(0), SKIP(1); int _needs; @@ -47,7 +47,7 @@ public class WebSocketParserD12 implements WebSocketParser { return _needs; } - }; + } private final WebSocketBuffers _buffers; private final EndPoint _endp; @@ -70,8 +70,9 @@ public class WebSocketParserD12 implements WebSocketParser * @param buffers The buffers to use for parsing. Only the {@link Buffers#getBuffer()} is used. * This should be a direct buffer if binary data is mostly used or an indirect buffer if utf-8 data * is mostly used. - * @param endp - * @param handler + * @param endp the endpoint + * @param handler the handler to notify when a parse event occurs + * @param shouldBeMasked whether masking should be handled */ public WebSocketParserD12(WebSocketBuffers buffers, EndPoint endp, FrameHandler handler, boolean shouldBeMasked) { @@ -160,10 +161,10 @@ public class WebSocketParserD12 implements WebSocketParser events++; _bytesNeeded-=data.length(); _handler.onFrame((byte)(_flags&(0xff^WebSocketConnectionD12.FLAG_FIN)), _opcode, data); - + _opcode=WebSocketConnectionD12.OP_CONTINUATION; } - + if (_buffer.space() == 0) throw new IllegalStateException("FULL: "+_state+" "+_bytesNeeded+">"+_buffer.capacity()); } @@ -185,7 +186,7 @@ public class WebSocketParserD12 implements WebSocketParser } // 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; while (_state!=State.DATA && available>=(_state==State.SKIP?1:_bytesNeeded)) @@ -197,13 +198,13 @@ public class WebSocketParserD12 implements WebSocketParser _state=State.OPCODE; _bytesNeeded=_state.getNeeds(); continue; - + case OPCODE: b=_buffer.get(); available--; _opcode=(byte)(b&0xf); _flags=(byte)(0xf&(b>>4)); - + if (WebSocketConnectionD12.isControlFrame(_opcode)&&!WebSocketConnectionD12.isLastFrame(_flags)) { events++; @@ -222,7 +223,7 @@ public class WebSocketParserD12 implements WebSocketParser available--; _masked=(b&0x80)!=0; b=(byte)(0x7f&b); - + switch(b) { case 0x7f: @@ -235,7 +236,7 @@ public class WebSocketParserD12 implements WebSocketParser break; default: _length=(0x7f&b); - _state=_masked?State.MASK:State.PAYLOAD; + _state=_masked?State.MASK:State.PAYLOAD; } _bytesNeeded=_state.getNeeds(); continue; @@ -253,7 +254,7 @@ public class WebSocketParserD12 implements WebSocketParser _skip=true; } - _state=_masked?State.MASK:State.PAYLOAD; + _state=_masked?State.MASK:State.PAYLOAD; _bytesNeeded=_state.getNeeds(); } continue; @@ -272,7 +273,7 @@ public class WebSocketParserD12 implements WebSocketParser _skip=true; } - _state=_masked?State.MASK:State.PAYLOAD; + _state=_masked?State.MASK:State.PAYLOAD; _bytesNeeded=_state.getNeeds(); } continue; @@ -289,10 +290,10 @@ public class WebSocketParserD12 implements WebSocketParser _bytesNeeded=(int)_length; _state=_skip?State.SKIP:State.DATA; break; - + case DATA: break; - + case SKIP: int skip=Math.min(available,_bytesNeeded); _buffer.skip(skip); @@ -300,7 +301,7 @@ public class WebSocketParserD12 implements WebSocketParser _bytesNeeded-=skip; if (_bytesNeeded==0) _state=State.START; - + } } @@ -345,12 +346,12 @@ public class WebSocketParserD12 implements WebSocketParser { if (_buffer==null) _buffer=_buffers.getBuffer(); - + _buffer.put(buffer); buffer.clear(); } } - + /* ------------------------------------------------------------ */ public void returnBuffer() { @@ -360,7 +361,7 @@ public class WebSocketParserD12 implements WebSocketParser _buffer=null; } } - + /* ------------------------------------------------------------ */ @Override public String toString() diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD13.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD13.java index 8e485b329f2..0be16ce562b 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD13.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketParserD13.java @@ -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.websocket; @@ -31,9 +31,9 @@ import org.eclipse.jetty.util.log.Logger; public class WebSocketParserD13 implements WebSocketParser { private static final Logger LOG = Log.getLogger(WebSocketParserD13.class); - - public enum State { - + + public enum State { + START(0), OPCODE(1), LENGTH_7(1), LENGTH_16(2), LENGTH_63(8), MASK(4), PAYLOAD(0), DATA(0), SKIP(1); int _needs; @@ -47,7 +47,7 @@ public class WebSocketParserD13 implements WebSocketParser { return _needs; } - }; + } private final WebSocketBuffers _buffers; private final EndPoint _endp; @@ -70,8 +70,9 @@ public class WebSocketParserD13 implements WebSocketParser * @param buffers The buffers to use for parsing. Only the {@link Buffers#getBuffer()} is used. * This should be a direct buffer if binary data is mostly used or an indirect buffer if utf-8 data * is mostly used. - * @param endp - * @param handler + * @param endp the endpoint + * @param handler the handler to notify when a parse event occurs + * @param shouldBeMasked whether masking should be handled */ public WebSocketParserD13(WebSocketBuffers buffers, EndPoint endp, FrameHandler handler, boolean shouldBeMasked) { @@ -160,10 +161,10 @@ public class WebSocketParserD13 implements WebSocketParser events++; _bytesNeeded-=data.length(); _handler.onFrame((byte)(_flags&(0xff^WebSocketConnectionD13.FLAG_FIN)), _opcode, data); - + _opcode=WebSocketConnectionD13.OP_CONTINUATION; } - + if (_buffer.space() == 0) throw new IllegalStateException("FULL: "+_state+" "+_bytesNeeded+">"+_buffer.capacity()); } @@ -185,7 +186,7 @@ public class WebSocketParserD13 implements WebSocketParser } // 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; while (_state!=State.DATA && available>=(_state==State.SKIP?1:_bytesNeeded)) @@ -197,13 +198,13 @@ public class WebSocketParserD13 implements WebSocketParser _state=State.OPCODE; _bytesNeeded=_state.getNeeds(); continue; - + case OPCODE: b=_buffer.get(); available--; _opcode=(byte)(b&0xf); _flags=(byte)(0xf&(b>>4)); - + if (WebSocketConnectionD13.isControlFrame(_opcode)&&!WebSocketConnectionD13.isLastFrame(_flags)) { events++; @@ -222,7 +223,7 @@ public class WebSocketParserD13 implements WebSocketParser available--; _masked=(b&0x80)!=0; b=(byte)(0x7f&b); - + switch(b) { case 0x7f: @@ -235,7 +236,7 @@ public class WebSocketParserD13 implements WebSocketParser break; default: _length=(0x7f&b); - _state=_masked?State.MASK:State.PAYLOAD; + _state=_masked?State.MASK:State.PAYLOAD; } _bytesNeeded=_state.getNeeds(); continue; @@ -253,7 +254,7 @@ public class WebSocketParserD13 implements WebSocketParser _skip=true; } - _state=_masked?State.MASK:State.PAYLOAD; + _state=_masked?State.MASK:State.PAYLOAD; _bytesNeeded=_state.getNeeds(); } continue; @@ -272,7 +273,7 @@ public class WebSocketParserD13 implements WebSocketParser _skip=true; } - _state=_masked?State.MASK:State.PAYLOAD; + _state=_masked?State.MASK:State.PAYLOAD; _bytesNeeded=_state.getNeeds(); } continue; @@ -289,10 +290,10 @@ public class WebSocketParserD13 implements WebSocketParser _bytesNeeded=(int)_length; _state=_skip?State.SKIP:State.DATA; break; - + case DATA: break; - + case SKIP: int skip=Math.min(available,_bytesNeeded); _buffer.skip(skip); @@ -300,7 +301,7 @@ public class WebSocketParserD13 implements WebSocketParser _bytesNeeded-=skip; if (_bytesNeeded==0) _state=State.START; - + } } @@ -345,12 +346,12 @@ public class WebSocketParserD13 implements WebSocketParser { if (_buffer==null) _buffer=_buffers.getBuffer(); - + _buffer.put(buffer); buffer.clear(); } } - + /* ------------------------------------------------------------ */ public void returnBuffer() { @@ -360,7 +361,7 @@ public class WebSocketParserD13 implements WebSocketParser _buffer=null; } } - + /* ------------------------------------------------------------ */ @Override public String toString() diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/TestClient.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/TestClient.java similarity index 100% rename from jetty-websocket/src/main/java/org/eclipse/jetty/websocket/TestClient.java rename to jetty-websocket/src/test/java/org/eclipse/jetty/websocket/TestClient.java diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/TestServer.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/TestServer.java similarity index 100% rename from jetty-websocket/src/main/java/org/eclipse/jetty/websocket/TestServer.java rename to jetty-websocket/src/test/java/org/eclipse/jetty/websocket/TestServer.java diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketClientTest.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketClientTest.java index 10d3e5c7072..0e65c889941 100644 --- a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketClientTest.java +++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketClientTest.java @@ -80,7 +80,7 @@ public class WebSocketClientTest { } }; - Future future = client.open(new URI("ws://127.0.0.1:" + _serverPort + "/"), websocket); + client.open(new URI("ws://127.0.0.1:" + _serverPort + "/"), websocket); Socket socket = _server.accept(); accept(socket); diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketMessageD13Test.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketMessageD13Test.java index 059fbfc6fca..6c4e8ae253a 100644 --- a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketMessageD13Test.java +++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketMessageD13Test.java @@ -909,6 +909,98 @@ public class WebSocketMessageD13Test lookFor("Message size > 15",input); } + @Test + public void testCloseCode() throws Exception + { + Socket socket = new Socket("localhost", __connector.getLocalPort()); + OutputStream output = socket.getOutputStream(); + output.write( + ("GET /chat HTTP/1.1\r\n"+ + "Host: server.example.com\r\n"+ + "Upgrade: websocket\r\n"+ + "Connection: Upgrade\r\n"+ + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"+ + "Sec-WebSocket-Origin: http://example.com\r\n"+ + "Sec-WebSocket-Protocol: chat\r\n" + + "Sec-WebSocket-Version: "+WebSocketConnectionD13.VERSION+"\r\n"+ + "\r\n").getBytes("ISO-8859-1")); + output.flush(); + + socket.setSoTimeout(100000); + InputStream input = socket.getInputStream(); + + lookFor("HTTP/1.1 101 Switching Protocols\r\n",input); + skipTo("Sec-WebSocket-Accept: ",input); + lookFor("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",input); + skipTo("\r\n\r\n",input); + + assertTrue(__serverWebSocket.awaitConnected(1000)); + assertNotNull(__serverWebSocket.connection); + + __serverWebSocket.getConnection().setMaxBinaryMessageSize(15); + + output.write(0x88); + output.write(0x82); + output.write(0x00); + output.write(0x00); + output.write(0x00); + output.write(0x00); + output.write(0x81); + output.write(0xFF); + output.flush(); + + assertEquals(0x80|WebSocketConnectionD13.OP_CLOSE,input.read()); + assertEquals(2,input.read()); + int code=(0xff&input.read())*0x100+(0xff&input.read()); + assertEquals(0x81FF,code); + } + + @Test + public void testNotUTF8() throws Exception + { + Socket socket = new Socket("localhost", __connector.getLocalPort()); + OutputStream output = socket.getOutputStream(); + output.write( + ("GET /chat HTTP/1.1\r\n"+ + "Host: server.example.com\r\n"+ + "Upgrade: websocket\r\n"+ + "Connection: Upgrade\r\n"+ + "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"+ + "Sec-WebSocket-Origin: http://example.com\r\n"+ + "Sec-WebSocket-Protocol: chat\r\n" + + "Sec-WebSocket-Version: "+WebSocketConnectionD13.VERSION+"\r\n"+ + "\r\n").getBytes("ISO-8859-1")); + output.flush(); + + socket.setSoTimeout(100000); + InputStream input = socket.getInputStream(); + + lookFor("HTTP/1.1 101 Switching Protocols\r\n",input); + skipTo("Sec-WebSocket-Accept: ",input); + lookFor("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",input); + skipTo("\r\n\r\n",input); + + assertTrue(__serverWebSocket.awaitConnected(1000)); + assertNotNull(__serverWebSocket.connection); + + __serverWebSocket.getConnection().setMaxBinaryMessageSize(15); + + output.write(0x81); + output.write(0x82); + output.write(0x00); + output.write(0x00); + output.write(0x00); + output.write(0x00); + output.write(0xc3); + output.write(0x28); + output.flush(); + + assertEquals(0x80|WebSocketConnectionD13.OP_CLOSE,input.read()); + assertEquals(15,input.read()); + int code=(0xff&input.read())*0x100+(0xff&input.read()); + assertEquals(WebSocketConnectionD13.CLOSE_NOT_UTF8,code); + lookFor("Invalid UTF-8",input); + } @Test public void testMaxBinarySize2() throws Exception diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketParserD00Test.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketParserD00Test.java index bfdcf1bae35..efa70d257eb 100644 --- a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketParserD00Test.java +++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketParserD00Test.java @@ -130,13 +130,6 @@ public class WebSocketParserD00Test } - // TODO test: - // blocking, - // async - // full - // EOF - // errors - private class Handler implements WebSocketParser.FrameHandler { public List _data = new ArrayList(); diff --git a/test-continuation/src/main/java/org/eclipse/jetty/continuation/test/ContinuationBase.java b/test-continuation/src/main/java/org/eclipse/jetty/continuation/test/ContinuationBase.java index 81c9de27a26..2049dc1fc07 100644 --- a/test-continuation/src/main/java/org/eclipse/jetty/continuation/test/ContinuationBase.java +++ b/test-continuation/src/main/java/org/eclipse/jetty/continuation/test/ContinuationBase.java @@ -242,6 +242,7 @@ public abstract class ContinuationBase extends TestCase try { Socket socket = new Socket("localhost",port); + socket.setSoTimeout(10000); socket.getOutputStream().write(request.getBytes("UTF-8")); response = toString(socket.getInputStream()); diff --git a/test-jetty-webapp/pom.xml b/test-jetty-webapp/pom.xml index e6f930b0e80..bedb7eb4e0d 100644 --- a/test-jetty-webapp/pom.xml +++ b/test-jetty-webapp/pom.xml @@ -173,7 +173,7 @@ org.eclipse.jetty jetty-websocket ${project.version} - provided + org.eclipse.jetty diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/HashTestServer.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/HashTestServer.java index 6ddbf791e07..720d90f3562 100644 --- a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/HashTestServer.java +++ b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/HashTestServer.java @@ -36,7 +36,7 @@ public class HashTestServer extends AbstractTestServer } - public SessionIdManager newSessionIdManager() + public SessionIdManager newSessionIdManager(String config) { return new HashSessionIdManager(); } diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java index c602cc7382a..6e6cb0fa35b 100644 --- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java +++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/JdbcTestServer.java @@ -22,22 +22,29 @@ import org.eclipse.jetty.toolchain.test.MavenTestingUtils; public class JdbcTestServer extends AbstractTestServer { public static final String DRIVER_CLASS = "org.apache.derby.jdbc.EmbeddedDriver"; - public static final String CONNECTION_URL = "jdbc:derby:sessions;create=true"; + public static final String DEFAULT_CONNECTION_URL = "jdbc:derby:sessions;create=true"; public static final int SAVE_INTERVAL = 1; + static { System.setProperty("derby.system.home", MavenTestingUtils.getTargetFile("test-derby").getAbsolutePath()); } + public JdbcTestServer(int port) { super(port); } - + + public JdbcTestServer(int port, int maxInactivePeriod, int scavengePeriod, String connectionUrl) + { + super(port, maxInactivePeriod, scavengePeriod, connectionUrl); + } + public JdbcTestServer(int port, int maxInactivePeriod, int scavengePeriod) { - super(port, maxInactivePeriod, scavengePeriod); + super(port, maxInactivePeriod, scavengePeriod, DEFAULT_CONNECTION_URL); } public JdbcTestServer (int port, boolean optimize) @@ -60,14 +67,14 @@ public class JdbcTestServer extends AbstractTestServer * @see org.eclipse.jetty.server.session.AbstractTestServer#newSessionIdManager() */ @Override - public SessionIdManager newSessionIdManager() + public SessionIdManager newSessionIdManager(String config) { synchronized(JdbcTestServer.class) { JDBCSessionIdManager idManager = new JDBCSessionIdManager(_server); idManager.setScavengeInterval(_scavengePeriod); idManager.setWorkerName("w"+(__workers++)); - idManager.setDriverInfo(DRIVER_CLASS, CONNECTION_URL); + idManager.setDriverInfo(DRIVER_CLASS, (config==null?DEFAULT_CONNECTION_URL:config)); return idManager; } } diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java index a4329716488..7742d16d411 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java @@ -3,12 +3,16 @@ package org.eclipse.jetty.server.session; import static org.junit.Assert.*; import java.io.IOException; +import java.util.EventListener; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpSessionActivationListener; +import javax.servlet.http.HttpSessionEvent; +import javax.servlet.http.HttpSessionListener; import org.eclipse.jetty.client.ContentExchange; import org.eclipse.jetty.client.HttpClient; @@ -30,6 +34,8 @@ public abstract class AbstractRemoveSessionTest AbstractTestServer server = createServer(0, 1, scavengePeriod); ServletContextHandler context = server.addContext(contextPath); context.addServlet(TestServlet.class, servletMapping); + TestEventListener testListener = new TestEventListener(); + context.getSessionHandler().addEventListener(testListener); server.start(); int port = server.getPort(); try @@ -49,6 +55,8 @@ public abstract class AbstractRemoveSessionTest assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); + //ensure sessionCreated listener is called + assertTrue (testListener.isCreated()); //now delete the session exchange = new ContentExchange(true); @@ -58,6 +66,8 @@ public abstract class AbstractRemoveSessionTest client.send(exchange); exchange.waitForDone(); assertEquals(HttpServletResponse.SC_OK,exchange.getResponseStatus()); + //ensure sessionDestroyed listener is called + assertTrue(testListener.isDestroyed()); // The session is not there anymore, but we present an old cookie @@ -106,4 +116,32 @@ public abstract class AbstractRemoveSessionTest } } + public static class TestEventListener implements HttpSessionListener + { + boolean wasCreated; + boolean wasDestroyed; + + public void sessionCreated(HttpSessionEvent se) + { + wasCreated = true; + } + + public void sessionDestroyed(HttpSessionEvent se) + { + wasDestroyed = true; + } + + public boolean isDestroyed() + { + return wasDestroyed; + } + + + public boolean isCreated() + { + return wasCreated; + } + + } + } diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractTestServer.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractTestServer.java index 87cba0cb0fb..c2ee3ea4816 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractTestServer.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractTestServer.java @@ -34,22 +34,31 @@ public abstract class AbstractTestServer protected final ContextHandlerCollection _contexts; protected SessionIdManager _sessionIdManager; + + public AbstractTestServer(int port) { this(port, 30, 10); } public AbstractTestServer(int port, int maxInactivePeriod, int scavengePeriod) + { + this (port, maxInactivePeriod, scavengePeriod, null); + } + + public AbstractTestServer(int port, int maxInactivePeriod, int scavengePeriod, String sessionIdMgrConfig) { _server = new Server(port); _maxInactivePeriod = maxInactivePeriod; _scavengePeriod = scavengePeriod; _contexts = new ContextHandlerCollection(); - _sessionIdManager = newSessionIdManager(); + _sessionIdManager = newSessionIdManager(sessionIdMgrConfig); + _server.setSessionIdManager(_sessionIdManager); } + + - - public abstract SessionIdManager newSessionIdManager(); + public abstract SessionIdManager newSessionIdManager(String config); public abstract SessionManager newSessionManager(); public abstract SessionHandler newSessionHandler(SessionManager sessionManager);