Skip to content

Commit e52cefb

Browse files
authored
Merge pull request #1361 from fl4via/2.2.x_backport_bug_fixes
2.2.x backport bug fixes
2 parents 9a06b56 + 215316d commit e52cefb

File tree

8 files changed

+317
-22
lines changed

8 files changed

+317
-22
lines changed

core/src/main/java/io/undertow/conduits/ReadTimeoutStreamSourceConduit.java

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,6 @@
1818

1919
package io.undertow.conduits;
2020

21-
import java.io.IOException;
22-
import java.nio.ByteBuffer;
23-
import java.nio.channels.FileChannel;
24-
import java.util.concurrent.TimeUnit;
25-
2621
import io.undertow.UndertowLogger;
2722
import io.undertow.UndertowMessages;
2823
import io.undertow.UndertowOptions;
@@ -41,6 +36,11 @@
4136
import org.xnio.conduits.ReadReadyHandler;
4237
import org.xnio.conduits.StreamSourceConduit;
4338

39+
import java.io.IOException;
40+
import java.nio.ByteBuffer;
41+
import java.nio.channels.FileChannel;
42+
import java.util.concurrent.TimeUnit;
43+
4444
/**
4545
* Wrapper for read timeout. This should always be the first wrapper applied to the underlying channel.
4646
*
@@ -49,7 +49,7 @@
4949
*/
5050
public final class ReadTimeoutStreamSourceConduit extends AbstractStreamSourceConduit<StreamSourceConduit> {
5151

52-
private XnioExecutor.Key handle;
52+
private volatile XnioExecutor.Key handle;
5353
private final StreamConnection connection;
5454
private volatile long expireTime = -1;
5555
private final OpenListener openListener;
@@ -60,14 +60,21 @@ public final class ReadTimeoutStreamSourceConduit extends AbstractStreamSourceCo
6060
private final Runnable timeoutCommand = new Runnable() {
6161
@Override
6262
public void run() {
63-
handle = null;
64-
if (expireTime == -1) {
63+
synchronized (ReadTimeoutStreamSourceConduit.this) {
64+
handle = null;
65+
}
66+
if (expireTime == -1 || !connection.isOpen()) {
6567
return;
6668
}
6769
long current = System.currentTimeMillis();
6870
if (current < expireTime) {
6971
//timeout has been bumped, re-schedule
70-
handle = WorkerUtils.executeAfter(connection.getIoThread(),timeoutCommand, (expireTime - current) + FUZZ_FACTOR, TimeUnit.MILLISECONDS);
72+
if (handle == null) {
73+
synchronized (ReadTimeoutStreamSourceConduit.this) {
74+
if (handle == null)
75+
handle = WorkerUtils.executeAfter(connection.getIoThread(), timeoutCommand, (expireTime - current) + FUZZ_FACTOR, TimeUnit.MILLISECONDS);
76+
}
77+
}
7178
return;
7279
}
7380
UndertowLogger.REQUEST_LOGGER.tracef("Timing out channel %s due to inactivity", connection.getSourceChannel());
@@ -131,12 +138,16 @@ private void handleReadTimeout(final long ret) throws IOException {
131138
final long expireTimeVar = expireTime;
132139
if (expireTimeVar != -1 && currentTime > expireTimeVar) {
133140
IoUtils.safeClose(connection);
134-
throw UndertowMessages.MESSAGES.readTimedOut(this.getTimeout());
141+
throw UndertowMessages.MESSAGES.readTimedOut(currentTime - (expireTimeVar - this.getTimeout()));
135142
}
136143
}
137144
expireTime = currentTime + timeout;
138145
if (handle == null) {
139-
handle = connection.getIoThread().executeAfter(timeoutCommand, timeout, TimeUnit.MILLISECONDS);
146+
synchronized (this) {
147+
if (handle == null)
148+
handle = connection.getIoThread().executeAfter(timeoutCommand, timeout, TimeUnit.MILLISECONDS);
149+
}
150+
140151
}
141152
}
142153

@@ -232,9 +243,13 @@ public void terminateReads() throws IOException {
232243

233244
private void cleanup() {
234245
if (handle != null) {
235-
handle.remove();
236-
handle = null;
237-
expireTime = -1;
246+
synchronized (this) {
247+
if (handle != null) {
248+
handle.remove();
249+
handle = null;
250+
expireTime = -1;
251+
}
252+
}
238253
}
239254
}
240255

@@ -247,7 +262,7 @@ public void suspendReads() {
247262
private void checkExpired() throws ReadTimeoutException {
248263
synchronized (this) {
249264
if (expired) {
250-
throw UndertowMessages.MESSAGES.readTimedOut(System.currentTimeMillis());
265+
throw UndertowMessages.MESSAGES.readTimedOut(System.currentTimeMillis() - (expireTime - getTimeout()));
251266
}
252267
}
253268
}

core/src/main/java/io/undertow/websockets/core/WebSocketChannel.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import io.undertow.websockets.extensions.ExtensionFunction;
2525
import org.xnio.ChannelExceptionHandler;
2626
import org.xnio.ChannelListener;
27+
import org.xnio.ChannelListener.SimpleSetter;
2728
import org.xnio.ChannelListeners;
2829
import org.xnio.IoUtils;
2930
import org.xnio.OptionMap;
@@ -82,6 +83,7 @@ public abstract class WebSocketChannel extends AbstractFramedChannel<WebSocketCh
8283
*/
8384
private final Set<WebSocketChannel> peerConnections;
8485

86+
private static final CloseMessage CLOSE_MSG = new CloseMessage(CloseMessage.GOING_AWAY, WebSocketMessages.MESSAGES.messageCloseWebSocket());
8587
/**
8688
* Create a new {@link WebSocketChannel}
8789
* 8
@@ -158,6 +160,15 @@ protected void lastDataRead() {
158160
} catch (IOException e) {
159161
IoUtils.safeClose(this);
160162
}
163+
final ChannelListener<?> listener = ((SimpleSetter<WebSocketChannel>)getReceiveSetter()).get();
164+
if(listener instanceof AbstractReceiveListener) {
165+
final AbstractReceiveListener abstractReceiveListener = (AbstractReceiveListener) listener;
166+
try {
167+
abstractReceiveListener.onCloseMessage(CLOSE_MSG, this);
168+
} catch(Exception e) {
169+
e.printStackTrace();
170+
}
171+
}
161172
}
162173
}
163174

core/src/main/java/io/undertow/websockets/core/WebSocketMessages.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,7 @@ public interface WebSocketMessages {
171171

172172
@Message(id = 2045, value = "Unable to send on newly created channel!")
173173
IllegalStateException unableToSendOnNewChannel();
174+
175+
@Message(id = 2046, value = "Closing WebSocket, peer went away.")
176+
String messageCloseWebSocket();
174177
}

core/src/test/java/io/undertow/websockets/core/protocol/AbstractWebSocketServerTest.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@
2626
import io.undertow.websockets.core.AbstractReceiveListener;
2727
import io.undertow.websockets.core.BufferedBinaryMessage;
2828
import io.undertow.websockets.core.BufferedTextMessage;
29+
import io.undertow.websockets.core.CloseMessage;
2930
import io.undertow.websockets.core.WebSocketCallback;
3031
import io.undertow.websockets.core.WebSocketChannel;
32+
import io.undertow.websockets.core.WebSocketMessages;
3133
import io.undertow.websockets.core.WebSockets;
3234
import io.undertow.websockets.spi.WebSocketHttpExchange;
3335
import io.undertow.websockets.utils.FrameChecker;
@@ -46,6 +48,7 @@
4648
import java.io.IOException;
4749
import java.net.URI;
4850
import java.nio.ByteBuffer;
51+
import java.util.concurrent.TimeUnit;
4952
import java.util.concurrent.atomic.AtomicBoolean;
5053

5154
/**
@@ -167,6 +170,50 @@ protected void onFullCloseMessage(WebSocketChannel channel, BufferedBinaryMessag
167170
client.destroy();
168171
}
169172

173+
@Test
174+
public void testCloseOnPeerGone() throws Exception {
175+
if (getVersion() == WebSocketVersion.V00) {
176+
// ignore 00 tests for now
177+
return;
178+
}
179+
final AtomicBoolean connected = new AtomicBoolean(false);
180+
final FutureResult<CloseMessage> latch = new FutureResult();
181+
DefaultServer.setRootHandler(new WebSocketProtocolHandshakeHandler(new WebSocketConnectionCallback() {
182+
@Override
183+
public void onConnect(final WebSocketHttpExchange exchange, final WebSocketChannel channel) {
184+
connected.set(true);
185+
channel.getReceiveSetter().set(new AbstractReceiveListener() {
186+
187+
@Override
188+
protected void onFullTextMessage(WebSocketChannel channel, BufferedTextMessage message) {
189+
Assert.fail();
190+
}
191+
192+
@Override
193+
protected void onCloseMessage(CloseMessage msg, WebSocketChannel channel) {
194+
latch.setResult(msg);
195+
}
196+
197+
@Override
198+
protected void onError(WebSocketChannel channel, Throwable t) {
199+
Assert.fail();
200+
}
201+
});
202+
channel.resumeReceives();
203+
}
204+
}));
205+
206+
WebSocketTestClient client = new WebSocketTestClient(getVersion(),
207+
new URI("ws://" + NetworkUtils.formatPossibleIpv6Address(DefaultServer.getHostAddress("default")) + ":"
208+
+ DefaultServer.getHostPort("default") + "/"));
209+
client.connect();
210+
client.destroy(true);
211+
latch.getIoFuture().await(5000, TimeUnit.MILLISECONDS);
212+
final CloseMessage msg = latch.getIoFuture().get();
213+
Assert.assertNotNull(msg);
214+
Assert.assertEquals(WebSocketMessages.MESSAGES.messageCloseWebSocket(), msg.getReason());
215+
}
216+
170217
protected WebSocketVersion getVersion() {
171218
return WebSocketVersion.V00;
172219
}

core/src/test/java/io/undertow/websockets/utils/WebSocketTestClient.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,11 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws E
137137
* Destroy the client and also close open connections if any exist
138138
*/
139139
public void destroy() {
140-
if (!closed) {
140+
this.destroy(false);
141+
}
142+
143+
public void destroy(boolean dirty) {
144+
if (!closed && !dirty) {
141145
final CountDownLatch latch = new CountDownLatch(1);
142146
send(new CloseWebSocketFrame(), new FrameListener() {
143147
@Override

servlet/src/main/java/io/undertow/servlet/handlers/security/SecurityPathMatches.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,14 @@ public SecurityPathMatch getSecurityInfo(final String path, final String method)
139139
handleMatch(method, extensionMatch, currentMatch);
140140
return new SecurityPathMatch(currentMatch.type, mergeConstraints(currentMatch));
141141
}
142+
143+
// if nothing else, check for security info defined for URL pattern '/'
144+
match = exactPathRoleInformation.get("/");
145+
if (match != null) {
146+
handleMatch(method, match, currentMatch);
147+
return new SecurityPathMatch(currentMatch.type, mergeConstraints(currentMatch));
148+
}
149+
142150
return new SecurityPathMatch(currentMatch.type, mergeConstraints(currentMatch));
143151
}
144152

servlet/src/test/java/io/undertow/servlet/test/security/constraint/SecurityConstraintUrlMappingTestCase.java

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@
1818

1919
package io.undertow.servlet.test.security.constraint;
2020

21-
import java.io.IOException;
22-
23-
import javax.servlet.ServletException;
24-
2521
import io.undertow.server.handlers.PathHandler;
2622
import io.undertow.servlet.api.DeploymentInfo;
2723
import io.undertow.servlet.api.DeploymentManager;
@@ -36,18 +32,21 @@
3632
import io.undertow.servlet.test.util.TestClassIntrospector;
3733
import io.undertow.testutils.DefaultServer;
3834
import io.undertow.testutils.HttpClientUtils;
35+
import io.undertow.testutils.TestHttpClient;
3936
import io.undertow.util.FlexBase64;
37+
import io.undertow.util.StatusCodes;
4038
import org.apache.http.Header;
4139
import org.apache.http.HttpResponse;
4240
import org.apache.http.client.methods.HttpGet;
4341
import org.apache.http.client.methods.HttpPost;
44-
import io.undertow.testutils.TestHttpClient;
45-
import io.undertow.util.StatusCodes;
4642
import org.junit.Assert;
4743
import org.junit.BeforeClass;
4844
import org.junit.Test;
4945
import org.junit.runner.RunWith;
5046

47+
import javax.servlet.ServletException;
48+
import java.io.IOException;
49+
5150
import static io.undertow.util.Headers.AUTHORIZATION;
5251
import static io.undertow.util.Headers.BASIC;
5352
import static io.undertow.util.Headers.WWW_AUTHENTICATE;
@@ -196,6 +195,19 @@ public void testAggregatedRoles() throws IOException {
196195
runSimpleUrlTest(DefaultServer.getDefaultServerURL() + "/servletContext/secured/1/2/aa", "user1:password1", "user2:password2");
197196
}
198197

198+
@Test
199+
public void testUnknown() throws IOException {
200+
TestHttpClient client = new TestHttpClient();
201+
try {
202+
HttpGet get = new HttpGet(DefaultServer.getDefaultServerURL() + "/servletContext/unknown");
203+
HttpResponse result = client.execute(get);
204+
assertEquals(StatusCodes.NOT_FOUND, result.getStatusLine().getStatusCode());
205+
HttpClientUtils.readResponse(result);
206+
} finally {
207+
client.getConnectionManager().shutdown();
208+
}
209+
}
210+
199211
@Test
200212
public void testHttpMethod() throws IOException {
201213
TestHttpClient client = new TestHttpClient();

0 commit comments

Comments
 (0)