Merge remote-tracking branch 'eclipse/jetty-10.0.x' into jetty-10.0.x-websocketmapping-refactor
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
commit
d37dfd4357
|
@ -30,7 +30,7 @@ public class JAASPrincipal implements Principal, Serializable
|
|||
{
|
||||
private static final long serialVersionUID = -5538962177019315479L;
|
||||
|
||||
private String _name = null;
|
||||
private final String _name;
|
||||
|
||||
public JAASPrincipal(String userName)
|
||||
{
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
source.. = src/main/java/
|
||||
output.. = target/classes/
|
||||
bin.includes = META-INF/,\
|
||||
.
|
||||
src.includes = META-INF/
|
|
@ -1,5 +0,0 @@
|
|||
source.. = src/main/java/
|
||||
output.. = target/classes/
|
||||
bin.includes = META-INF/,\
|
||||
.
|
||||
src.includes = META-INF/
|
|
@ -1,8 +0,0 @@
|
|||
source.. = src/main/java/
|
||||
output.. = target/classes/
|
||||
bin.includes = META-INF/,\
|
||||
.,\
|
||||
jettyhome/
|
||||
bin.excludes = jettyhome/logs/*.log,\
|
||||
jettyhome/lib/*
|
||||
src.includes = jettyhome/
|
|
@ -1,4 +0,0 @@
|
|||
source.. = src/main/java/
|
||||
output.. = target/classes/
|
||||
bin.includes = META-INF/,\
|
||||
.
|
|
@ -11,8 +11,9 @@
|
|||
<properties>
|
||||
<assembly-directory>target/distribution</assembly-directory>
|
||||
<bundle-symbolic-name>${project.groupId}.runner</bundle-symbolic-name>
|
||||
<it.debug>false</it.debug>
|
||||
</properties>
|
||||
<url>http://www.eclipse.org/jetty</url>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
@ -35,21 +36,38 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<configuration>
|
||||
<!-- jetty-runner is not an OSGi component -->
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<artifactId>maven-invoker-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>integration-test</id>
|
||||
<phase>integration-test</phase>
|
||||
<goals>
|
||||
<goal>install</goal>
|
||||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
|
||||
</archive>
|
||||
<javaHome>${java.home}</javaHome>
|
||||
<environmentVariables>
|
||||
<JAVA_HOME>${java.home}</JAVA_HOME>
|
||||
</environmentVariables>
|
||||
<debug>${it.debug}</debug>
|
||||
<projectsDirectory>src/it</projectsDirectory>
|
||||
<timeoutInSeconds>600</timeoutInSeconds>
|
||||
<cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
|
||||
<localRepositoryPath>${project.build.directory}/local-repo</localRepositoryPath>
|
||||
<settingsFile>src/it/settings.xml</settingsFile>
|
||||
<skipInvocation>${skipTests}</skipInvocation>
|
||||
<scriptVariables>
|
||||
<maven.dependency.plugin.version>${maven.dependency.plugin.version}</maven.dependency.plugin.version>
|
||||
</scriptVariables>
|
||||
<goals>
|
||||
<goal>clean</goal>
|
||||
</goals>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
@ -63,6 +81,28 @@
|
|||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.felix</groupId>
|
||||
<artifactId>maven-bundle-plugin</artifactId>
|
||||
<configuration>
|
||||
<!-- jetty-runner is not an OSGi component -->
|
||||
<skip>true</skip>
|
||||
<!-- there is no way to skip MANIFEST creation by the plugin so just configure dummy location -->
|
||||
<manifestLocation>${project.build.directory}/NON_USED_MANIFEST</manifestLocation>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestFile>src/main/resources/MANIFEST.MF</manifestFile>
|
||||
<manifest>
|
||||
<mainClass>org.eclipse.jetty.runner.Runner</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<settings>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>it-repo</id>
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>local.central</id>
|
||||
<url>@localRepositoryUrl@</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>local.central</id>
|
||||
<url>@localRepositoryUrl@</url>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
</releases>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</profile>
|
||||
</profiles>
|
||||
</settings>
|
|
@ -0,0 +1 @@
|
|||
invoker.goals = generate-resources
|
|
@ -0,0 +1,63 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
|
||||
<groupId>org.eclipse.jetty.its</groupId>
|
||||
<artifactId>jetty-runner-it-test</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<packaging>war</packaging>
|
||||
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-runner</artifactId>
|
||||
<version>@project.version@</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>@maven.dependency.plugin.version@</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-jetty-runner</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-runner</artifactId>
|
||||
<version>@project.version@</version>
|
||||
<type>jar</type>
|
||||
<overWrite>false</overWrite>
|
||||
<outputDirectory>${project.build.directory}/</outputDirectory>
|
||||
<destFileName>jetty-runner.jar</destFileName>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
<overWriteReleases>false</overWriteReleases>
|
||||
<overWriteSnapshots>true</overWriteSnapshots>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
</project>
|
|
@ -0,0 +1,10 @@
|
|||
import java.util.jar.*
|
||||
|
||||
File artifact = new File( basedir, "target/jetty-runner.jar" )
|
||||
assert artifact.exists()
|
||||
|
||||
JarFile jar = new JarFile( artifact );
|
||||
|
||||
Attributes manifest = jar.getManifest().getMainAttributes();
|
||||
|
||||
assert manifest.getValue( new Attributes.Name( "Main-Class" ) ).equals( "org.eclipse.jetty.runner.Runner" )
|
|
@ -0,0 +1 @@
|
|||
Comment: Jetty Runner
|
|
@ -128,7 +128,18 @@ public interface Callback extends Invocable
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
static Callback from(Runnable completed)
|
||||
{
|
||||
return new Completing()
|
||||
{
|
||||
public void completed()
|
||||
{
|
||||
completed.run();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class Completing implements Callback
|
||||
{
|
||||
@Override
|
||||
|
|
|
@ -18,24 +18,6 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.javax.common;
|
||||
|
||||
import org.eclipse.jetty.util.SharedBlockingCallback;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.core.ExtensionConfig;
|
||||
import org.eclipse.jetty.websocket.core.FrameHandler;
|
||||
import org.eclipse.jetty.websocket.javax.common.decoders.AvailableDecoders;
|
||||
import org.eclipse.jetty.websocket.javax.common.encoders.AvailableEncoders;
|
||||
import org.eclipse.jetty.websocket.javax.common.util.ReflectUtils;
|
||||
|
||||
import javax.websocket.CloseReason;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.Extension;
|
||||
import javax.websocket.MessageHandler;
|
||||
import javax.websocket.RemoteEndpoint.Async;
|
||||
import javax.websocket.RemoteEndpoint.Basic;
|
||||
import javax.websocket.Session;
|
||||
import javax.websocket.WebSocketContainer;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.security.Principal;
|
||||
|
@ -48,6 +30,25 @@ import java.util.Objects;
|
|||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.websocket.CloseReason;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.Extension;
|
||||
import javax.websocket.MessageHandler;
|
||||
import javax.websocket.RemoteEndpoint.Async;
|
||||
import javax.websocket.RemoteEndpoint.Basic;
|
||||
import javax.websocket.Session;
|
||||
import javax.websocket.WebSocketContainer;
|
||||
|
||||
import org.eclipse.jetty.util.SharedBlockingCallback;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.core.ExtensionConfig;
|
||||
import org.eclipse.jetty.websocket.core.FrameHandler;
|
||||
import org.eclipse.jetty.websocket.javax.common.decoders.AvailableDecoders;
|
||||
import org.eclipse.jetty.websocket.javax.common.encoders.AvailableEncoders;
|
||||
import org.eclipse.jetty.websocket.javax.common.util.ReflectUtils;
|
||||
|
||||
/**
|
||||
* Client Session for the JSR.
|
||||
*/
|
||||
|
@ -535,7 +536,7 @@ public class JavaxWebSocketSession extends AbstractLifeCycle implements javax.we
|
|||
@Override
|
||||
public boolean isOpen()
|
||||
{
|
||||
return coreSession.isOpen();
|
||||
return coreSession.isOutputOpen();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,7 +32,14 @@ import org.eclipse.jetty.http.HttpVersion;
|
|||
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.server.*;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.LocalConnector;
|
||||
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.SslConnectionFactory;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
|
|
|
@ -18,6 +18,13 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.javax.tests.client.misbehaving;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.websocket.ContainerProvider;
|
||||
import javax.websocket.WebSocketContainer;
|
||||
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.eclipse.jetty.websocket.core.internal.WebSocketChannel;
|
||||
import org.eclipse.jetty.websocket.javax.tests.CoreServer;
|
||||
|
@ -25,12 +32,6 @@ import org.junit.jupiter.api.AfterAll;
|
|||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.websocket.ContainerProvider;
|
||||
import javax.websocket.WebSocketContainer;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
|
|
@ -38,7 +38,9 @@ import org.junit.jupiter.api.BeforeAll;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
/**
|
||||
* Tests a {@link javax.websocket.Decoder.TextStream} automatic decoding to a Socket onMessage parameter
|
||||
|
|
|
@ -25,7 +25,12 @@ import java.net.URI;
|
|||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.websocket.*;
|
||||
import javax.websocket.ContainerProvider;
|
||||
import javax.websocket.Endpoint;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.MessageHandler;
|
||||
import javax.websocket.Session;
|
||||
import javax.websocket.WebSocketContainer;
|
||||
import javax.websocket.server.ServerContainer;
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
|
||||
|
|
|
@ -31,7 +31,13 @@ import javax.servlet.ServletException;
|
|||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.websocket.*;
|
||||
import javax.websocket.ContainerProvider;
|
||||
import javax.websocket.Endpoint;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.OnError;
|
||||
import javax.websocket.OnMessage;
|
||||
import javax.websocket.Session;
|
||||
import javax.websocket.WebSocketContainer;
|
||||
import javax.websocket.server.ServerEndpoint;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
|
|
|
@ -18,6 +18,11 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.common;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketAddress;
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
import org.eclipse.jetty.websocket.api.CloseStatus;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
|
@ -27,11 +32,6 @@ import org.eclipse.jetty.websocket.api.UpgradeResponse;
|
|||
import org.eclipse.jetty.websocket.api.WebSocketBehavior;
|
||||
import org.eclipse.jetty.websocket.core.FrameHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketAddress;
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
|
||||
public class WebSocketSessionImpl implements Session, Dumpable
|
||||
{
|
||||
private final FrameHandler.CoreSession coreSession;
|
||||
|
@ -160,7 +160,7 @@ public class WebSocketSessionImpl implements Session, Dumpable
|
|||
@Override
|
||||
public boolean isOpen()
|
||||
{
|
||||
return remoteEndpoint.getCoreSession().isOpen();
|
||||
return remoteEndpoint.getCoreSession().isOutputOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,14 +18,13 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.core;
|
||||
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Utf8Appendable;
|
||||
import org.eclipse.jetty.util.Utf8StringBuilder;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Utf8Appendable;
|
||||
import org.eclipse.jetty.util.Utf8StringBuilder;
|
||||
|
||||
/**
|
||||
* Representation of a WebSocket Close (status code & reason)
|
||||
|
@ -194,7 +193,7 @@ public class CloseStatus
|
|||
|
||||
int len = 2; // status code
|
||||
|
||||
byte reasonBytes[] = null;
|
||||
byte[] reasonBytes = null;
|
||||
|
||||
if (reason != null)
|
||||
{
|
||||
|
@ -208,7 +207,7 @@ public class CloseStatus
|
|||
ByteBuffer buf = BufferUtil.allocate(len);
|
||||
BufferUtil.flipToFill(buf);
|
||||
buf.put((byte)((statusCode >>> 8) & 0xFF));
|
||||
buf.put((byte)((statusCode >>> 0) & 0xFF));
|
||||
buf.put((byte)(statusCode & 0xFF));
|
||||
|
||||
if ((reasonBytes != null) && (reasonBytes.length > 0))
|
||||
{
|
||||
|
|
|
@ -270,7 +270,7 @@ public interface FrameHandler extends IncomingFrames
|
|||
/**
|
||||
* @return True if the websocket is open outbound
|
||||
*/
|
||||
boolean isOpen();
|
||||
boolean isOutputOpen();
|
||||
|
||||
/**
|
||||
* If using BatchMode.ON or BatchMode.AUTO, trigger a flush of enqueued / batched frames.
|
||||
|
@ -374,7 +374,7 @@ public interface FrameHandler extends IncomingFrames
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen()
|
||||
public boolean isOutputOpen()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -19,18 +19,15 @@
|
|||
package org.eclipse.jetty.websocket.core.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Queue;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.DecoratedObjectFactory;
|
||||
import org.eclipse.jetty.util.IteratingCallback;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
|
@ -201,7 +198,7 @@ public class ExtensionStack implements IncomingFrames, OutgoingFrames, Dumpable
|
|||
outgoing.sendFrame(frame, callback, batch);
|
||||
}
|
||||
|
||||
public void connect(IncomingFrames incoming, OutgoingFrames outgoing, WebSocketChannel webSocketChannel)
|
||||
public void initialize(IncomingFrames incoming, OutgoingFrames outgoing, WebSocketChannel webSocketChannel)
|
||||
{
|
||||
if (extensions == null)
|
||||
throw new IllegalStateException();
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.core.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
|
@ -74,6 +75,18 @@ public class FrameFlusher extends IteratingCallback
|
|||
}
|
||||
}
|
||||
|
||||
public void onClose()
|
||||
{
|
||||
Throwable cause = null;
|
||||
synchronized (this)
|
||||
{
|
||||
if (!queue.isEmpty())
|
||||
cause = new IOException("Closed");
|
||||
}
|
||||
if (cause!=null)
|
||||
onCompleteFailure(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Action process() throws Throwable
|
||||
{
|
||||
|
|
|
@ -20,16 +20,19 @@ package org.eclipse.jetty.websocket.core.internal;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.IteratingCallback;
|
||||
import org.eclipse.jetty.util.Utf8Appendable;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
|
@ -62,6 +65,7 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
|||
private final FrameHandler handler;
|
||||
private final Negotiated negotiated;
|
||||
private final boolean demanding;
|
||||
private final Flusher flusher = new Flusher();
|
||||
|
||||
private WebSocketConnection connection;
|
||||
private boolean autoFragment = WebSocketConstants.DEFAULT_AUTO_FRAGMENT;
|
||||
|
@ -79,7 +83,7 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
|||
this.behavior = behavior;
|
||||
this.negotiated = negotiated;
|
||||
this.demanding = handler.isDemanding();
|
||||
negotiated.getExtensions().connect(new IncomingAdaptor(), new OutgoingAdaptor(), this);
|
||||
negotiated.getExtensions().initialize(new IncomingAdaptor(), new OutgoingAdaptor(), this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -153,7 +157,7 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
|||
if (frame.getOpCode() == OpCode.CLOSE)
|
||||
{
|
||||
if (!(frame instanceof ParsedFrame)) // already check in parser
|
||||
CloseStatus.getCloseStatus(frame);
|
||||
CloseStatus.getCloseStatus(frame); // return ignored as get used to validate there is a closeStatus
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -235,9 +239,14 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen()
|
||||
public boolean isOutputOpen()
|
||||
{
|
||||
return channelState.isOutOpen();
|
||||
return channelState.isOutputOpen();
|
||||
}
|
||||
|
||||
public boolean isClosed()
|
||||
{
|
||||
return channelState.isClosed();
|
||||
}
|
||||
|
||||
public void setWebSocketConnection(WebSocketConnection connection)
|
||||
|
@ -282,99 +291,94 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
|||
|
||||
public void onClosed(Throwable cause)
|
||||
{
|
||||
onClosed(cause, new CloseStatus(CloseStatus.NO_CLOSE, cause == null?null:cause.toString()));
|
||||
CloseStatus closeStatus = new CloseStatus(CloseStatus.NO_CLOSE, cause == null?null:cause.toString());
|
||||
if (channelState.onClosed(closeStatus))
|
||||
closeConnection(cause, closeStatus);
|
||||
}
|
||||
|
||||
public void onClosed(Throwable cause, CloseStatus closeStatus)
|
||||
public void closeConnection(Throwable cause, CloseStatus closeStatus)
|
||||
{
|
||||
if (channelState.onClosed(closeStatus))
|
||||
{
|
||||
connection.cancelDemand();
|
||||
connection.cancelDemand();
|
||||
|
||||
// Forward Errors to Local WebSocket EndPoint
|
||||
// Forward Errors to Local WebSocket EndPoint
|
||||
if (cause!=null)
|
||||
{
|
||||
try
|
||||
{
|
||||
handler.onError(cause);
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
cause.addSuppressed(e);
|
||||
if (e != cause)
|
||||
cause.addSuppressed(e);
|
||||
LOG.warn(cause);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
handler.onClosed(closeStatus);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an Error event seen by the Session and/or Connection
|
||||
*
|
||||
* @param cause the cause
|
||||
*/
|
||||
public void processError(Throwable cause)
|
||||
{
|
||||
CloseStatus closeStatus;
|
||||
|
||||
if (cause instanceof Utf8Appendable.NotUtf8Exception)
|
||||
{
|
||||
closeStatus = new CloseStatus(CloseStatus.BAD_PAYLOAD, cause.getMessage());
|
||||
}
|
||||
else if (cause instanceof SocketTimeoutException)
|
||||
{
|
||||
// A path often seen in Windows
|
||||
closeStatus = new CloseStatus(CloseStatus.SHUTDOWN, cause.getMessage());
|
||||
}
|
||||
else if (cause instanceof IOException)
|
||||
{
|
||||
closeStatus = new CloseStatus(CloseStatus.PROTOCOL, cause.getMessage());
|
||||
}
|
||||
else if (cause instanceof SocketException)
|
||||
{
|
||||
// A path unique to Unix
|
||||
closeStatus = new CloseStatus(CloseStatus.SHUTDOWN, cause.getMessage());
|
||||
}
|
||||
else if (cause instanceof CloseException)
|
||||
{
|
||||
CloseException ce = (CloseException)cause;
|
||||
closeStatus = new CloseStatus(ce.getStatusCode(), ce.getMessage());
|
||||
}
|
||||
else if (cause instanceof WebSocketTimeoutException)
|
||||
{
|
||||
closeStatus = new CloseStatus(CloseStatus.SHUTDOWN, cause.getMessage());
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.warn("Unhandled Error (closing connection)", cause);
|
||||
|
||||
// Exception on end-user WS-Endpoint.
|
||||
// Fast-fail & close connection with reason.
|
||||
int statusCode = CloseStatus.SERVER_ERROR;
|
||||
if (behavior == Behavior.CLIENT)
|
||||
statusCode = CloseStatus.POLICY_VIOLATION;
|
||||
|
||||
closeStatus = new CloseStatus(statusCode, cause.getMessage());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// TODO can we avoid the illegal state exception in outClosed
|
||||
close(closeStatus, Callback.NOOP, false);
|
||||
handler.onClosed(closeStatus);
|
||||
}
|
||||
catch (IllegalStateException e)
|
||||
catch (Throwable e)
|
||||
{
|
||||
if (cause == null)
|
||||
cause = e;
|
||||
else
|
||||
cause.addSuppressed(e);
|
||||
LOG.warn(e);
|
||||
}
|
||||
onClosed(cause, closeStatus);
|
||||
|
||||
if (connection.getEndPoint().isOpen())
|
||||
connection.close();
|
||||
}
|
||||
|
||||
AbnormalCloseStatus abnormalCloseStatusFor(Throwable cause)
|
||||
{
|
||||
int code;
|
||||
if (cause instanceof ProtocolException)
|
||||
code = CloseStatus.PROTOCOL;
|
||||
else if (cause instanceof CloseException)
|
||||
code = ((CloseException)cause).getStatusCode();
|
||||
else if (cause instanceof Utf8Appendable.NotUtf8Exception)
|
||||
code = CloseStatus.BAD_PAYLOAD;
|
||||
else if (cause instanceof WebSocketTimeoutException || cause instanceof TimeoutException || cause instanceof SocketTimeoutException)
|
||||
code = CloseStatus.SHUTDOWN;
|
||||
else if (behavior == Behavior.CLIENT)
|
||||
code = CloseStatus.POLICY_VIOLATION;
|
||||
else
|
||||
code = CloseStatus.SERVER_ERROR;
|
||||
|
||||
return new AbnormalCloseStatus(code, cause.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an Error that originated from the connection.
|
||||
* For protocol causes, send and abnormal close frame
|
||||
* otherwise just close the connection.
|
||||
*
|
||||
* @param cause the cause
|
||||
*/
|
||||
public void processConnectionError(Throwable cause)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("processConnectionError {} {}", this, cause);
|
||||
|
||||
CloseStatus closeStatus = abnormalCloseStatusFor(cause);
|
||||
|
||||
if (closeStatus.getCode() == CloseStatus.PROTOCOL)
|
||||
close(closeStatus, Callback.NOOP, false);
|
||||
else if (channelState.onClosed(closeStatus))
|
||||
closeConnection(cause, closeStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an Error that originated from the handler.
|
||||
* Send an abnormal close frame to ensure connection is closed.
|
||||
*
|
||||
* @param cause the cause
|
||||
*/
|
||||
public void processHandlerError(Throwable cause)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("processHandlerError {} {}", this, cause);
|
||||
|
||||
close(abnormalCloseStatusFor(cause), Callback.NOOP, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -393,27 +397,27 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("ConnectionState: Transition to CONNECTED");
|
||||
|
||||
try
|
||||
{
|
||||
// Open connection and handler
|
||||
channelState.onOpen();
|
||||
handler.onOpen(this);
|
||||
if (!demanding)
|
||||
connection.demand(1);
|
||||
// Open connection and handler
|
||||
channelState.onOpen();
|
||||
handler.onOpen(this);
|
||||
if (!demanding)
|
||||
connection.demand(1);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("ConnectionState: Transition to OPEN");
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOG.warn("Error during OPEN", t);
|
||||
// TODO: this must trigger onError AND onClose
|
||||
processError(new CloseException(CloseStatus.SERVER_ERROR, t));
|
||||
}
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("ConnectionState: Transition to OPEN");
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
processError(t); // Handle error
|
||||
LOG.warn("Error during OPEN", t);
|
||||
try
|
||||
{
|
||||
handler.onError(t);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
t.addSuppressed(e);
|
||||
}
|
||||
processHandlerError(new CloseException(CloseStatus.SERVER_ERROR, t));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -454,73 +458,73 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
|||
@Override
|
||||
public void sendFrame(Frame frame, Callback callback, boolean batch)
|
||||
{
|
||||
synchronized(this)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("sendFrame({}, {}, {})", frame, callback, batch);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("sendFrame({}, {}, {})", frame, callback, batch);
|
||||
|
||||
boolean closed;
|
||||
try
|
||||
{
|
||||
assertValidOutgoing(frame);
|
||||
}
|
||||
catch (Throwable ex)
|
||||
{
|
||||
callback.failed(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
synchronized(flusher)
|
||||
{
|
||||
boolean closeConnection = channelState.onOutgoingFrame(frame);
|
||||
|
||||
if (frame.getOpCode() == OpCode.CLOSE)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("close({}, {}, {})", CloseStatus.getCloseStatus(frame), callback, batch);
|
||||
|
||||
if (closeConnection)
|
||||
{
|
||||
callback = new Callback.Nested(callback)
|
||||
{
|
||||
@Override
|
||||
public void completed()
|
||||
{
|
||||
closeConnection(null, channelState.getCloseStatus());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
flusher.queue.offer(new FrameEntry(frame, callback, batch));
|
||||
}
|
||||
flusher.iterate();
|
||||
}
|
||||
catch (Throwable ex)
|
||||
{
|
||||
try
|
||||
{
|
||||
assertValidOutgoing(frame);
|
||||
closed = channelState.checkOutgoing(frame);
|
||||
}
|
||||
catch (Throwable ex)
|
||||
{
|
||||
callback.failed(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame.getOpCode() == OpCode.CLOSE)
|
||||
finally
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("close({}, {}, {})", CloseStatus.getCloseStatus(frame), callback, batch);
|
||||
|
||||
if (closed)
|
||||
if (frame.getOpCode() == OpCode.CLOSE)
|
||||
{
|
||||
callback = new Callback.Nested(callback)
|
||||
{
|
||||
@Override
|
||||
public void completed()
|
||||
{
|
||||
try
|
||||
{
|
||||
handler.onClosed(channelState.getCloseStatus());
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
try
|
||||
{
|
||||
handler.onError(e);
|
||||
}
|
||||
catch (Throwable e2)
|
||||
{
|
||||
e.addSuppressed(e2);
|
||||
LOG.warn(e);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
connection.close();
|
||||
}
|
||||
}
|
||||
};
|
||||
CloseStatus closeStatus = CloseStatus.getCloseStatus(frame);
|
||||
if (closeStatus instanceof AbnormalCloseStatus)
|
||||
closeConnection(null, closeStatus);
|
||||
}
|
||||
}
|
||||
|
||||
negotiated.getExtensions().sendFrame(frame, callback, batch);
|
||||
}
|
||||
connection.sendFrameQueue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush(Callback callback)
|
||||
{
|
||||
synchronized(this)
|
||||
synchronized(flusher)
|
||||
{
|
||||
negotiated.getExtensions().sendFrame(FrameFlusher.FLUSH_FRAME, callback, false);
|
||||
flusher.queue.offer(new FrameEntry(FrameFlusher.FLUSH_FRAME, callback, false));
|
||||
}
|
||||
connection.sendFrameQueue();
|
||||
flusher.iterate();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -613,14 +617,13 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
|||
LOG.debug("receiveFrame({}, {}) - connectionState={}, handler={}",
|
||||
frame, callback, channelState, handler);
|
||||
|
||||
|
||||
boolean closed = channelState.checkIncoming(frame);
|
||||
boolean closeConnection = channelState.onIncomingFrame(frame);
|
||||
|
||||
// Handle inbound close
|
||||
if (frame.getOpCode() == OpCode.CLOSE)
|
||||
{
|
||||
connection.cancelDemand();
|
||||
if (closed)
|
||||
if (closeConnection)
|
||||
{
|
||||
callback = new Callback.Nested(callback)
|
||||
{
|
||||
|
@ -640,7 +643,7 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
|||
@Override
|
||||
public void completed()
|
||||
{
|
||||
if (channelState.isOutOpen())
|
||||
if (channelState.isOutputOpen())
|
||||
{
|
||||
CloseStatus closeStatus = CloseStatus.getCloseStatus(frame);
|
||||
|
||||
|
@ -648,8 +651,9 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
|||
LOG.debug("ConnectionState: sending close response {}", closeStatus);
|
||||
|
||||
// this may race with a rare application close but errors are ignored
|
||||
if (closeStatus==null)
|
||||
closeStatus = CloseStatus.NO_CODE_STATUS;
|
||||
close(closeStatus.getCode(), closeStatus.getReason(), Callback.NOOP);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -735,8 +739,9 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("WSChannel@%x{%s,%s,af=%b,i/o=%d/%d,fs=%d}->%s",
|
||||
return String.format("WSChannel@%x{%s,%s,%s,af=%b,i/o=%d/%d,fs=%d}->%s",
|
||||
hashCode(),
|
||||
behavior,
|
||||
channelState,
|
||||
negotiated,
|
||||
autoFragment,
|
||||
|
@ -745,4 +750,67 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio
|
|||
maxFrameSize,
|
||||
handler);
|
||||
}
|
||||
|
||||
static class AbnormalCloseStatus extends CloseStatus
|
||||
{
|
||||
public AbnormalCloseStatus(int statusCode, String reasonPhrase)
|
||||
{
|
||||
super(statusCode, reasonPhrase);
|
||||
}
|
||||
}
|
||||
|
||||
private class Flusher extends IteratingCallback
|
||||
{
|
||||
private final Queue<FrameEntry> queue = new ArrayDeque<>();
|
||||
FrameEntry entry;
|
||||
|
||||
@Override
|
||||
protected Action process() throws Throwable
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
entry = queue.poll();
|
||||
}
|
||||
if (entry==null)
|
||||
return Action.IDLE;
|
||||
|
||||
negotiated.getExtensions().sendFrame(entry.frame, this, entry.batch);
|
||||
return Action.SCHEDULED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
entry.callback.succeeded();
|
||||
super.succeeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCompleteFailure(Throwable cause)
|
||||
{
|
||||
entry.callback.failed(cause);
|
||||
Queue<FrameEntry> entries;
|
||||
synchronized (this)
|
||||
{
|
||||
entries = new ArrayDeque<>(queue);
|
||||
queue.clear();
|
||||
}
|
||||
entries.forEach(e-> failEntry(cause, e));
|
||||
}
|
||||
|
||||
private void failEntry(Throwable cause, FrameEntry e)
|
||||
{
|
||||
try
|
||||
{
|
||||
e.callback.failed(cause);
|
||||
}
|
||||
catch(Throwable x)
|
||||
{
|
||||
if (cause != x)
|
||||
cause.addSuppressed(x);
|
||||
LOG.warn(cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,8 +33,8 @@ public class WebSocketChannelState
|
|||
CONNECTING,
|
||||
CONNECTED,
|
||||
OPEN,
|
||||
ICLOSED,
|
||||
OCLOSED,
|
||||
ISHUT,
|
||||
OSHUT,
|
||||
CLOSED
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,11 @@ public class WebSocketChannelState
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return _channelState.toString();
|
||||
return String.format("%s@%x{%s,i=%s,o=%s,c=%s}",getClass().getSimpleName(),hashCode(),
|
||||
_channelState,
|
||||
OpCode.name(_incomingContinuation),
|
||||
OpCode.name(_outgoingContinuation),
|
||||
_closeStatus);
|
||||
}
|
||||
|
||||
|
||||
|
@ -85,16 +89,16 @@ public class WebSocketChannelState
|
|||
return getState()==State.CLOSED;
|
||||
}
|
||||
|
||||
public boolean isInOpen()
|
||||
public boolean isInputOpen()
|
||||
{
|
||||
State state = getState();
|
||||
return (state==State.OPEN || state==State.OCLOSED);
|
||||
return (state==State.OPEN || state==State.OSHUT);
|
||||
}
|
||||
|
||||
public boolean isOutOpen()
|
||||
public boolean isOutputOpen()
|
||||
{
|
||||
State state = getState();
|
||||
return (state==State.OPEN || state==State.ICLOSED);
|
||||
return (state==State.OPEN || state==State.ISHUT);
|
||||
}
|
||||
|
||||
public CloseStatus getCloseStatus()
|
||||
|
@ -118,28 +122,39 @@ public class WebSocketChannelState
|
|||
}
|
||||
}
|
||||
|
||||
public boolean checkOutgoing(Frame frame) throws ProtocolException
|
||||
public boolean onOutgoingFrame(Frame frame) throws ProtocolException
|
||||
{
|
||||
byte opcode = frame.getOpCode();
|
||||
boolean fin = frame.isFin();
|
||||
|
||||
synchronized (this)
|
||||
{
|
||||
if (!isOutOpen())
|
||||
if (!isOutputOpen())
|
||||
{
|
||||
if (opcode == OpCode.CLOSE && CloseStatus.getCloseStatus(frame) instanceof WebSocketChannel.AbnormalCloseStatus)
|
||||
_channelState = State.CLOSED;
|
||||
throw new IllegalStateException(_channelState.toString());
|
||||
}
|
||||
|
||||
if (opcode == OpCode.CLOSE)
|
||||
{
|
||||
_closeStatus = CloseStatus.getCloseStatus(frame);
|
||||
if (_closeStatus instanceof WebSocketChannel.AbnormalCloseStatus)
|
||||
{
|
||||
_channelState = State.CLOSED;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (_channelState)
|
||||
{
|
||||
case OPEN:
|
||||
_channelState = State.OCLOSED;
|
||||
_channelState = State.OSHUT;
|
||||
return false;
|
||||
case ICLOSED:
|
||||
|
||||
case ISHUT:
|
||||
_channelState = State.CLOSED;
|
||||
return true;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException(_channelState.toString());
|
||||
}
|
||||
|
@ -153,14 +168,14 @@ public class WebSocketChannelState
|
|||
return false;
|
||||
}
|
||||
|
||||
public boolean checkIncoming(Frame frame) throws ProtocolException
|
||||
public boolean onIncomingFrame(Frame frame) throws ProtocolException
|
||||
{
|
||||
byte opcode = frame.getOpCode();
|
||||
boolean fin = frame.isFin();
|
||||
|
||||
synchronized (this)
|
||||
{
|
||||
if (!isInOpen())
|
||||
if (!isInputOpen())
|
||||
throw new IllegalStateException(_channelState.toString());
|
||||
|
||||
if (opcode == OpCode.CLOSE)
|
||||
|
@ -170,9 +185,9 @@ public class WebSocketChannelState
|
|||
switch (_channelState)
|
||||
{
|
||||
case OPEN:
|
||||
_channelState = State.ICLOSED;
|
||||
_channelState = State.ISHUT;
|
||||
return false;
|
||||
case OCLOSED:
|
||||
case OSHUT:
|
||||
_channelState = State.CLOSED;
|
||||
return true;
|
||||
default:
|
||||
|
|
|
@ -18,6 +18,13 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.core.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.eclipse.jetty.io.AbstractConnection;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.Connection;
|
||||
|
@ -34,13 +41,6 @@ import org.eclipse.jetty.websocket.core.MessageTooLargeException;
|
|||
import org.eclipse.jetty.websocket.core.ProtocolException;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketTimeoutException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Provides the implementation of {@link org.eclipse.jetty.io.Connection} that is suitable for WebSocket
|
||||
*/
|
||||
|
@ -167,22 +167,44 @@ public class WebSocketConnection extends AbstractConnection implements Connectio
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("onClose() of physical connection");
|
||||
|
||||
// TODO review all close paths
|
||||
IOException e = new IOException("Closed");
|
||||
channel.onClosed(e);
|
||||
if (!channel.isClosed())
|
||||
{
|
||||
IOException e = new IOException("Closed");
|
||||
channel.onClosed(e);
|
||||
}
|
||||
flusher.onClose();
|
||||
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onIdleExpired()
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("onIdleExpired()");
|
||||
|
||||
channel.processError(new WebSocketTimeoutException("Connection Idle Timeout"));
|
||||
// treat as a handler error because socket is still open
|
||||
channel.processHandlerError(new WebSocketTimeoutException("Connection Idle Timeout"));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event for no activity on connection (read or write)
|
||||
*
|
||||
* @return true to signal that the endpoint must be closed, false to keep the endpoint open
|
||||
*/
|
||||
@Override
|
||||
protected boolean onReadTimeout(Throwable timeout)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("onReadTimeout()");
|
||||
|
||||
// treat as a handler error because socket is still open
|
||||
channel.processHandlerError(new WebSocketTimeoutException("Timeout on Read", timeout));
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void onFrame(Parser.ParsedFrame frame)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
|
@ -219,7 +241,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio
|
|||
referenced.release();
|
||||
|
||||
// notify session & endpoint
|
||||
channel.processError(cause);
|
||||
channel.processHandlerError(cause);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -431,7 +453,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio
|
|||
LOG.warn(t.toString());
|
||||
BufferUtil.clear(networkBuffer.getBuffer());
|
||||
releaseNetworkBuffer();
|
||||
channel.processError(t);
|
||||
channel.processConnectionError(t);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -476,18 +498,6 @@ public class WebSocketConnection extends AbstractConnection implements Connectio
|
|||
super.onOpen();
|
||||
}
|
||||
|
||||
/**
|
||||
* Event for no activity on connection (read or write)
|
||||
*
|
||||
* @return true to signal that the endpoint must be closed, false to keep the endpoint open
|
||||
*/
|
||||
@Override
|
||||
protected boolean onReadTimeout(Throwable timeout)
|
||||
{
|
||||
channel.processError(new WebSocketTimeoutException("Timeout on Read", timeout));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputBufferSize(int inputBufferSize)
|
||||
{
|
||||
|
@ -577,7 +587,6 @@ public class WebSocketConnection extends AbstractConnection implements Connectio
|
|||
|
||||
/**
|
||||
* Enqueue a Frame to be sent.
|
||||
* @see #sendFrameQueue()
|
||||
* @param frame The frame to queue
|
||||
* @param callback The callback to call once the frame is sent
|
||||
* @param batch True if batch mode is to be used
|
||||
|
@ -592,10 +601,6 @@ public class WebSocketConnection extends AbstractConnection implements Connectio
|
|||
wsf.setMask(mask);
|
||||
}
|
||||
flusher.enqueue(frame, callback, batch);
|
||||
}
|
||||
|
||||
void sendFrameQueue()
|
||||
{
|
||||
flusher.iterate();
|
||||
}
|
||||
|
||||
|
@ -610,7 +615,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio
|
|||
public void onCompleteFailure(Throwable x)
|
||||
{
|
||||
super.onCompleteFailure(x);
|
||||
channel.processError(x);
|
||||
channel.processConnectionError(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,11 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.core;
|
||||
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.NetworkConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
|
@ -39,11 +44,6 @@ import org.eclipse.jetty.websocket.core.server.internal.RFC6455Handshaker;
|
|||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.eclipse.jetty.util.Callback.NOOP;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
@ -65,7 +65,7 @@ public class WebSocketCloseTest extends WebSocketTester
|
|||
|
||||
enum State
|
||||
{
|
||||
OPEN, ICLOSED, OCLOSED
|
||||
OPEN, ISHUT, OSHUT
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
|
@ -93,7 +93,7 @@ public class WebSocketCloseTest extends WebSocketTester
|
|||
|
||||
break;
|
||||
}
|
||||
case ICLOSED:
|
||||
case ISHUT:
|
||||
{
|
||||
TestFrameHandler serverHandler = new TestFrameHandler();
|
||||
|
||||
|
@ -109,12 +109,12 @@ public class WebSocketCloseTest extends WebSocketTester
|
|||
assertNotNull(frame);
|
||||
assertThat(new CloseStatus(frame.getPayload()).getCode(), is(CloseStatus.NORMAL));
|
||||
|
||||
assertThat(server.handler.getCoreSession().toString(), containsString("ICLOSED"));
|
||||
LOG.info("Server: ICLOSED");
|
||||
assertThat(server.handler.getCoreSession().toString(), containsString("ISHUT"));
|
||||
LOG.info("Server: ISHUT");
|
||||
|
||||
break;
|
||||
}
|
||||
case OCLOSED:
|
||||
case OSHUT:
|
||||
{
|
||||
TestFrameHandler serverHandler = new TestFrameHandler();
|
||||
|
||||
|
@ -129,8 +129,8 @@ public class WebSocketCloseTest extends WebSocketTester
|
|||
assertNotNull(frame);
|
||||
assertThat(new CloseStatus(frame.getPayload()).getCode(), is(CloseStatus.NORMAL));
|
||||
|
||||
assertThat(server.handler.getCoreSession().toString(), containsString("OCLOSED"));
|
||||
LOG.info("Server: OCLOSED");
|
||||
assertThat(server.handler.getCoreSession().toString(), containsString("OSHUT"));
|
||||
LOG.info("Server: OSHUT");
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ public class WebSocketCloseTest extends WebSocketTester
|
|||
@Test
|
||||
public void serverClose_ICLOSED() throws Exception
|
||||
{
|
||||
setup(State.ICLOSED);
|
||||
setup(State.ISHUT);
|
||||
|
||||
server.handler.receivedCallback.poll().succeeded();
|
||||
Frame frame = receiveFrame(client.getInputStream());
|
||||
|
@ -154,7 +154,7 @@ public class WebSocketCloseTest extends WebSocketTester
|
|||
@Test
|
||||
public void serverDifferentClose_ICLOSED() throws Exception
|
||||
{
|
||||
setup(State.ICLOSED);
|
||||
setup(State.ISHUT);
|
||||
|
||||
server.sendFrame(CloseStatus.toFrame(CloseStatus.SHUTDOWN));
|
||||
server.handler.receivedCallback.poll().succeeded();
|
||||
|
@ -171,7 +171,7 @@ public class WebSocketCloseTest extends WebSocketTester
|
|||
{
|
||||
try (StacklessLogging stackless = new StacklessLogging(WebSocketChannel.class))
|
||||
{
|
||||
setup(State.ICLOSED);
|
||||
setup(State.ISHUT);
|
||||
server.handler.receivedCallback.poll().failed(new Exception("test failure"));
|
||||
|
||||
Frame frame = receiveFrame(client.getInputStream());
|
||||
|
@ -186,7 +186,7 @@ public class WebSocketCloseTest extends WebSocketTester
|
|||
@Test
|
||||
public void clientClose_OCLOSED() throws Exception
|
||||
{
|
||||
setup(State.OCLOSED);
|
||||
setup(State.OSHUT);
|
||||
server.handler.getCoreSession().demand(1);
|
||||
client.getOutputStream().write(RawFrameBuilder.buildClose(new CloseStatus(CloseStatus.NORMAL), true));
|
||||
assertNotNull(server.handler.receivedFrames.poll(10, TimeUnit.SECONDS));
|
||||
|
@ -201,7 +201,7 @@ public class WebSocketCloseTest extends WebSocketTester
|
|||
@Test
|
||||
public void clientDifferentClose_OCLOSED() throws Exception
|
||||
{
|
||||
setup(State.OCLOSED);
|
||||
setup(State.OSHUT);
|
||||
server.handler.getCoreSession().demand(1);
|
||||
client.getOutputStream().write(RawFrameBuilder.buildClose(new CloseStatus(CloseStatus.BAD_PAYLOAD), true));
|
||||
assertNotNull(server.handler.receivedFrames.poll(10, TimeUnit.SECONDS));
|
||||
|
@ -218,7 +218,7 @@ public class WebSocketCloseTest extends WebSocketTester
|
|||
{
|
||||
try (StacklessLogging stackless = new StacklessLogging(WebSocketChannel.class))
|
||||
{
|
||||
setup(State.OCLOSED);
|
||||
setup(State.OSHUT);
|
||||
server.handler.getCoreSession().demand(1);
|
||||
client.getOutputStream().write(RawFrameBuilder.buildClose(new CloseStatus(CloseStatus.NORMAL), true));
|
||||
assertNotNull(server.handler.receivedFrames.poll(10, TimeUnit.SECONDS));
|
||||
|
@ -246,7 +246,7 @@ public class WebSocketCloseTest extends WebSocketTester
|
|||
@Test
|
||||
public void clientSendsBadFrame_OCLOSED() throws Exception
|
||||
{
|
||||
setup(State.OCLOSED);
|
||||
setup(State.OSHUT);
|
||||
|
||||
client.getOutputStream().write(RawFrameBuilder.buildFrame(OpCode.PONG, "pong frame not masked", false));
|
||||
server.handler.getCoreSession().demand(1);
|
||||
|
@ -258,7 +258,7 @@ public class WebSocketCloseTest extends WebSocketTester
|
|||
@Test
|
||||
public void clientSendsBadFrame_ICLOSED() throws Exception
|
||||
{
|
||||
setup(State.ICLOSED);
|
||||
setup(State.ISHUT);
|
||||
|
||||
client.getOutputStream().write(RawFrameBuilder.buildFrame(OpCode.PONG, "pong frame not masked", false));
|
||||
assertFalse(server.handler.closed.await(250, TimeUnit.MILLISECONDS));
|
||||
|
@ -286,7 +286,7 @@ public class WebSocketCloseTest extends WebSocketTester
|
|||
@Test
|
||||
public void clientAborts_OCLOSED() throws Exception
|
||||
{
|
||||
setup(State.OCLOSED);
|
||||
setup(State.OSHUT);
|
||||
|
||||
client.close();
|
||||
assertFalse(server.handler.closed.await(250, TimeUnit.MILLISECONDS));
|
||||
|
@ -299,7 +299,7 @@ public class WebSocketCloseTest extends WebSocketTester
|
|||
@Test
|
||||
public void clientAborts_ICLOSED() throws Exception
|
||||
{
|
||||
setup(State.ICLOSED);
|
||||
setup(State.ISHUT);
|
||||
|
||||
client.close();
|
||||
assertFalse(server.handler.closed.await(250, TimeUnit.MILLISECONDS));
|
||||
|
@ -330,7 +330,7 @@ public class WebSocketCloseTest extends WebSocketTester
|
|||
@Test
|
||||
public void onFrameThrows_OCLOSED() throws Exception
|
||||
{
|
||||
setup(State.OCLOSED);
|
||||
setup(State.OSHUT);
|
||||
|
||||
client.getOutputStream().write(RawFrameBuilder.buildFrame(OpCode.BINARY, "binary", true));
|
||||
|
||||
|
@ -478,7 +478,7 @@ public class WebSocketCloseTest extends WebSocketTester
|
|||
|
||||
public boolean isOpen()
|
||||
{
|
||||
return handler.getCoreSession().isOpen();
|
||||
return handler.getCoreSession().isOutputOpen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,11 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.core.client;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.NetworkConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
|
@ -45,11 +50,6 @@ import org.hamcrest.Matchers;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
@ -208,7 +208,7 @@ public class WebSocketClientServerTest
|
|||
|
||||
public boolean isOpen()
|
||||
{
|
||||
return handler.getCoreSession().isOpen();
|
||||
return handler.getCoreSession().isOutputOpen();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -272,7 +272,7 @@ public class WebSocketClientServerTest
|
|||
|
||||
public boolean isOpen()
|
||||
{
|
||||
return handler.getCoreSession().isOpen();
|
||||
return handler.getCoreSession().isOutputOpen();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.core.extensions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.util.DecoratedObjectFactory;
|
||||
|
@ -35,9 +38,6 @@ import org.eclipse.jetty.websocket.core.internal.IdentityExtension;
|
|||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
@ -80,7 +80,7 @@ public class ExtensionStackTest
|
|||
// Setup Listeners
|
||||
IncomingFrames session = new IncomingFramesCapture();
|
||||
OutgoingFrames connection = new OutgoingFramesCapture();
|
||||
stack.connect(session, connection, null);
|
||||
stack.initialize(session, connection, null);
|
||||
|
||||
// Dump
|
||||
LOG.debug("{}", stack.dump());
|
||||
|
@ -104,7 +104,7 @@ public class ExtensionStackTest
|
|||
// Setup Listeners
|
||||
IncomingFrames session = new IncomingFramesCapture();
|
||||
OutgoingFrames connection = new OutgoingFramesCapture();
|
||||
stack.connect(session, connection, null);
|
||||
stack.initialize(session, connection, null);
|
||||
|
||||
// Dump
|
||||
LOG.debug("{}", stack.dump());
|
||||
|
|
|
@ -18,6 +18,10 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.core.extensions;
|
||||
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.NetworkConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
|
@ -44,10 +48,6 @@ import org.eclipse.jetty.websocket.core.server.WebSocketUpgradeHandler;
|
|||
import org.eclipse.jetty.websocket.core.server.internal.RFC6455Handshaker;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.eclipse.jetty.util.Callback.NOOP;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
@ -218,7 +218,7 @@ public class ValidationExtensionTest extends WebSocketTester
|
|||
|
||||
public boolean isOpen()
|
||||
{
|
||||
return handler.getCoreSession().isOpen();
|
||||
return handler.getCoreSession().isOutputOpen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,12 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.core.server;
|
||||
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.NetworkConnector;
|
||||
|
@ -46,12 +52,6 @@ import org.hamcrest.MatcherAssert;
|
|||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
|
@ -547,7 +547,7 @@ public class WebSocketServerTest extends WebSocketTester
|
|||
|
||||
public boolean isOpen()
|
||||
{
|
||||
return handler.getCoreSession().isOpen();
|
||||
return handler.getCoreSession().isOutputOpen();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue