You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by mg...@apache.org on 2015/04/10 15:51:49 UTC
[2/6] wicket git commit: Cross-Site WebSocket Hijacking protection
added. WebsocketSettings extended with allowedDomains. Hijacking protection
flag added to websocket settings. WebSocketBehavior and WebSocketResource got
new methods called onAbort(). New
Cross-Site WebSocket Hijacking protection added. WebsocketSettings
extended with allowedDomains. Hijacking protection flag added to
websocket settings. WebSocketBehavior and WebSocketResource got new
methods called onAbort(). New broadcast message type: AbortedMessage
introduced. New payload type: WebSocketAbortedPayload introduced.
AbstractWebSocketProcessor closes connection when protection check
fails. WebSocketTesterProcessorTest fixed and cleaned up
Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/5c5c3640
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/5c5c3640
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/5c5c3640
Branch: refs/heads/master
Commit: 5c5c36406a60c96dea9e9c7cf41e46f3733ba257
Parents: 2f3b357
Author: admin <ge...@mail.rakuten.com>
Authored: Fri Mar 20 17:18:56 2015 +0900
Committer: Martin Tzvetanov Grigorov <mg...@apache.org>
Committed: Fri Apr 10 16:51:07 2015 +0300
----------------------------------------------------------------------
.../wicket/protocol/ws/WebSocketSettings.java | 64 +++++++++
.../ws/api/AbstractWebSocketProcessor.java | 60 ++++++--
.../ws/api/ConnectionRejectedException.java | 21 +++
.../ws/api/IWebSocketConnectionFilter.java | 38 +++++
.../protocol/ws/api/WebSocketBehavior.java | 17 +++
.../ws/api/WebSocketConnectionOriginFilter.java | 64 +++++++++
.../protocol/ws/api/WebSocketResource.java | 18 +++
.../ws/api/event/WebSocketAbortedPayload.java | 42 ++++++
.../protocol/ws/api/message/AbortedMessage.java | 56 ++++++++
.../ws/util/tester/TestWebSocketProcessor.java | 14 ++
.../ws/util/tester/TestWebSocketResource.java | 8 ++
.../tester/WebSocketTesterProcessorTest.java | 144 +++++++++++++++++++
12 files changed, 531 insertions(+), 15 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/WebSocketSettings.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/WebSocketSettings.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/WebSocketSettings.java
index 3b2d5c9a..92a4a5c 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/WebSocketSettings.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/WebSocketSettings.java
@@ -16,6 +16,9 @@
*/
package org.apache.wicket.protocol.ws;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
import java.util.concurrent.Callable;
import org.apache.wicket.Application;
@@ -87,6 +90,16 @@ public class WebSocketSettings
private IWebSocketConnectionRegistry connectionRegistry = new SimpleWebSocketConnectionRegistry();
/**
+ * The whitelist of allowed domains where the client can connect to the application from
+ */
+ private final List<String> allowedDomains = new ArrayList<String>();
+
+ /**
+ * Flag which indicates whether connection filtering should be active or not
+ */
+ private boolean protectionNeeded = false;
+
+ /**
* Set the executor for processing websocket push messages broadcasted to all sessions.
* Default executor does all the processing in the caller thread. Using a proper thread pool is adviced
* for applications that send push events from ajax calls to avoid page level deadlocks.
@@ -155,6 +168,57 @@ public class WebSocketSettings
return sendPayloadExecutor;
}
+ /**
+ * Flag that controls whether hijacking protection should be turned on or not
+ *
+ * @param protectionNeeded
+ * True if protection needed
+ */
+ public void setHijackingProtectionEnabled(boolean protectionNeeded) {
+ this.protectionNeeded = protectionNeeded;
+ }
+
+ /**
+ * Flag that shows whether hijacking protection is turned on or not
+ *
+ * @param protectionNeeded
+ * True if protection turned on
+ */
+ public boolean isHijackingProtectionEnabled() {
+ return this.protectionNeeded;
+ }
+
+ /**
+ * The list of whitelisted domains which are allowed to initiate a websocket connection. This
+ * list will be eventually used by the
+ * {@link org.apache.wicket.protocol.ws.api.IWebSocketConnectionFilter} to abort potentially
+ * unsafe connections. Example domain names might be:
+ *
+ * <pre>
+ * http://www.example.com
+ * http://ww2.example.com
+ * </pre>
+ *
+ * @param domains
+ * The collection of domains
+ */
+ public void setAllowedDomains(Collection<String> domains) {
+ this.allowedDomains.addAll(domains);
+ }
+
+ /**
+ * The list of whitelisted domains which are allowed to initiate a websocket connection. This
+ * list will be eventually used by the
+ * {@link org.apache.wicket.protocol.ws.api.IWebSocketConnectionFilter} to abort potentially
+ * unsafe connections
+ *
+ * @param domains
+ * The collection of domains if or an empty list when no domains were added
+ */
+ public List<String> getAllowedDomains() {
+ return this.allowedDomains;
+ }
+
/**
* A factory method for the {@link org.apache.wicket.request.http.WebResponse}
* that should be used to write the response back to the client/browser
http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketProcessor.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketProcessor.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketProcessor.java
index 8ae53c9..1dac814 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketProcessor.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketProcessor.java
@@ -29,12 +29,14 @@ import org.apache.wicket.page.IPageManager;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.protocol.http.WicketFilter;
import org.apache.wicket.protocol.ws.WebSocketSettings;
+import org.apache.wicket.protocol.ws.api.event.WebSocketAbortedPayload;
import org.apache.wicket.protocol.ws.api.event.WebSocketBinaryPayload;
import org.apache.wicket.protocol.ws.api.event.WebSocketClosedPayload;
import org.apache.wicket.protocol.ws.api.event.WebSocketConnectedPayload;
import org.apache.wicket.protocol.ws.api.event.WebSocketPayload;
import org.apache.wicket.protocol.ws.api.event.WebSocketPushPayload;
import org.apache.wicket.protocol.ws.api.event.WebSocketTextPayload;
+import org.apache.wicket.protocol.ws.api.message.AbortedMessage;
import org.apache.wicket.protocol.ws.api.message.BinaryMessage;
import org.apache.wicket.protocol.ws.api.message.ClosedMessage;
import org.apache.wicket.protocol.ws.api.message.ConnectedMessage;
@@ -76,6 +78,20 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
*/
static final int NO_PAGE_ID = -1;
+ /**
+ * 1008 indicates that an endpoint is terminating the connection because it has received a message that violates its policy. This is a generic status code
+ * that can be returned when there is no other more suitable status code (e.g., 1003 or 1009) or if there is a need to hide specific details about the
+ * policy.
+ * <p>
+ * See <a href="https://tools.ietf.org/html/rfc6455#section-7.4.1">RFC 6455, Section 7.4.1 Defined Status Codes</a>.
+ */
+ static final int POLICY_VIOLATION = 1008;
+
+ /**
+ * Explanatory text for the client to explain why the connection is getting aborted
+ */
+ static final String ORIGIN_MISMATCH = "Origin mismatch";
+
private final WebRequest webRequest;
private final int pageId;
private final String resourceName;
@@ -84,6 +100,8 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
private final String sessionId;
private final WebSocketSettings webSocketSettings;
private final IWebSocketConnectionRegistry connectionRegistry;
+ private final IWebSocketConnectionFilter connectionFilter;
+ private final HttpServletRequest servletRequest;
/**
* Constructor.
@@ -117,13 +135,17 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
this.baseUrl = Url.parse(baseUrl);
WicketFilter wicketFilter = application.getWicketFilter();
- this.webRequest = new WebSocketRequest(new ServletRequestCopy(request), wicketFilter.getFilterPath());
+ this.servletRequest = new ServletRequestCopy(request);
+
+ this.webRequest = new WebSocketRequest(servletRequest, wicketFilter.getFilterPath());
this.application = Args.notNull(application, "application");
this.webSocketSettings = WebSocketSettings.Holder.get(application);
this.connectionRegistry = webSocketSettings.getConnectionRegistry();
+
+ this.connectionFilter = new WebSocketConnectionOriginFilter(webSocketSettings);
}
@Override
@@ -139,20 +161,25 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
broadcastMessage(binaryMessage);
}
- /**
- * A helper that registers the opened connection in the application-level
- * registry.
- *
- * @param connection
- * the web socket connection to use to communicate with the client
- * @see #onOpen(Object)
- */
- protected final void onConnect(final IWebSocketConnection connection)
- {
- IKey key = getRegistryKey();
- connectionRegistry.setConnection(getApplication(), getSessionId(), key, connection);
- broadcastMessage(new ConnectedMessage(getApplication(), getSessionId(), key));
- }
+ /**
+ * A helper that registers the opened connection in the application-level registry.
+ *
+ * @param connection
+ * the web socket connection to use to communicate with the client
+ * @see #onOpen(Object)
+ */
+ protected final void onConnect(final IWebSocketConnection connection) {
+ IKey key = getRegistryKey();
+ try {
+ connectionRegistry.setConnection(getApplication(), getSessionId(), key, connection);
+ connectionFilter.doFilter(servletRequest);
+ broadcastMessage(new ConnectedMessage(getApplication(), getSessionId(), key));
+ } catch (ConnectionRejectedException e) {
+ broadcastMessage(new AbortedMessage(getApplication(), getSessionId(), key));
+ connectionRegistry.removeConnection(getApplication(), getSessionId(), key);
+ connection.close(POLICY_VIOLATION, ORIGIN_MISMATCH);
+ }
+ }
@Override
public void onClose(int closeCode, String message)
@@ -316,6 +343,9 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
{
payload = new WebSocketClosedPayload((ClosedMessage) message, handler);
}
+ else if (message instanceof AbortedMessage) {
+ payload = new WebSocketAbortedPayload((AbortedMessage) message, handler);
+ }
else if (message instanceof IWebSocketPushMessage)
{
payload = new WebSocketPushPayload((IWebSocketPushMessage) message, handler);
http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/ConnectionRejectedException.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/ConnectionRejectedException.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/ConnectionRejectedException.java
new file mode 100644
index 0000000..15af612
--- /dev/null
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/ConnectionRejectedException.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.protocol.ws.api;
+
+public class ConnectionRejectedException extends RuntimeException {
+
+}
http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/IWebSocketConnectionFilter.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/IWebSocketConnectionFilter.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/IWebSocketConnectionFilter.java
new file mode 100644
index 0000000..a43c7c1
--- /dev/null
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/IWebSocketConnectionFilter.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.protocol.ws.api;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Common interface for rejecting connections which are not meeting some of the security concerns.
+ * One example might be when the connection 'Origin' header does not match the origin of the
+ * application host
+ *
+ * @author Gergely Nagy
+ *
+ */
+public interface IWebSocketConnectionFilter {
+
+ /**
+ * Method for rejecting connections based on the current request
+ *
+ * @param servletRequest
+ * The servlet request holding the request headers
+ */
+ public void doFilter(HttpServletRequest servletRequest);
+}
http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketBehavior.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketBehavior.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketBehavior.java
index 2f8c67c..803246d 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketBehavior.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketBehavior.java
@@ -18,12 +18,14 @@ package org.apache.wicket.protocol.ws.api;
import org.apache.wicket.Component;
import org.apache.wicket.event.IEvent;
+import org.apache.wicket.protocol.ws.api.event.WebSocketAbortedPayload;
import org.apache.wicket.protocol.ws.api.event.WebSocketBinaryPayload;
import org.apache.wicket.protocol.ws.api.event.WebSocketClosedPayload;
import org.apache.wicket.protocol.ws.api.event.WebSocketConnectedPayload;
import org.apache.wicket.protocol.ws.api.event.WebSocketPayload;
import org.apache.wicket.protocol.ws.api.event.WebSocketPushPayload;
import org.apache.wicket.protocol.ws.api.event.WebSocketTextPayload;
+import org.apache.wicket.protocol.ws.api.message.AbortedMessage;
import org.apache.wicket.protocol.ws.api.message.BinaryMessage;
import org.apache.wicket.protocol.ws.api.message.ClosedMessage;
import org.apache.wicket.protocol.ws.api.message.ConnectedMessage;
@@ -77,6 +79,12 @@ public abstract class WebSocketBehavior extends BaseWebSocketBehavior
ClosedMessage message = closedPayload.getMessage();
onClose(message);
}
+ else if (wsPayload instanceof WebSocketAbortedPayload)
+ {
+ WebSocketAbortedPayload abortedPayload = (WebSocketAbortedPayload) wsPayload;
+ AbortedMessage message = abortedPayload.getMessage();
+ onAbort(message);
+ }
else if (wsPayload instanceof WebSocketPushPayload)
{
WebSocketPushPayload pushPayload = (WebSocketPushPayload) wsPayload;
@@ -121,6 +129,15 @@ public abstract class WebSocketBehavior extends BaseWebSocketBehavior
{
}
+ /**
+ * A callback method called when the server has aborted the connection
+ *
+ * @param message
+ * the aborted message with the info about the server
+ */
+ protected void onAbort(AbortedMessage message) {
+ }
+
/**
* A callback method called when there is a text message sent by the client
*
http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketConnectionOriginFilter.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketConnectionOriginFilter.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketConnectionOriginFilter.java
new file mode 100644
index 0000000..655ed30
--- /dev/null
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketConnectionOriginFilter.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.protocol.ws.api;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.wicket.protocol.ws.WebSocketSettings;
+
+public class WebSocketConnectionOriginFilter implements IWebSocketConnectionFilter {
+
+ private final WebSocketSettings webSocketSettings;
+
+ public WebSocketConnectionOriginFilter(WebSocketSettings webSocketSettings) {
+ this.webSocketSettings = webSocketSettings;
+ }
+
+ @Override
+ public void doFilter(HttpServletRequest servletRequest) {
+ if (webSocketSettings.isHijackingProtectionEnabled()) {
+ String oUrl = getOriginUrl(servletRequest);
+ if (invalid(oUrl))
+ throw new ConnectionRejectedException();
+ }
+ }
+
+ private boolean invalid(String oUrl) {
+ if (originMismatch(oUrl))
+ return true;
+ if (oUrl == null || "".equals(oUrl))
+ return true;
+ return false;
+ }
+
+ private boolean originMismatch(String oUrl) {
+ List<String> allowedDomains = webSocketSettings.getAllowedDomains();
+ return !allowedDomains.contains(oUrl);
+ }
+
+ private String getOriginUrl(HttpServletRequest servletRequest) {
+ ArrayList<String> origins = Collections.list(servletRequest.getHeaders("Origin"));
+ if (origins.size() != 1)
+ return null;
+ return origins.get(0);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketResource.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketResource.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketResource.java
index 1db09f9..95aed1d 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketResource.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketResource.java
@@ -16,12 +16,14 @@
*/
package org.apache.wicket.protocol.ws.api;
+import org.apache.wicket.protocol.ws.api.event.WebSocketAbortedPayload;
import org.apache.wicket.protocol.ws.api.event.WebSocketBinaryPayload;
import org.apache.wicket.protocol.ws.api.event.WebSocketClosedPayload;
import org.apache.wicket.protocol.ws.api.event.WebSocketConnectedPayload;
import org.apache.wicket.protocol.ws.api.event.WebSocketPayload;
import org.apache.wicket.protocol.ws.api.event.WebSocketPushPayload;
import org.apache.wicket.protocol.ws.api.event.WebSocketTextPayload;
+import org.apache.wicket.protocol.ws.api.message.AbortedMessage;
import org.apache.wicket.protocol.ws.api.message.BinaryMessage;
import org.apache.wicket.protocol.ws.api.message.ClosedMessage;
import org.apache.wicket.protocol.ws.api.message.ConnectedMessage;
@@ -62,6 +64,12 @@ public abstract class WebSocketResource implements IResource
ClosedMessage message = connectedPayload.getMessage();
onClose(message);
}
+ else if (payload instanceof WebSocketAbortedPayload)
+ {
+ WebSocketAbortedPayload abortedPayload = (WebSocketAbortedPayload) payload;
+ AbortedMessage message = abortedPayload.getMessage();
+ onAbort(message);
+ }
else if (payload instanceof WebSocketPushPayload)
{
WebSocketPushPayload pushPayload = (WebSocketPushPayload) payload;
@@ -94,6 +102,16 @@ public abstract class WebSocketResource implements IResource
{
}
+ /**
+ * A callback method called when the server has aborted the connection
+ *
+ * @param message
+ * the aborted message with the info about the server
+ */
+ protected void onAbort(AbortedMessage message)
+ {
+ }
+
/**
* A callback method called when a WebSocket client has closed the connection
* to the endpoint handled by this WebSocketBehavior
http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/event/WebSocketAbortedPayload.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/event/WebSocketAbortedPayload.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/event/WebSocketAbortedPayload.java
new file mode 100644
index 0000000..5a19ad4
--- /dev/null
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/event/WebSocketAbortedPayload.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.protocol.ws.api.event;
+
+import org.apache.wicket.protocol.ws.api.WebSocketRequestHandler;
+import org.apache.wicket.protocol.ws.api.message.AbortedMessage;
+
+/**
+ * * Payload for event broadcasting when the server aborted a WebSocket connection
+ *
+ * @since 7.0.0-M5
+ */
+public class WebSocketAbortedPayload extends WebSocketPayload<AbortedMessage> {
+
+ private final AbortedMessage message;
+
+ public WebSocketAbortedPayload(AbortedMessage message, WebSocketRequestHandler handler) {
+ super(handler);
+
+ this.message = message;
+ }
+
+ @Override
+ public AbortedMessage getMessage() {
+ return message;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/message/AbortedMessage.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/message/AbortedMessage.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/message/AbortedMessage.java
new file mode 100644
index 0000000..2040554
--- /dev/null
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/message/AbortedMessage.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.protocol.ws.api.message;
+
+import org.apache.wicket.Application;
+import org.apache.wicket.protocol.ws.api.registry.IKey;
+import org.apache.wicket.util.lang.Args;
+
+/**
+ * A {@link IWebSocketMessage message} sent when the web socket connection is aborted.
+ *
+ * @since 7.0.0-M5
+ */
+public class AbortedMessage implements IWebSocketMessage {
+
+ private final Application application;
+ private final String sessionId;
+ private final IKey key;
+
+ public AbortedMessage(Application application, String sessionId, IKey key) {
+ this.application = Args.notNull(application, "application");
+ this.sessionId = Args.notNull(sessionId, "sessionId");
+ this.key = Args.notNull(key, "key");
+ }
+
+ public Application getApplication() {
+ return application;
+ }
+
+ public String getSessionId() {
+ return sessionId;
+ }
+
+ public IKey getKey() {
+ return key;
+ }
+
+ @Override
+ public final String toString() {
+ return "The server aborted the client connection";
+ }
+}
http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketProcessor.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketProcessor.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketProcessor.java
index 80642a8..1ae8789 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketProcessor.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketProcessor.java
@@ -36,6 +36,20 @@ import org.apache.wicket.util.tester.WicketTester;
*/
abstract class TestWebSocketProcessor extends AbstractWebSocketProcessor
{
+ /**
+ *
+ * Constructor.
+ *
+ * @param request
+ * the http request that was used to create the TomcatWebSocketProcessor
+ * @param application
+ * the current Wicket Application
+ */
+ public TestWebSocketProcessor(final HttpServletRequest request, final WebApplication application)
+ {
+ super(request, application);
+ }
+
/**
* Constructor.
*
http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketResource.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketResource.java b/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketResource.java
index 2bee3f1..942889d 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketResource.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketResource.java
@@ -22,6 +22,7 @@ import org.apache.wicket.util.string.Strings;
import org.junit.Assert;
import org.apache.wicket.protocol.ws.api.WebSocketRequestHandler;
import org.apache.wicket.protocol.ws.api.WebSocketResource;
+import org.apache.wicket.protocol.ws.api.message.AbortedMessage;
import org.apache.wicket.protocol.ws.api.message.BinaryMessage;
import org.apache.wicket.protocol.ws.api.message.ClosedMessage;
import org.apache.wicket.protocol.ws.api.message.ConnectedMessage;
@@ -37,6 +38,7 @@ public class TestWebSocketResource extends WebSocketResource
static final AtomicBoolean ON_CONNECT_CALLED = new AtomicBoolean(false);
static final AtomicBoolean ON_CLOSE_CALLED = new AtomicBoolean(false);
+ static final AtomicBoolean ON_ABORT_CALLED = new AtomicBoolean(false);
private final String expectedMessage;
@@ -76,6 +78,12 @@ public class TestWebSocketResource extends WebSocketResource
super.onClose(message);
}
+ @Override
+ protected void onAbort(AbortedMessage message) {
+ ON_ABORT_CALLED.set(true);
+ super.onAbort(message);
+ }
+
@Override
protected void onMessage(WebSocketRequestHandler handler, TextMessage message)
{
http://git-wip-us.apache.org/repos/asf/wicket/blob/5c5c3640/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/WebSocketTesterProcessorTest.java
----------------------------------------------------------------------
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/WebSocketTesterProcessorTest.java b/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/WebSocketTesterProcessorTest.java
new file mode 100644
index 0000000..b47b8af
--- /dev/null
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/test/java/org/apache/wicket/protocol/ws/util/tester/WebSocketTesterProcessorTest.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.protocol.ws.util.tester;
+
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.wicket.mock.MockApplication;
+import org.apache.wicket.protocol.http.WebApplication;
+import org.apache.wicket.protocol.http.mock.MockHttpServletRequest;
+import org.apache.wicket.protocol.ws.WebSocketSettings;
+import org.apache.wicket.request.http.WebRequest;
+import org.apache.wicket.util.tester.WicketTester;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class WebSocketTesterProcessorTest extends Assert {
+
+ final static AtomicBoolean messageReceived = new AtomicBoolean(false);
+
+ private static class TestProcessor extends TestWebSocketProcessor {
+ private TestProcessor(HttpServletRequest request, WebApplication application) {
+ super(request, application);
+ }
+
+ @Override
+ protected void onOutMessage(String message) {
+ messageReceived.set(true);
+ }
+
+ @Override
+ protected void onOutMessage(byte[] message, int offset, int length) {
+ messageReceived.set(true);
+ }
+ }
+
+ WicketTester tester;
+ WebApplication application = new MockApplication() {
+ @Override
+ protected void init() {
+ super.init();
+
+ getSharedResources().add(TestWebSocketResource.TEXT, new TestWebSocketResource("expected"));
+ }
+ };
+
+ @Before
+ public void before() {
+ tester = new WicketTester(application);
+ WebApplication webApplication = tester.getApplication();
+ webApplication.getWicketFilter().setFilterPath("");
+ }
+
+ @After
+ public void after() {
+ tester.destroy();
+ TestWebSocketResource.ON_ABORT_CALLED.set(false);
+ }
+
+ @Test
+ public void onConnectNoOrigin() throws Exception {
+ // Given header 'Origin' is missing
+ configureRequest(true, new String[] { "http://www.example.com" }, new String[] {});
+
+ // When we open a connection
+ TestWebSocketProcessor processor = new TestProcessor(tester.getRequest(), tester.getApplication());
+ processor.onOpen(new Object());
+
+ // Then it fails
+ assertEquals(true, TestWebSocketResource.ON_ABORT_CALLED.get());
+ }
+
+ @Ignore
+ @Test
+ public void onConnectMultipleOrigins() throws Exception {
+ // Given the request contains multiple header 'Origin's
+ configureRequest(true, new String[] { "http://www.example.com" }, new String[] { "http://www.example.com", "http://ww2.example.com" });
+
+ // When we open a connection
+ TestWebSocketProcessor processor = new TestProcessor(tester.getRequest(), tester.getApplication());
+ processor.onOpen(new Object());
+
+ // Then it fails
+ assertEquals(true, TestWebSocketResource.ON_ABORT_CALLED.get());
+ }
+
+ @Test
+ public void onConnectMatchingOrigin() throws Exception {
+ // Given header 'Origin' matches the host origin
+ configureRequest(true, new String[] { "http://www.example.com" }, new String[] { "http://www.example.com" });
+
+ // When we open a connection
+ TestWebSocketProcessor processor = new TestProcessor(tester.getRequest(), tester.getApplication());
+ processor.onOpen(new Object());
+
+ // Then it succeeds
+ assertEquals(false, TestWebSocketResource.ON_ABORT_CALLED.get());
+ }
+
+ @Test
+ public void onConnectMismatchingOrigin() throws Exception {
+ // Given header 'Origin' does not match the host origin
+ configureRequest(true, new String[] { "http://www.example.com" }, new String[] { "http://ww2.example.com" });
+
+ // When we open a connection
+ TestWebSocketProcessor processor = new TestProcessor(tester.getRequest(), tester.getApplication());
+ processor.onOpen(new Object());
+
+ // Then it fails
+ assertEquals(true, TestWebSocketResource.ON_ABORT_CALLED.get());
+ }
+
+ protected void configureRequest(boolean protectionNeeded, String[] allowedDomains, String[] origins) {
+ WebSocketSettings webSocketSettings = WebSocketSettings.Holder.get(application);
+ webSocketSettings.setHijackingProtectionEnabled(protectionNeeded);
+ webSocketSettings.setAllowedDomains(Arrays.asList(allowedDomains));
+ MockHttpServletRequest request = tester.getRequest();
+ for (String origin : origins) {
+ request.addHeader("Origin", origin);
+ }
+ request.addParameter("resourceName", TestWebSocketResource.TEXT);
+ request.addParameter(WebRequest.PARAM_AJAX_BASE_URL, ".");
+ }
+
+}
\ No newline at end of file