[Git][java-team/undertow][upstream] New upstream version 2.0.29
Markus Koschany
gitlab at salsa.debian.org
Wed Jan 8 22:20:46 GMT 2020
Markus Koschany pushed to branch upstream at Debian Java Maintainers / undertow
Commits:
dad62d54 by Markus Koschany at 2020-01-08T22:52:38+01:00
New upstream version 2.0.29
- - - - -
25 changed files:
- benchmarks/pom.xml
- core/pom.xml
- core/src/main/java/io/undertow/UndertowLogger.java
- core/src/main/java/io/undertow/protocols/ajp/AjpClientChannel.java
- core/src/main/java/io/undertow/protocols/http2/Http2Channel.java
- core/src/main/java/io/undertow/protocols/ssl/UndertowAcceptingSslChannel.java
- core/src/main/java/io/undertow/protocols/ssl/UndertowXnioSsl.java
- core/src/main/java/io/undertow/server/handlers/encoding/RequestEncodingHandler.java
- core/src/main/java/io/undertow/server/protocol/framed/AbstractFramedChannel.java
- core/src/main/java/io/undertow/server/protocol/framed/AbstractFramedStreamSinkChannel.java
- core/src/main/java/io/undertow/server/protocol/framed/AbstractFramedStreamSourceChannel.java
- core/src/main/java/io/undertow/websockets/core/WebSocketChannel.java
- core/src/main/resources/META-INF/services/io.undertow.server.handlers.builder.HandlerBuilder
- core/src/test/java/io/undertow/server/handlers/RangeRequestTestCase.java
- core/src/test/java/io/undertow/server/handlers/largerange.txt
- coverage-report/pom.xml
- dist/pom.xml
- examples/pom.xml
- karaf/pom.xml
- parser-generator/pom.xml
- pom.xml
- servlet/pom.xml
- servlet/src/main/java/io/undertow/servlet/compat/rewrite/RewriteHandler.java
- websockets-jsr/pom.xml
- websockets-jsr/src/main/java/io/undertow/websockets/jsr/ServerWebSocketContainer.java
Changes:
=====================================
benchmarks/pom.xml
=====================================
@@ -25,12 +25,12 @@
<parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-parent</artifactId>
- <version>2.0.28.Final</version>
+ <version>2.0.29.Final</version>
</parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-benchmarks</artifactId>
- <version>2.0.28.Final</version>
+ <version>2.0.29.Final</version>
<name>Undertow Benchmarks</name>
=====================================
core/pom.xml
=====================================
@@ -25,12 +25,12 @@
<parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-parent</artifactId>
- <version>2.0.28.Final</version>
+ <version>2.0.29.Final</version>
</parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-core</artifactId>
- <version>2.0.28.Final</version>
+ <version>2.0.29.Final</version>
<name>Undertow Core</name>
=====================================
core/src/main/java/io/undertow/UndertowLogger.java
=====================================
@@ -401,9 +401,9 @@ public interface UndertowLogger extends BasicLogger {
@Message(id = 5086, value = "Failed to accept SSL request")
void failedToAcceptSSLRequest(@Cause Exception e);
- @LogMessage(level = ERROR)
- @Message(id = 5087, value = "Failed to use the server order")
- void failedToUseServerOrder(@Cause ReflectiveOperationException e);
+// @LogMessage(level = ERROR)
+// @Message(id = 5087, value = "Failed to use the server order")
+// void failedToUseServerOrder(@Cause ReflectiveOperationException e);
@LogMessage(level = ERROR)
@Message(id = 5088, value = "Failed to execute ServletOutputStream.closeAsync() on IO thread")
=====================================
core/src/main/java/io/undertow/protocols/ajp/AjpClientChannel.java
=====================================
@@ -169,7 +169,7 @@ public class AjpClientChannel extends AbstractFramedChannel<AjpClientChannel, Ab
return lastFrameSent;
}
- protected void lastDataRead() {
+ protected synchronized void lastDataRead() {
if(!lastFrameSent) {
markReadsBroken(new ClosedChannelException());
markWritesBroken(new ClosedChannelException());
=====================================
core/src/main/java/io/undertow/protocols/http2/Http2Channel.java
=====================================
@@ -587,17 +587,22 @@ public class Http2Channel extends AbstractFramedChannel<Http2Channel, AbstractHt
}
protected void lastDataRead() {
- lastDataRead = true;
- if(!peerGoneAway) {
+ final boolean peerGoneAway;
+ synchronized (this) {
+ lastDataRead = true;
+ peerGoneAway = this.peerGoneAway;
+ if(peerGoneAway) {
+ if(!thisGoneAway) {
+ //we send a goaway message, and then close
+ sendGoAway(ERROR_CONNECT_ERROR);
+ }
+ }
+ }
+ if (!peerGoneAway) {
//we just close the connection, as the peer has performed an unclean close
IoUtils.safeClose(this);
- } else {
- peerGoneAway = true;
- if(!thisGoneAway) {
- //we send a goaway message, and then close
- sendGoAway(ERROR_CONNECT_ERROR);
- }
}
+
}
@Override
=====================================
core/src/main/java/io/undertow/protocols/ssl/UndertowAcceptingSslChannel.java
=====================================
@@ -38,8 +38,6 @@ import org.xnio.channels.AcceptingChannel;
import org.xnio.ssl.SslConnection;
import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
@@ -59,18 +57,6 @@ import javax.net.ssl.SSLParameters;
*/
class UndertowAcceptingSslChannel implements AcceptingChannel<SslConnection> {
- static final Method USE_CIPHER_SUITES_METHOD;
-
- static {
- Method method = null;
- try {
- method = SSLParameters.class.getDeclaredMethod("setUseCipherSuitesOrder", boolean.class);
- method.setAccessible(true);
- } catch (NoSuchMethodException e) {
- }
- USE_CIPHER_SUITES_METHOD = method;
- }
-
private final UndertowXnioSsl ssl;
private final AcceptingChannel<? extends StreamConnection> tcpServer;
@@ -158,14 +144,10 @@ class UndertowAcceptingSslChannel implements AcceptingChannel<SslConnection> {
final InetSocketAddress peerAddress = tcpConnection.getPeerAddress(InetSocketAddress.class);
final SSLEngine engine = ssl.getSslContext().createSSLEngine(getHostNameNoResolve(peerAddress), peerAddress.getPort());
- if(USE_CIPHER_SUITES_METHOD != null && useCipherSuitesOrder) {
+ if(useCipherSuitesOrder) {
SSLParameters sslParameters = engine.getSSLParameters();
- try {
- USE_CIPHER_SUITES_METHOD.invoke(sslParameters, true);
- engine.setSSLParameters(sslParameters);
- } catch (IllegalAccessException | InvocationTargetException e) {
- UndertowLogger.ROOT_LOGGER.failedToUseServerOrder(e);
- }
+ sslParameters.setUseCipherSuitesOrder(true);
+ engine.setSSLParameters(sslParameters);
}
final boolean clientMode = useClientMode != 0;
engine.setUseClientMode(clientMode);
=====================================
core/src/main/java/io/undertow/protocols/ssl/UndertowXnioSsl.java
=====================================
@@ -21,8 +21,6 @@ package io.undertow.protocols.ssl;
import static org.xnio.IoUtils.safeClose;
import java.io.IOException;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URI;
@@ -42,7 +40,6 @@ import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
-import io.undertow.UndertowLogger;
import io.undertow.UndertowOptions;
import org.xnio.ChannelListener;
import org.xnio.ChannelListeners;
@@ -82,18 +79,6 @@ public class UndertowXnioSsl extends XnioSsl {
private final ByteBufferPool bufferPool;
private volatile SSLContext sslContext;
- private static final Method USE_CIPHER_SUITES_METHOD;
-
- static {
- Method method = null;
- try {
- method = SSLParameters.class.getDeclaredMethod("setUseCipherSuitesOrder", boolean.class);
- method.setAccessible(true);
- } catch (NoSuchMethodException e) {
- }
- USE_CIPHER_SUITES_METHOD = method;
- }
-
/**
* Construct a new instance.
*
@@ -298,14 +283,10 @@ public class UndertowXnioSsl extends XnioSsl {
}
}
boolean useCipherSuitesOrder = optionMap.get(UndertowOptions.SSL_USER_CIPHER_SUITES_ORDER, false);
- if (USE_CIPHER_SUITES_METHOD != null && useCipherSuitesOrder) {
+ if (useCipherSuitesOrder) {
SSLParameters sslParameters = engine.getSSLParameters();
- try {
- USE_CIPHER_SUITES_METHOD.invoke(sslParameters, true);
- engine.setSSLParameters(sslParameters);
- } catch (IllegalAccessException | InvocationTargetException e) {
- UndertowLogger.ROOT_LOGGER.failedToUseServerOrder(e);
- }
+ sslParameters.setUseCipherSuitesOrder(true);
+ engine.setSSLParameters(sslParameters);
}
final String endpointIdentificationAlgorithm = optionMap.get(UndertowOptions.ENDPOINT_IDENTIFICATION_ALGORITHM, null);
if (endpointIdentificationAlgorithm != null) {
=====================================
core/src/main/java/io/undertow/server/handlers/encoding/RequestEncodingHandler.java
=====================================
@@ -23,6 +23,7 @@ import java.util.Map;
import java.util.Set;
import org.xnio.conduits.StreamSourceConduit;
+import io.undertow.conduits.GzipStreamSourceConduit;
import io.undertow.conduits.InflatingStreamSourceConduit;
import io.undertow.server.ConduitWrapper;
import io.undertow.server.HandlerWrapper;
@@ -108,6 +109,7 @@ public class RequestEncodingHandler implements HttpHandler {
@Override
public HttpHandler wrap(HttpHandler handler) {
return new RequestEncodingHandler(handler)
+ .addEncoding("gzip", GzipStreamSourceConduit.WRAPPER)
.addEncoding("deflate", InflatingStreamSourceConduit.WRAPPER);
}
};
=====================================
core/src/main/java/io/undertow/server/protocol/framed/AbstractFramedChannel.java
=====================================
@@ -139,8 +139,9 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
private final Runnable taskRunQueueRunnable = new Runnable() {
@Override
public void run() {
- while (!taskRunQueue.isEmpty()) {
- taskRunQueue.poll().run();
+ Runnable runnable;
+ while ((runnable = taskRunQueue.poll()) != null) {
+ runnable.run();
}
}
};
@@ -341,173 +342,185 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
* existing source channels. In general if you suspend receives or don't have some other way
* of calling this method then it can prevent frame channels for being fully consumed.
*/
- public synchronized R receive() throws IOException {
- if (readChannelDone && receiver == null) {
- //we have received the last frame, we just shut down and return
- //it would probably make more sense to have the last channel responsible for this
- //however it is much simpler just to have it here
- if(readData != null) {
- readData.close();
- readData = null;
- }
- channel.getSourceChannel().suspendReads();
- channel.getSourceChannel().shutdownReads();
- return null;
- }
- partialRead = false;
- boolean requiresReinvoke = false;
- int reinvokeDataRemaining = 0;
- ReferenceCountedPooled pooled = this.readData;
- boolean hasData = false;
- if (pooled == null) {
- pooled = allocateReferenceCountedBuffer();
- if (pooled == null) {
- return null;
- }
- } else if(pooled.isFreed()) {
- //we attempt to re-used an existing buffer
- if(!pooled.tryUnfree()) {
- pooled = allocateReferenceCountedBuffer();
- if (pooled == null) {
- return null;
- }
- }
- pooled.getBuffer().clear();
- } else {
- hasData = pooled.getBuffer().hasRemaining();
- pooled.getBuffer().compact();
- }
- boolean forceFree = false;
- int read = 0;
+ public R receive() throws IOException {
+ // store in a local variable to prevent invoking lastDataRead twice
+ boolean receivedMinusOne = false;
try {
- read = channel.getSourceChannel().read(pooled.getBuffer());
- if (read == 0 && !hasData) {
- //no data, we just free the buffer
- forceFree = true;
- return null;
- } else if (read == -1 && !hasData) {
- forceFree = true;
- readChannelDone = true;
- lastDataRead();
- return null;
- } else if(isLastFrameReceived() && frameDataRemaining == 0) {
- //we got data, although we should have received the last frame
- forceFree = true;
- markReadsBroken(new ClosedChannelException());
- }
- pooled.getBuffer().flip();
- if(read == -1) {
- requiresReinvoke = true;
- reinvokeDataRemaining = pooled.getBuffer().remaining();
- }
- if (frameDataRemaining > 0) {
- if (frameDataRemaining >= pooled.getBuffer().remaining()) {
- frameDataRemaining -= pooled.getBuffer().remaining();
- if(receiver != null) {
- //we still create a pooled view, this means that if the buffer is still active we can re-used it
- //which prevents attacks based on sending lots of small fragments
- PooledByteBuffer frameData = pooled.createView();
- receiver.dataReady(null, frameData);
- } else {
- //we are dropping a frame
- pooled.close();
+ synchronized (this) {
+ if (readChannelDone && receiver == null) {
+ //we have received the last frame, we just shut down and return
+ //it would probably make more sense to have the last channel responsible for this
+ //however it is much simpler just to have it here
+ if(readData != null) {
+ readData.close();
readData = null;
}
- if(frameDataRemaining == 0) {
- receiver = null;
- }
+ channel.getSourceChannel().suspendReads();
+ channel.getSourceChannel().shutdownReads();
return null;
- } else {
- PooledByteBuffer frameData = pooled.createView((int) frameDataRemaining);
- frameDataRemaining = 0;
- if(receiver != null) {
- receiver.dataReady(null, frameData);
- } else{
- //we are dropping the frame
- frameData.close();
- }
- receiver = null;
}
- //if we read data into a frame we just return immediately, even if there is more remaining
- //see https://issues.jboss.org/browse/UNDERTOW-410
- //basically if we don't do this we loose some message ordering semantics
- //as the second message may be processed before the first one
-
- //this is problematic for HTTPS, where the read listener may also be invoked by a queued task
- //and not by the selector mechanism
- return null;
- }
- FrameHeaderData data = parseFrame(pooled.getBuffer());
- if (data != null) {
- PooledByteBuffer frameData;
- if (data.getFrameLength() >= pooled.getBuffer().remaining()) {
- frameDataRemaining = data.getFrameLength() - pooled.getBuffer().remaining();
- frameData = pooled.createView();
- pooled.getBuffer().position(pooled.getBuffer().limit());
+ partialRead = false;
+ boolean requiresReinvoke = false;
+ int reinvokeDataRemaining = 0;
+ ReferenceCountedPooled pooled = this.readData;
+ boolean hasData = false;
+ if (pooled == null) {
+ pooled = allocateReferenceCountedBuffer();
+ if (pooled == null) {
+ return null;
+ }
+ } else if(pooled.isFreed()) {
+ //we attempt to re-used an existing buffer
+ if(!pooled.tryUnfree()) {
+ pooled = allocateReferenceCountedBuffer();
+ if (pooled == null) {
+ return null;
+ }
+ }
+ pooled.getBuffer().clear();
} else {
- frameData = pooled.createView((int) data.getFrameLength());
+ hasData = pooled.getBuffer().hasRemaining();
+ pooled.getBuffer().compact();
}
- AbstractFramedStreamSourceChannel<?, ?, ?> existing = data.getExistingChannel();
- if (existing != null) {
- if (data.getFrameLength() > frameData.getBuffer().remaining()) {
- receiver = (R) existing;
+ boolean forceFree = false;
+ int read = 0;
+ try {
+ read = channel.getSourceChannel().read(pooled.getBuffer());
+ if (read == 0 && !hasData) {
+ //no data, we just free the buffer
+ forceFree = true;
+ return null;
+ } else if (read == -1 && !hasData) {
+ forceFree = true;
+ receivedMinusOne = readChannelDone = true;
+ return null;
+ } else if(isLastFrameReceived() && frameDataRemaining == 0) {
+ //we got data, although we should have received the last frame
+ forceFree = true;
+ markReadsBroken(new ClosedChannelException());
}
- existing.dataReady(data, frameData);
- if(isLastFrameReceived()) {
- handleLastFrame(existing);
+ pooled.getBuffer().flip();
+ if(read == -1) {
+ requiresReinvoke = true;
+ reinvokeDataRemaining = pooled.getBuffer().remaining();
}
- return null;
- } else {
- boolean moreData = data.getFrameLength() > frameData.getBuffer().remaining();
- R newChannel = createChannel(data, frameData);
- if (newChannel != null) {
- if (moreData) {
- receiver = newChannel;
+ if (frameDataRemaining > 0) {
+ if (frameDataRemaining >= pooled.getBuffer().remaining()) {
+ frameDataRemaining -= pooled.getBuffer().remaining();
+ if(receiver != null) {
+ //we still create a pooled view, this means that if the buffer is still active we can re-used it
+ //which prevents attacks based on sending lots of small fragments
+ PooledByteBuffer frameData = pooled.createView();
+ receiver.dataReady(null, frameData);
+ } else {
+ //we are dropping a frame
+ pooled.close();
+ readData = null;
+ }
+ if(frameDataRemaining == 0) {
+ receiver = null;
+ }
+ return null;
+ } else {
+ PooledByteBuffer frameData = pooled.createView((int) frameDataRemaining);
+ frameDataRemaining = 0;
+ if(receiver != null) {
+ receiver.dataReady(null, frameData);
+ } else{
+ //we are dropping the frame
+ frameData.close();
+ }
+ receiver = null;
}
+ //if we read data into a frame we just return immediately, even if there is more remaining
+ //see https://issues.jboss.org/browse/UNDERTOW-410
+ //basically if we don't do this we loose some message ordering semantics
+ //as the second message may be processed before the first one
+
+ //this is problematic for HTTPS, where the read listener may also be invoked by a queued task
+ //and not by the selector mechanism
+ return null;
+ }
+ FrameHeaderData data = parseFrame(pooled.getBuffer());
+ if (data != null) {
+ PooledByteBuffer frameData;
+ if (data.getFrameLength() >= pooled.getBuffer().remaining()) {
+ frameDataRemaining = data.getFrameLength() - pooled.getBuffer().remaining();
+ frameData = pooled.createView();
+ pooled.getBuffer().position(pooled.getBuffer().limit());
+ } else {
+ frameData = pooled.createView((int) data.getFrameLength());
+ }
+ AbstractFramedStreamSourceChannel<?, ?, ?> existing = data.getExistingChannel();
+ if (existing != null) {
+ if (data.getFrameLength() > frameData.getBuffer().remaining()) {
+ receiver = (R) existing;
+ }
+ existing.dataReady(data, frameData);
+ if (isLastFrameReceived()) {
+ handleLastFrame(existing);
+ }
+ return null;
+ } else {
+ boolean moreData = data.getFrameLength() > frameData.getBuffer().remaining();
+ R newChannel = createChannel(data, frameData);
+ if (newChannel != null) {
+ if (moreData) {
+ receiver = newChannel;
+ }
- if(isLastFrameReceived()) {
- handleLastFrame(newChannel);
+ if(isLastFrameReceived()) {
+ handleLastFrame(newChannel);
+ }
+ } else {
+ frameData.close();
+ }
+ return newChannel;
}
} else {
- frameData.close();
+ //we set partial read to true so the read listener knows not to immediately call receive again
+ partialRead = true;
}
- return newChannel;
- }
- } else {
- //we set partial read to true so the read listener knows not to immediately call receive again
- partialRead = true;
- }
- return null;
- } catch (IOException|RuntimeException|Error e) {
- //something has code wrong with parsing, close the read side
- //we don't close the write side, as the underlying implementation will most likely want to send an error
- markReadsBroken(e);
- forceFree = true;
- throw e;
- }finally {
- //if the receive caused the channel to break the close listener may be have been called
- //which will make readData null
- if (readData != null) {
- if (!pooled.getBuffer().hasRemaining() || forceFree) {
- if(pooled.getBuffer().capacity() < 1024 || forceFree) {
- //if there is less than 1k left we don't allow it to be re-aquired
- readData = null;
- }
- //even though this is freed we may un-free it if we get a new packet
- //this prevents many small reads resulting in a large number of allocated buffers
- pooled.close();
+ return null;
+ } catch (IOException|RuntimeException|Error e) {
+ //something has code wrong with parsing, close the read side
+ //we don't close the write side, as the underlying implementation will most likely want to send an error
+ markReadsBroken(e);
+ forceFree = true;
+ throw e;
+ }finally {
+ //if the receive caused the channel to break the close listener may be have been called
+ //which will make readData null
+ if (readData != null) {
+ if (!pooled.getBuffer().hasRemaining() || forceFree) {
+ if(pooled.getBuffer().capacity() < 1024 || forceFree) {
+ //if there is less than 1k left we don't allow it to be re-aquired
+ readData = null;
+ }
+ //even though this is freed we may un-free it if we get a new packet
+ //this prevents many small reads resulting in a large number of allocated buffers
+ pooled.close();
- }
- }
- if(requiresReinvoke) {
- if(readData != null && !readData.isFreed()) {
- if(readData.getBuffer().remaining() == reinvokeDataRemaining) {
- readData.close();
- readData = null;
- UndertowLogger.REQUEST_IO_LOGGER.debugf("Partial message read before connection close %s", this);
+ }
+ }
+ if(requiresReinvoke) {
+ if(readData != null && !readData.isFreed()) {
+ if(readData.getBuffer().remaining() == reinvokeDataRemaining) {
+ readData.close();
+ readData = null;
+ UndertowLogger.REQUEST_IO_LOGGER.debugf("Partial message read before connection close %s", this);
+ }
+ }
+ channel.getSourceChannel().wakeupReads();
}
}
- channel.getSourceChannel().wakeupReads();
+ }
+ } finally {
+ // read receivedMinusOne, and not readChannelDone
+ // to prevent lastDataRead being invoked twice in case of
+ // two concurrent receive invocations
+ if (receivedMinusOne) {
+ lastDataRead();
}
}
}
@@ -596,8 +609,8 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
flushingSenders = true;
try {
int toSend = 0;
- while (!newFrames.isEmpty()) {
- S frame = newFrames.poll();
+ S frame;
+ while ((frame = newFrames.poll()) != null) {
frame.preWrite();
if (framePriority.insertFrame(frame, pendingFrames)) {
if (!heldFrames.isEmpty()) {
@@ -813,13 +826,15 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
*/
@Override
public void close() throws IOException {
- if (UndertowLogger.REQUEST_IO_LOGGER.isTraceEnabled()) {
- UndertowLogger.REQUEST_IO_LOGGER.tracef(new ClosedChannelException(), "Channel %s is being closed", this);
- }
- safeClose(channel);
- if (readData != null) {
- readData.close();
- readData = null;
+ synchronized (this) {
+ if (UndertowLogger.REQUEST_IO_LOGGER.isTraceEnabled()) {
+ UndertowLogger.REQUEST_IO_LOGGER.tracef(new ClosedChannelException(), "Channel %s is being closed", this);
+ }
+ safeClose(channel);
+ if (readData != null) {
+ readData.close();
+ readData = null;
+ }
}
closeSubChannels();
}
@@ -933,8 +948,9 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
@Override
public void handleEvent(final StreamSourceChannel channel) {
//clear the task queue before reading
- while (!taskRunQueue.isEmpty()) {
- taskRunQueue.poll().run();
+ Runnable runnable;
+ while ((runnable = taskRunQueue.poll()) != null) {
+ runnable.run();
}
final R receiver = AbstractFramedChannel.this.receiver;
=====================================
core/src/main/java/io/undertow/server/protocol/framed/AbstractFramedStreamSinkChannel.java
=====================================
@@ -108,6 +108,8 @@ public abstract class AbstractFramedStreamSinkChannel<C extends AbstractFramedCh
private volatile boolean writesResumed;
@SuppressWarnings("unused")
private volatile int inListenerLoop;
+ /* keep track of successful writes to properly prevent a loop UNDERTOW-1624 */
+ private volatile boolean writeSucceeded;
private static final AtomicIntegerFieldUpdater<AbstractFramedStreamSinkChannel> inListenerLoopUpdater = AtomicIntegerFieldUpdater.newUpdater(AbstractFramedStreamSinkChannel.class, "inListenerLoop");
@@ -196,6 +198,8 @@ public abstract class AbstractFramedStreamSinkChannel<C extends AbstractFramedCh
if (inListenerLoopUpdater.compareAndSet(this, 0, 1)) {
getChannel().runInIoThread(new Runnable() {
+ // loopCount keeps track of runnable being invoked in a
+ // loop without any successful write operation
int loopCount = 0;
@Override
@@ -205,7 +209,11 @@ public abstract class AbstractFramedStreamSinkChannel<C extends AbstractFramedCh
if (listener == null || !isWriteResumed()) {
return;
}
- if (loopCount++ == 100) {
+ if (writeSucceeded) {
+ // reset write succeeded and loopCount
+ writeSucceeded = false;
+ loopCount = 0;
+ } else if (loopCount++ == 100) {
//should never happen
UndertowLogger.ROOT_LOGGER.listenerNotProgressing();
IoUtils.safeClose(AbstractFramedStreamSinkChannel.this);
@@ -387,6 +395,7 @@ public abstract class AbstractFramedStreamSinkChannel<C extends AbstractFramedCh
if(!buffer.hasRemaining()) {
handleBufferFull();
}
+ writeSucceeded = writeSucceeded || copied > 0;
return copied;
}
@@ -408,6 +417,7 @@ public abstract class AbstractFramedStreamSinkChannel<C extends AbstractFramedCh
if(!buffer.hasRemaining()) {
handleBufferFull();
}
+ writeSucceeded = writeSucceeded || copied > 0;
return copied;
}
@@ -433,6 +443,7 @@ public abstract class AbstractFramedStreamSinkChannel<C extends AbstractFramedCh
protected boolean sendInternal(PooledByteBuffer pooled) throws IOException {
if (safeToSend()) {
this.body = pooled;
+ writeSucceeded = true;
return true;
}
return false;
@@ -516,7 +527,7 @@ public abstract class AbstractFramedStreamSinkChannel<C extends AbstractFramedCh
}
@Override
- public void close() throws IOException {
+ public synchronized void close() throws IOException {
if(fullyFlushed || anyAreSet(state, STATE_CLOSED)) {
return;
}
=====================================
core/src/main/java/io/undertow/server/protocol/framed/AbstractFramedStreamSourceChannel.java
=====================================
@@ -49,6 +49,7 @@ import io.undertow.UndertowMessages;
* Source channel, used to receive framed messages.
*
* @author Stuart Douglas
+ * @author Flavia Rainone
*/
public abstract class AbstractFramedStreamSourceChannel<C extends AbstractFramedChannel<C, R, S>, R extends AbstractFramedStreamSourceChannel<C, R, S>, S extends AbstractFramedStreamSinkChannel<C, R, S>> implements StreamSourceChannel {
@@ -62,13 +63,13 @@ public abstract class AbstractFramedStreamSourceChannel<C extends AbstractFramed
private static final int STATE_DONE = 1 << 1;
private static final int STATE_READS_RESUMED = 1 << 2;
- private static final int STATE_CLOSED = 1 << 3;
- private static final int STATE_LAST_FRAME = 1 << 4;
- private static final int STATE_IN_LISTENER_LOOP = 1 << 5;
- private static final int STATE_STREAM_BROKEN = 1 << 6;
- private static final int STATE_RETURNED_MINUS_ONE = 1 << 7;
- private static final int STATE_WAITNG_MINUS_ONE = 1 << 8;
-
+ private static final int STATE_READS_AWAKEN = 1 << 3;
+ private static final int STATE_CLOSED = 1 << 4;
+ private static final int STATE_LAST_FRAME = 1 << 5;
+ private static final int STATE_IN_LISTENER_LOOP = 1 << 6;
+ private static final int STATE_STREAM_BROKEN = 1 << 7;
+ private static final int STATE_RETURNED_MINUS_ONE = 1 << 8;
+ private static final int STATE_WAITNG_MINUS_ONE = 1 << 9;
/**
* The backing data for the current frame.
@@ -215,7 +216,7 @@ public abstract class AbstractFramedStreamSourceChannel<C extends AbstractFramed
@Override
public void suspendReads() {
synchronized (lock) {
- state &= ~STATE_READS_RESUMED;
+ state &= ~(STATE_READS_RESUMED | STATE_READS_AWAKEN);
}
}
@@ -263,35 +264,51 @@ public abstract class AbstractFramedStreamSourceChannel<C extends AbstractFramed
*/
void resumeReadsInternal(boolean wakeup) {
synchronized (lock) {
- boolean alreadyResumed = anyAreSet(state, STATE_READS_RESUMED);
state |= STATE_READS_RESUMED;
- if (!alreadyResumed || wakeup) {
- if (!anyAreSet(state, STATE_IN_LISTENER_LOOP)) {
- state |= STATE_IN_LISTENER_LOOP;
- getFramedChannel().runInIoThread(new Runnable() {
-
- @Override
- public void run() {
- try {
- boolean moreData;
- do {
- ChannelListener<? super R> listener = getReadListener();
- if (listener == null || !isReadResumed()) {
- return;
- }
- ChannelListeners.invokeChannelListener((R) AbstractFramedStreamSourceChannel.this, listener);
- //if writes are shutdown or we become active then we stop looping
- //we stop when writes are shutdown because we can't flush until we are active
- //although we may be flushed as part of a batch
- moreData = (frameDataRemaining > 0 && data != null) || !pendingFrameData.isEmpty() || anyAreSet(state, STATE_WAITNG_MINUS_ONE);
+ // mark state awaken if wakeup is true
+ if (wakeup)
+ state |= STATE_READS_AWAKEN;
+ // if not waked && not resumed, return
+ else if (!anyAreSet(state, STATE_READS_RESUMED))
+ return;
+ if (!anyAreSet(state, STATE_IN_LISTENER_LOOP)) {
+ state |= STATE_IN_LISTENER_LOOP;
+ getFramedChannel().runInIoThread(new Runnable() {
+
+ @Override
+ public void run() {
+ try {
+ boolean readAgain;
+ do {
+ synchronized(lock) {
+ state &= ~STATE_READS_AWAKEN;
+ }
+ ChannelListener<? super R> listener = getReadListener();
+ if (listener == null || !isReadResumed()) {
+ return;
+ }
+ ChannelListeners.invokeChannelListener((R) AbstractFramedStreamSourceChannel.this, listener);
+ //if writes are shutdown or we become active then we stop looping
+ //we stop when writes are shutdown because we can't flush until we are active
+ //although we may be flushed as part of a batch
+ final boolean moreData = (frameDataRemaining > 0 && data != null) || !pendingFrameData.isEmpty() || anyAreSet(state, STATE_WAITNG_MINUS_ONE);
+
+ synchronized (lock) {
+ // keep running if either reads are resumed and there is more data to read, or if reads are awaken
+ readAgain =((isReadResumed() && moreData) || allAreSet(state, STATE_READS_AWAKEN))
+ // as long as channel is not closed and there is no stream broken
+ && allAreClear(state,STATE_CLOSED | STATE_STREAM_BROKEN);
+ if (!readAgain)
+ state &= ~STATE_IN_LISTENER_LOOP;
}
- while (allAreSet(state, STATE_READS_RESUMED) && allAreClear(state, STATE_CLOSED | STATE_STREAM_BROKEN) && moreData);
- } finally {
+ } while (readAgain);
+ } catch (RuntimeException | Error e) {
+ synchronized (lock) {
state &= ~STATE_IN_LISTENER_LOOP;
}
}
- });
- }
+ }
+ });
}
}
}
@@ -311,7 +328,9 @@ public abstract class AbstractFramedStreamSourceChannel<C extends AbstractFramed
}
waitingForFrame = false;
if(data == null && pendingFrameData.isEmpty() && frameDataRemaining == 0) {
- state |= STATE_DONE | STATE_CLOSED;
+ synchronized (lock) {
+ state |= STATE_DONE | STATE_CLOSED;
+ }
getFramedChannel().notifyFrameReadComplete(this);
IoUtils.safeClose(this);
}
@@ -603,7 +622,7 @@ public abstract class AbstractFramedStreamSourceChannel<C extends AbstractFramed
}
@Override
- public void close() {
+ public synchronized void close() {
if(anyAreSet(state, STATE_CLOSED)) {
return;
}
=====================================
core/src/main/java/io/undertow/websockets/core/WebSocketChannel.java
=====================================
@@ -147,7 +147,7 @@ public abstract class WebSocketChannel extends AbstractFramedChannel<WebSocketCh
}
@Override
- protected void lastDataRead() {
+ protected synchronized void lastDataRead() {
if(!closeFrameReceived && !closeFrameSent) {
//the peer has likely already gone away, but try and send a close frame anyway
//this will likely just result in the write() failing an immediate connection termination
=====================================
core/src/main/resources/META-INF/services/io.undertow.server.handlers.builder.HandlerBuilder
=====================================
@@ -25,6 +25,7 @@ io.undertow.server.handlers.PathSeparatorHandler$Builder
io.undertow.server.handlers.IPAddressAccessControlHandler$Builder
io.undertow.server.handlers.ByteRangeHandler$Builder
io.undertow.server.handlers.encoding.EncodingHandler$Builder
+io.undertow.server.handlers.encoding.RequestEncodingHandler$Builder
io.undertow.server.handlers.LearningPushHandler$Builder
io.undertow.server.handlers.SetHeaderHandler$Builder
io.undertow.predicate.PredicatesHandler$DoneHandlerBuilder
@@ -38,4 +39,4 @@ io.undertow.server.handlers.StoredResponseHandler$Builder
io.undertow.server.handlers.SecureCookieHandler$Builder
io.undertow.server.handlers.ForwardedHandler$Builder
io.undertow.server.handlers.HttpContinueAcceptingHandler$Builder
-io.undertow.server.handlers.form.EagerFormParsingHandler$Builder
\ No newline at end of file
+io.undertow.server.handlers.form.EagerFormParsingHandler$Builder
=====================================
core/src/test/java/io/undertow/server/handlers/RangeRequestTestCase.java
=====================================
@@ -104,7 +104,7 @@ public class RangeRequestTestCase {
HttpResponse result = client.execute(get);
Assert.assertEquals(StatusCodes.PARTIAL_CONTENT, result.getStatusLine().getStatusCode());
String response = EntityUtils.toString(result.getEntity());
- Assert.assertEquals("89\n2:012345", response);
+ Assert.assertEquals("89#2:012345", response);
Assert.assertEquals( "bytes 10-20/1034", result.getFirstHeader(Headers.CONTENT_RANGE_STRING).getValue());
get = new HttpGet(DefaultServer.getDefaultServerURL() + path);
@@ -112,7 +112,7 @@ public class RangeRequestTestCase {
result = client.execute(get);
Assert.assertEquals(StatusCodes.PARTIAL_CONTENT, result.getStatusLine().getStatusCode());
response = EntityUtils.toString(result.getEntity());
- Assert.assertEquals("3:0123456789\n74:012345678", response);
+ Assert.assertEquals("3:0123456789#74:012345678", response);
Assert.assertEquals( "bytes 1000-1024/1034", result.getFirstHeader(Headers.CONTENT_RANGE_STRING).getValue());
get = new HttpGet(DefaultServer.getDefaultServerURL() + path);
@@ -120,7 +120,7 @@ public class RangeRequestTestCase {
result = client.execute(get);
Assert.assertEquals(StatusCodes.PARTIAL_CONTENT, result.getStatusLine().getStatusCode());
response = EntityUtils.toString(result.getEntity());
- Assert.assertEquals(":0123456789\n74:012345678", response);
+ Assert.assertEquals(":0123456789#74:012345678", response);
Assert.assertEquals( "bytes 1001-1024/1034", result.getFirstHeader(Headers.CONTENT_RANGE_STRING).getValue());
get = new HttpGet(DefaultServer.getDefaultServerURL() + path);
=====================================
core/src/test/java/io/undertow/server/handlers/largerange.txt
=====================================
@@ -1,74 +1 @@
-1:0123456789
-2:0123456789
-3:0123456789
-4:0123456789
-5:0123456789
-6:0123456789
-7:0123456789
-8:0123456789
-9:0123456789
-10:0123456789
-11:0123456789
-12:0123456789
-13:0123456789
-14:0123456789
-15:0123456789
-16:0123456789
-17:0123456789
-18:0123456789
-19:0123456789
-20:0123456789
-21:0123456789
-22:0123456789
-23:0123456789
-24:0123456789
-25:0123456789
-26:0123456789
-27:0123456789
-28:0123456789
-29:0123456789
-30:0123456789
-31:0123456789
-32:0123456789
-33:0123456789
-34:0123456789
-35:0123456789
-36:0123456789
-37:0123456789
-38:0123456789
-39:0123456789
-40:0123456789
-41:0123456789
-42:0123456789
-43:0123456789
-44:0123456789
-45:0123456789
-46:0123456789
-47:0123456789
-48:0123456789
-49:0123456789
-50:0123456789
-51:0123456789
-52:0123456789
-53:0123456789
-54:0123456789
-55:0123456789
-56:0123456789
-57:0123456789
-58:0123456789
-59:0123456789
-60:0123456789
-61:0123456789
-62:0123456789
-63:0123456789
-64:0123456789
-65:0123456789
-66:0123456789
-67:0123456789
-68:0123456789
-69:0123456789
-70:0123456789
-71:0123456789
-72:0123456789
-73:0123456789
-74:0123456789abcdefg
+1:0123456789#2:0123456789#3:0123456789#4:0123456789#5:0123456789#6:0123456789#7:0123456789#8:0123456789#9:0123456789#10:0123456789#11:0123456789#12:0123456789#13:0123456789#14:0123456789#15:0123456789#16:0123456789#17:0123456789#18:0123456789#19:0123456789#20:0123456789#21:0123456789#22:0123456789#23:0123456789#24:0123456789#25:0123456789#26:0123456789#27:0123456789#28:0123456789#29:0123456789#30:0123456789#31:0123456789#32:0123456789#33:0123456789#34:0123456789#35:0123456789#36:0123456789#37:0123456789#38:0123456789#39:0123456789#40:0123456789#41:0123456789#42:0123456789#43:0123456789#44:0123456789#45:0123456789#46:0123456789#47:0123456789#48:0123456789#49:0123456789#50:0123456789#51:0123456789#52:0123456789#53:0123456789#54:0123456789#55:0123456789#56:0123456789#57:0123456789#58:0123456789#59:0123456789#60:0123456789#61:0123456789#62:0123456789#63:0123456789#64:0123456789#65:0123456789#66:0123456789#67:0123456789#68:0123456789#69:0123456789#70:0123456789#71:0123456789#72:0123456789#73:0123456789#74:0123456789abcdefg#
\ No newline at end of file
=====================================
coverage-report/pom.xml
=====================================
@@ -3,7 +3,7 @@
<parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-parent</artifactId>
- <version>2.0.28.Final</version>
+ <version>2.0.29.Final</version>
</parent>
<artifactId>undertow-coverage-report</artifactId>
<name>Undertow Test Coverage Report</name>
=====================================
dist/pom.xml
=====================================
@@ -25,12 +25,12 @@
<parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-parent</artifactId>
- <version>2.0.28.Final</version>
+ <version>2.0.29.Final</version>
</parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-dist</artifactId>
- <version>2.0.28.Final</version>
+ <version>2.0.29.Final</version>
<name>Undertow: Distribution</name>
=====================================
examples/pom.xml
=====================================
@@ -25,12 +25,12 @@
<parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-parent</artifactId>
- <version>2.0.28.Final</version>
+ <version>2.0.29.Final</version>
</parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-examples</artifactId>
- <version>2.0.28.Final</version>
+ <version>2.0.29.Final</version>
<name>Undertow Examples</name>
=====================================
karaf/pom.xml
=====================================
@@ -25,12 +25,12 @@
<parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-parent</artifactId>
- <version>2.0.28.Final</version>
+ <version>2.0.29.Final</version>
</parent>
<groupId>io.undertow</groupId>
<artifactId>karaf</artifactId>
- <version>2.0.28.Final</version>
+ <version>2.0.29.Final</version>
<packaging>pom</packaging>
<name>Undertow Karaf Features</name>
=====================================
parser-generator/pom.xml
=====================================
@@ -25,12 +25,12 @@
<parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-parent</artifactId>
- <version>2.0.28.Final</version>
+ <version>2.0.29.Final</version>
</parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-parser-generator</artifactId>
- <version>2.0.28.Final</version>
+ <version>2.0.29.Final</version>
<name>Undertow Parser Generator</name>
<description>An annotation processor that is used to generate the HTTP parser</description>
=====================================
pom.xml
=====================================
@@ -28,7 +28,7 @@
<groupId>io.undertow</groupId>
<artifactId>undertow-parent</artifactId>
- <version>2.0.28.Final</version>
+ <version>2.0.29.Final</version>
<name>Undertow</name>
<description>Undertow</description>
=====================================
servlet/pom.xml
=====================================
@@ -25,12 +25,12 @@
<parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-parent</artifactId>
- <version>2.0.28.Final</version>
+ <version>2.0.29.Final</version>
</parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
- <version>2.0.28.Final</version>
+ <version>2.0.29.Final</version>
<name>Undertow Servlet</name>
=====================================
servlet/src/main/java/io/undertow/servlet/compat/rewrite/RewriteHandler.java
=====================================
@@ -29,6 +29,8 @@ import io.undertow.util.Headers;
import io.undertow.util.QueryParameterUtils;
import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Map;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
@@ -157,8 +159,16 @@ public class RewriteHandler implements HttpHandler {
}
// - env (note: this sets a request attribute)
if (rules[i].isEnv() && newtest != null) {
+ Map<String, String> attrs = exchange.getAttachment(HttpServerExchange.REQUEST_ATTRIBUTES);
+ if (attrs == null) {
+ attrs = new HashMap<>();
+ exchange.putAttachment(HttpServerExchange.REQUEST_ATTRIBUTES, attrs);
+ }
for (int j = 0; j < rules[i].getEnvSize(); j++) {
- request.setAttribute(rules[i].getEnvName(j), rules[i].getEnvResult(j));
+ final String envName = rules[i].getEnvName(j);
+ final String envResult = rules[i].getEnvResult(j);
+ attrs.put(envName, envResult);
+ request.setAttribute(envName, envResult);
}
}
// - content type (note: this will not force the content type, use a filter
=====================================
websockets-jsr/pom.xml
=====================================
@@ -25,12 +25,12 @@
<parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-parent</artifactId>
- <version>2.0.28.Final</version>
+ <version>2.0.29.Final</version>
</parent>
<groupId>io.undertow</groupId>
<artifactId>undertow-websockets-jsr</artifactId>
- <version>2.0.28.Final</version>
+ <version>2.0.29.Final</version>
<name>Undertow WebSockets JSR356 implementations</name>
=====================================
websockets-jsr/src/main/java/io/undertow/websockets/jsr/ServerWebSocketContainer.java
=====================================
@@ -352,18 +352,24 @@ public class ServerWebSocketContainer implements ServerContainer, Closeable {
extensions.add(ExtensionImpl.create(e));
}
ConfiguredClientEndpoint configured = clientEndpoints.get(endpointInstance.getClass());
+ Endpoint instance = endpointInstance;
if(configured == null) {
synchronized (clientEndpoints) {
- configured = clientEndpoints.get(endpointInstance.getClass());
+ // make sure to create an instance of AnnotatedEndpoint if we have the annotation
+ configured = getClientEndpoint(endpointInstance.getClass(), false);
if(configured == null) {
+ // if we don't, add an endpoint anyway to the list of clientEndpoints
clientEndpoints.put(endpointInstance.getClass(), configured = new ConfiguredClientEndpoint());
+ } else {
+ // use the factory in configured to reach the endpoint
+ instance = configured.getFactory().createInstance(new ImmediateInstanceHandle<>(endpointInstance));
}
}
}
EncodingFactory encodingFactory = EncodingFactory.createFactory(classIntrospecter, cec.getDecoders(), cec.getEncoders());
UndertowSession undertowSession = new UndertowSession(channel, connectionBuilder.getUri(), Collections.<String, String>emptyMap(), Collections.<String, List<String>>emptyMap(), sessionHandler, null, new ImmediateInstanceHandle<>(endpointInstance), cec, connectionBuilder.getUri().getQuery(), encodingFactory.createEncoding(cec), configured, clientNegotiation.getSelectedSubProtocol(), extensions, connectionBuilder);
- endpointInstance.onOpen(undertowSession, cec);
+ instance.onOpen(undertowSession, cec);
channel.resumeReceives();
return undertowSession;
View it on GitLab: https://salsa.debian.org/java-team/undertow/commit/dad62d54d26f5f310f286dc3581a0fb3c416eff1
--
View it on GitLab: https://salsa.debian.org/java-team/undertow/commit/dad62d54d26f5f310f286dc3581a0fb3c416eff1
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20200108/7c26564e/attachment.html>
More information about the pkg-java-commits
mailing list