You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by re...@apache.org on 2022/04/14 13:34:41 UTC

[wicket] 01/02: {WICKET-6969} allow asynchronous pushing of messages.

This is an automated email from the ASF dual-hosted git repository.

reiern70 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/wicket.git

commit d0c5782efa515d8403c3be8a016c6499ec156822
Author: reiern70 <re...@gmail.com>
AuthorDate: Tue Apr 5 10:06:11 2022 -0600

    {WICKET-6969} allow asynchronous pushing of messages.
---
 .../examples/websocket/JSR356Application.java      |  6 ++-
 .../examples/websocket/charts/ChartUpdater.java    |  3 +-
 .../websocket/progress/ProgressUpdater.java        |  5 --
 .../wicket/protocol/ws/WebSocketSettings.java      | 62 ++++++++++++++++++++--
 .../ws/api/AbstractWebSocketConnection.java        | 16 +++++-
 .../ws/api/AbstractWebSocketProcessor.java         |  8 +--
 .../protocol/ws/api/IWebSocketConnection.java      | 35 ++++++++++--
 .../protocol/ws/api/IWebSocketRequestHandler.java  | 54 +++++++++++++++++++
 .../protocol/ws/api/WebSocketPushBroadcaster.java  |  9 +++-
 .../protocol/ws/api/WebSocketRequestHandler.java   | 43 +++++++++++++++
 .../wicket/protocol/ws/api/WebSocketResponse.java  | 29 +++++++++-
 .../ws/util/tester/TestWebSocketConnection.java    |  5 +-
 .../ws/util/tester/TestWebSocketProcessor.java     | 12 +++++
 13 files changed, 259 insertions(+), 28 deletions(-)

diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/JSR356Application.java b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/JSR356Application.java
index 8bc6e9d80f..660a91a17c 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/JSR356Application.java
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/JSR356Application.java
@@ -23,8 +23,6 @@ import org.apache.wicket.protocol.http.WebApplication;
 import org.apache.wicket.protocol.https.HttpsConfig;
 import org.apache.wicket.protocol.https.HttpsMapper;
 import org.apache.wicket.protocol.ws.WebSocketSettings;
-import org.apache.wicket.protocol.ws.api.IWebSocketSession;
-import org.apache.wicket.protocol.ws.api.IWebSocketSessionConfigurer;
 import org.apache.wicket.request.Request;
 import org.apache.wicket.request.Response;
 import org.slf4j.Logger;
@@ -68,6 +66,10 @@ public class JSR356Application extends WicketExampleApplication
 
 		final WebSocketSettings webSocketSettings = WebSocketSettings.Holder.get(this);
 
+		// use asynchronous/non-blocking push mode
+		webSocketSettings.setAsynchronousPush(true);
+		webSocketSettings.setAsynchronousPushTimeout(6000L);
+
 		webSocketSettings.setSocketSessionConfigurer(webSocketSession -> {
 			LOGGER.info("getMaxIdleTimeout = {}", webSocketSession.getMaxIdleTimeout());
 			// use 5 minutes idle timeout
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/charts/ChartUpdater.java b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/charts/ChartUpdater.java
index 0b586a0a71..3949d04eb8 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/charts/ChartUpdater.java
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/charts/ChartUpdater.java
@@ -116,7 +116,8 @@ public class ChartUpdater
 						// stop if the web socket connection is closed
 						return;
 					}
-					connection.sendMessage(json);
+					//send mon-blocking message
+					connection.sendMessageAsync(json);
 
 					// sleep for a while to simulate work
 					TimeUnit.SECONDS.sleep(1);
diff --git a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressUpdater.java b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressUpdater.java
index 48f9848869..397f2958f9 100644
--- a/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressUpdater.java
+++ b/wicket-examples/src/main/java/org/apache/wicket/examples/websocket/progress/ProgressUpdater.java
@@ -22,15 +22,10 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.wicket.Application;
 import org.apache.wicket.WicketRuntimeException;
-import org.apache.wicket.application.IClassResolver;
-import org.apache.wicket.page.IManageablePage;
 import org.apache.wicket.protocol.ws.WebSocketSettings;
-import org.apache.wicket.protocol.ws.api.IWebSocketConnection;
 import org.apache.wicket.protocol.ws.api.WebSocketPushBroadcaster;
-import org.apache.wicket.protocol.ws.api.message.ConnectedMessage;
 import org.apache.wicket.protocol.ws.api.message.IWebSocketPushMessage;
 import org.apache.wicket.protocol.ws.api.registry.IKey;
-import org.apache.wicket.protocol.ws.api.registry.IWebSocketConnectionRegistry;
 import org.apache.wicket.protocol.ws.api.registry.PageIdKey;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
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 7aca394db3..eccf259327 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
@@ -158,11 +158,24 @@ public class WebSocketSettings
 	 */
 	private Function<Integer, Boolean> notifyOnCloseEvent = (code) -> true;
 
-	public boolean shouldNotifyOnCloseEvent(int closeCode) {
+	/**
+	 * Flag that allows to use asynchronous push. By default, it is set to <code>false</code>.
+	 */
+	private boolean asynchronousPush = false;
+
+	/**
+	 * The timeout to use for asynchronous push. By default, it is -1 which means use timeout configured by
+	 * server implementation.
+	 */
+	private long asynchronousPushTimeout = -1;
+
+	public boolean shouldNotifyOnCloseEvent(int closeCode)
+	{
 		return notifyOnCloseEvent == null || notifyOnCloseEvent.apply(closeCode);
 	}
 
-	public void setNotifyOnCloseEvent(Function<Integer, Boolean> notifyOnCloseEvent) {
+	public void setNotifyOnCloseEvent(Function<Integer, Boolean> notifyOnCloseEvent)
+	{
 		this.notifyOnCloseEvent = notifyOnCloseEvent;
 	}
 
@@ -174,11 +187,13 @@ public class WebSocketSettings
 	 */
 	private Function<Throwable, Boolean> notifyOnErrorEvent = (throwable) -> true;
 
-	public boolean shouldNotifyOnErrorEvent(Throwable throwable) {
+	public boolean shouldNotifyOnErrorEvent(Throwable throwable)
+	{
 		return notifyOnErrorEvent == null || notifyOnErrorEvent.apply(throwable);
 	}
 
-	public void setNotifyOnErrorEvent(Function<Throwable, Boolean> notifyOnErrorEvent) {
+	public void setNotifyOnErrorEvent(Function<Throwable, Boolean> notifyOnErrorEvent)
+	{
 		this.notifyOnErrorEvent = notifyOnErrorEvent;
 	}
 
@@ -305,7 +320,24 @@ public class WebSocketSettings
 	 */
 	public WebResponse newWebSocketResponse(IWebSocketConnection connection)
 	{
-		return new WebSocketResponse(connection);
+		return newWebSocketResponse(connection, isAsynchronousPush(), getAsynchronousPushTimeout());
+	}
+
+	/**
+	 * 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
+	 *
+	 * @param connection
+	 *              The active web socket connection
+	 * @param asynchronousPush
+	 *              Whether asynchronous push is wanted or not.
+     * @param timeout
+     *              The timeout to be used for push operations
+	 * @return the response object that should be used to write the response back to the client
+	 */
+	public WebResponse newWebSocketResponse(IWebSocketConnection connection, boolean asynchronousPush, long timeout)
+	{
+		return new WebSocketResponse(connection, asynchronousPush, timeout);
 	}
 
 	/**
@@ -497,4 +529,24 @@ public class WebSocketSettings
 			return new Thread(r, "Wicket-WebSocket-HttpRequest-Thread-" + counter.getAndIncrement());
 		}
 	}
+
+	public void setAsynchronousPush(boolean asynchronousPush)
+	{
+		this.asynchronousPush = asynchronousPush;
+	}
+
+	public boolean isAsynchronousPush()
+	{
+		return asynchronousPush;
+	}
+
+	public void setAsynchronousPushTimeout(long asynchronousPushTimeout)
+	{
+		this.asynchronousPushTimeout = asynchronousPushTimeout;
+	}
+
+	public long getAsynchronousPushTimeout()
+	{
+		return asynchronousPushTimeout;
+	}
 }
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketConnection.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketConnection.java
index 3cd0f627d4..1b6154a93d 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketConnection.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/AbstractWebSocketConnection.java
@@ -21,6 +21,8 @@ import org.apache.wicket.protocol.ws.api.message.IWebSocketPushMessage;
 import org.apache.wicket.protocol.ws.api.registry.IKey;
 import org.apache.wicket.util.lang.Args;
 
+import java.util.concurrent.Future;
+
 /**
  * Abstract class handling the Web Socket broadcast messages.
  */
@@ -50,7 +52,19 @@ public abstract class AbstractWebSocketConnection implements IWebSocketConnectio
 	@Override
 	public void sendMessage(IWebSocketPushMessage message)
 	{
-		webSocketProcessor.broadcastMessage(message, this);
+		webSocketProcessor.broadcastMessage(message, this, false, -1);
+	}
+
+	@Override
+	public void sendMessageAsync(IWebSocketPushMessage message)
+	{
+		webSocketProcessor.broadcastMessage(message, this, true, -1);
+	}
+
+	@Override
+	public void sendMessageAsync(IWebSocketPushMessage message, long timeout)
+	{
+		webSocketProcessor.broadcastMessage(message, this, true, timeout);
 	}
 
 	@Override
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 59e49d11dc..a1b5a065f8 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
@@ -183,7 +183,7 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 			}
 		}
 
-		broadcastMessage(new ConnectedMessage(getApplication(), getSessionId(), key), connection);
+		broadcastMessage(new ConnectedMessage(getApplication(), getSessionId(), key), connection, webSocketSettings.isAsynchronousPush(), webSocketSettings.getAsynchronousPushTimeout());
 	}
 
 	@Override
@@ -211,7 +211,7 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 	{
 		IKey key = getRegistryKey();
 		IWebSocketConnection connection = connectionRegistry.getConnection(application, sessionId, key);
-		broadcastMessage(message, connection);
+		broadcastMessage(message, connection, webSocketSettings.isAsynchronousPush(), webSocketSettings.getAsynchronousPushTimeout());
 	}
 
 	/**
@@ -226,7 +226,7 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 	 * @param message
 	 *      the message to broadcast
 	 */
-	public final void broadcastMessage(final IWebSocketMessage message, IWebSocketConnection connection)
+	public final void broadcastMessage(final IWebSocketMessage message, IWebSocketConnection connection, boolean asynchronousPush, long timeout)
 	{
 		if (connection != null && (connection.isOpen() || isSpecialMessage(message)))
 		{
@@ -234,7 +234,7 @@ public abstract class AbstractWebSocketProcessor implements IWebSocketProcessor
 			Session oldSession = ThreadContext.getSession();
 			RequestCycle oldRequestCycle = ThreadContext.getRequestCycle();
 
-			WebResponse webResponse = webSocketSettings.newWebSocketResponse(connection);
+			WebResponse webResponse = webSocketSettings.newWebSocketResponse(connection, asynchronousPush, timeout);
 			try
 			{
 				WebSocketRequestMapper requestMapper = new WebSocketRequestMapper(application.getRootRequestMapper());
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/IWebSocketConnection.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/IWebSocketConnection.java
index 7275559bd5..01c4a76dd0 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/IWebSocketConnection.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/IWebSocketConnection.java
@@ -71,11 +71,11 @@ public interface IWebSocketConnection
      *
      * @param message
      *      the text message
-     * @param timeOut
+     * @param timeout
      *      the timeout for operation
      * @return a {@link java.util.concurrent.Future} representing the send operation
      */
-    Future<Void> sendMessageAsync(String message, long timeOut);
+    Future<Void> sendMessageAsync(String message, long timeout);
 
 	/**
 	 * Sends a binary message to the client.
@@ -113,11 +113,11 @@ public interface IWebSocketConnection
      *      the offset to read from
      * @param length
      *      how much data to read
-     * @param timeOut
-     *      *      the timeout for operation
+     * @param timeout
+     *      the timeout for operation
      * @return a {@link java.util.concurrent.Future} representing the send operation
      */
-    Future<Void> sendMessageAsync(byte[] message, int offset, int length, long timeOut);
+    Future<Void> sendMessageAsync(byte[] message, int offset, int length, long timeout);
 
 	/**
 	 * Broadcasts a push message to the wicket page (and it's components) associated with this
@@ -130,6 +130,31 @@ public interface IWebSocketConnection
 	 */
 	void sendMessage(IWebSocketPushMessage message);
 
+	/**
+	 * Broadcasts a push message to the wicket page (and it's components) associated with this
+	 * connection. The components can then send messages or component updates to client by adding
+	 * them to the target. Pushing to client is done asynchronously.
+	 *
+	 * @param message
+	 *     the push message to send
+	 *
+	 */
+	void sendMessageAsync(IWebSocketPushMessage message);
+
+
+	/**
+	 * Broadcasts a push message to the wicket page (and it's components) associated with this
+	 * connection. The components can then send messages or component updates to client by adding
+	 * them to the target. Pushing to client is done asynchronously.
+	 *
+	 * @param message
+	 *     the push message to send
+	 * @param timeout
+	 *     the timeout in milliseconds
+	 *
+	 */
+	void sendMessageAsync(IWebSocketPushMessage message, long timeout);
+
 	/**
 	 * @return The application for which this WebSocket connection is registered
 	 */
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/IWebSocketRequestHandler.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/IWebSocketRequestHandler.java
index 8d49155c28..eae4504d04 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/IWebSocketRequestHandler.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/IWebSocketRequestHandler.java
@@ -19,6 +19,8 @@ package org.apache.wicket.protocol.ws.api;
 import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
 import org.apache.wicket.request.ILoggableRequestHandler;
 
+import java.util.concurrent.Future;
+
 /**
  * An interface for outbound communication with web socket clients
  *
@@ -34,6 +36,28 @@ public interface IWebSocketRequestHandler extends IPartialPageRequestHandler, IL
 	 */
 	void push(CharSequence message);
 
+	/**
+	 * Pushes a text message to the client in an asynchronous way.
+	 *
+	 * @param message
+	 *      the text message to push to the client if the web socket connection is open
+	 * @return
+	 *      a {@link java.util.concurrent.Future} representing the send operation. Or null if connection is closed.
+	 */
+	Future<Void> pushAsync(CharSequence message);
+
+	/**
+	 * Pushes a text message to the client in an asynchronous way.
+	 *
+	 * @param message
+	 *      the text message to push to the client if the web socket connection is open
+	 * @param timeout
+	 *      the timeout for operation
+	 * @return
+	 * 		a {@link java.util.concurrent.Future} representing the send operation. Or null if connection is closed.
+	 */
+	Future<Void> pushAsync(CharSequence message, long timeout);
+
 	/**
 	 * Pushes a binary message to the client.
 	 *
@@ -45,4 +69,34 @@ public interface IWebSocketRequestHandler extends IPartialPageRequestHandler, IL
 	 *      how many bytes to read from the message
 	 */
 	void push(byte[] message, int offset, int length);
+
+	/**
+	 * Pushes a binary message to the client.
+	 *
+	 * @param message
+	 *      the binary message to push to the client if the web socket connection is open
+	 * @param offset
+	 *      the offset to start to read from the message
+	 * @param length
+	 *      how many bytes to read from the message
+	 * @return
+	 * 		a {@link java.util.concurrent.Future} representing the send operation. Or null if connection is closed.
+	 */
+	Future<Void> pushAsync(byte[] message, int offset, int length);
+
+	/**
+	 * Pushes a binary message to the client.
+	 *
+	 * @param message
+	 *      the binary message to push to the client if the web socket connection is open
+	 * @param offset
+	 *      the offset to start to read from the message
+	 * @param length
+	 *      how many bytes to read from the message
+	 * @param timeout
+	 *      the timeout for operation
+	 * @return
+	 * 		a {@link java.util.concurrent.Future} representing the send operation. Or null if connection is closed.
+	 */
+	Future<Void> pushAsync(byte[] message, int offset, int length, long timeout);
 }
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketPushBroadcaster.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketPushBroadcaster.java
index c5f6d78e02..8ddb4ab3ed 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketPushBroadcaster.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketPushBroadcaster.java
@@ -189,7 +189,14 @@ public class WebSocketPushBroadcaster
 				@Override
 				public void run()
 				{
-					wsConnection.sendMessage(message);
+					if (webSocketSettings.isAsynchronousPush())
+					{
+						wsConnection.sendMessageAsync(message, webSocketSettings.getAsynchronousPushTimeout());
+					}
+					else
+					{
+						wsConnection.sendMessage(message);
+					}
 				}
 			});
 		}
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketRequestHandler.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketRequestHandler.java
index 7ce1f620b5..3ceb8cf1d0 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketRequestHandler.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketRequestHandler.java
@@ -19,6 +19,7 @@ package org.apache.wicket.protocol.ws.api;
 import java.io.IOException;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.concurrent.Future;
 
 import org.apache.wicket.Component;
 import org.apache.wicket.Page;
@@ -77,6 +78,27 @@ public class WebSocketRequestHandler extends AbstractPartialPageRequestHandler i
 		}
 	}
 
+	@Override
+	public Future<Void> pushAsync(CharSequence message, long timeout)
+	{
+		if (connection.isOpen())
+		{
+			Args.notNull(message, "message");
+			return connection.sendMessageAsync(message.toString(), timeout);
+		}
+		else
+		{
+			LOG.warn("The websocket connection is already closed. Cannot push the text message '{}'", message);
+		}
+		return null;
+	}
+
+	@Override
+	public Future<Void> pushAsync(CharSequence message)
+	{
+		return pushAsync(message, -1);
+	}
+
 	@Override
 	public void push(byte[] message, int offset, int length)
 	{
@@ -97,6 +119,27 @@ public class WebSocketRequestHandler extends AbstractPartialPageRequestHandler i
 		}
 	}
 
+	@Override
+	public Future<Void> pushAsync(byte[] message, int offset, int length)
+	{
+		return pushAsync(message, offset, length, -1);
+	}
+
+	@Override
+	public Future<Void> pushAsync(byte[] message, int offset, int length, long timeout)
+	{
+		if (connection.isOpen())
+		{
+			Args.notNull(message, "message");
+			return connection.sendMessageAsync(message, offset, length, timeout);
+		}
+		else
+		{
+			LOG.warn("The websocket connection is already closed. Cannot push the binary message '{}'", message);
+		}
+		return java.util.concurrent.CompletableFuture.completedFuture(null);
+	}
+
 	/**
 	 * @return if <code>true</code> then EMPTY partial updates will se send. If <code>false</code> then EMPTY
 	 *    partial updates will be skipped. A possible use case is: a page receives and a push event but no one is
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketResponse.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketResponse.java
index 1064c7c7d7..05669cd8b0 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketResponse.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/api/WebSocketResponse.java
@@ -47,9 +47,20 @@ public class WebSocketResponse extends WebResponse
 
 	private boolean isRedirect = false;
 
+	private final boolean asynchronous;
+
+	private final long timeout;
+
 	public WebSocketResponse(final IWebSocketConnection conn)
+	{
+		this(conn, false, -1);
+	}
+
+	public WebSocketResponse(final IWebSocketConnection conn, boolean asynchronous, long timeout)
 	{
 		this.connection = conn;
+		this.asynchronous = asynchronous;
+		this.timeout = timeout;
 	}
 
 	@Override
@@ -87,13 +98,27 @@ public class WebSocketResponse extends WebResponse
 			{
 				if (text != null)
 				{
-					connection.sendMessage(text.toString());
+					if (asynchronous)
+					{
+						connection.sendMessageAsync(text.toString(), timeout);
+					}
+					else
+					{
+						connection.sendMessage(text.toString());
+					}
 					text = null;
 				}
 				else if (binary != null)
 				{
 					byte[] bytes = binary.toByteArray();
-					connection.sendMessage(bytes, 0, bytes.length);
+					if (asynchronous)
+					{
+                       connection.sendMessageAsync(bytes, 0, bytes.length, timeout);
+					}
+					else
+					{
+						connection.sendMessage(bytes, 0, bytes.length);
+					}
 					binary.close();
 					binary = null;
 				}
diff --git a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketConnection.java b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketConnection.java
index 28dce651a5..ed4bd3748e 100644
--- a/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketConnection.java
+++ b/wicket-native-websocket/wicket-native-websocket-core/src/main/java/org/apache/wicket/protocol/ws/util/tester/TestWebSocketConnection.java
@@ -22,6 +22,7 @@ import java.util.concurrent.Future;
 import org.apache.wicket.Application;
 import org.apache.wicket.protocol.http.WebApplication;
 import org.apache.wicket.protocol.ws.api.IWebSocketConnection;
+import org.apache.wicket.protocol.ws.api.message.IWebSocketPushMessage;
 import org.apache.wicket.protocol.ws.api.registry.IKey;
 
 /**
@@ -92,14 +93,14 @@ abstract class TestWebSocketConnection implements IWebSocketConnection
     }
 
     @Override
-    public Future<Void> sendMessageAsync(byte[] message, int offset, int length, long timeOut)
+    public Future<Void> sendMessageAsync(byte[] message, int offset, int length, long timeout)
     {
         checkOpenness();
         onOutMessage(message, offset, length);
         return null;
     }
 
-    /**
+	/**
 	 * A callback method that is called when a text message should be send to the client
 	 *
 	 * @param message
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 6e70466d0b..f5efb54d2b 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
@@ -158,6 +158,18 @@ abstract class TestWebSocketProcessor extends AbstractWebSocketProcessor
 			{
 				TestWebSocketProcessor.this.broadcastMessage(message);
 			}
+
+			@Override
+			public void sendMessageAsync(IWebSocketPushMessage message, long timeout)
+			{
+				TestWebSocketProcessor.this.broadcastMessage(message);
+			}
+
+			@Override
+			public void sendMessageAsync(IWebSocketPushMessage message)
+			{
+				TestWebSocketProcessor.this.broadcastMessage(message);
+			}
 		});
 	}