[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