[Git][java-team/undertow][upstream] New upstream version 2.0.27

Markus Koschany gitlab at salsa.debian.org
Sun Oct 20 16:24:16 BST 2019



Markus Koschany pushed to branch upstream at Debian Java Maintainers / undertow


Commits:
494faa11 by Markus Koschany at 2019-10-20T15:15:43Z
New upstream version 2.0.27
- - - - -


26 changed files:

- benchmarks/pom.xml
- core/pom.xml
- core/src/main/java/io/undertow/Undertow.java
- core/src/main/java/io/undertow/UndertowOptions.java
- core/src/main/java/io/undertow/conduits/StoredResponseStreamSinkConduit.java
- core/src/main/java/io/undertow/protocols/http2/HpackDecoder.java
- core/src/main/java/io/undertow/protocols/http2/Http2Channel.java
- core/src/main/java/io/undertow/security/impl/BasicAuthenticationMechanism.java
- core/src/main/java/io/undertow/server/handlers/resource/CachedResource.java
- core/src/main/java/io/undertow/server/handlers/resource/PathResource.java
- core/src/main/java/io/undertow/server/protocol/framed/AbstractFramedChannel.java
- core/src/test/java/io/undertow/server/handlers/RangeRequestTestCase.java
- + core/src/test/java/io/undertow/server/handlers/largerange.txt
- + core/src/test/java/io/undertow/util/ByteRangeTestCase.java
- 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/attribute/ServletRequestLineAttribute.java
- servlet/src/main/java/io/undertow/servlet/compat/rewrite/RewriteHandler.java
- servlet/src/test/java/io/undertow/servlet/test/compat/rewrite/RewriteTestCase.java
- servlet/src/test/java/io/undertow/servlet/test/dispatcher/DispatcherForwardTestCase.java
- websockets-jsr/pom.xml


Changes:

=====================================
benchmarks/pom.xml
=====================================
@@ -25,12 +25,12 @@
     <parent>
         <groupId>io.undertow</groupId>
         <artifactId>undertow-parent</artifactId>
-        <version>2.0.26.Final</version>
+        <version>2.0.27.Final</version>
     </parent>
 
     <groupId>io.undertow</groupId>
     <artifactId>undertow-benchmarks</artifactId>
-    <version>2.0.26.Final</version>
+    <version>2.0.27.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.26.Final</version>
+        <version>2.0.27.Final</version>
     </parent>
 
     <groupId>io.undertow</groupId>
     <artifactId>undertow-core</artifactId>
-    <version>2.0.26.Final</version>
+    <version>2.0.27.Final</version>
 
     <name>Undertow Core</name>
 


=====================================
core/src/main/java/io/undertow/Undertow.java
=====================================
@@ -114,7 +114,7 @@ public final class Undertow {
     }
 
     public synchronized void start() {
-        UndertowLogger.ROOT_LOGGER.debugf("starting undertow server %s", this);
+        UndertowLogger.ROOT_LOGGER.infof("starting server: %s", Version.getFullVersionString());
         xnio = Xnio.getInstance(Undertow.class.getClassLoader());
         channels = new ArrayList<>();
         try {
@@ -249,7 +249,7 @@ public final class Undertow {
     }
 
     public synchronized void stop() {
-        UndertowLogger.ROOT_LOGGER.debugf("stopping undertow server %s", this);
+        UndertowLogger.ROOT_LOGGER.infof("stopping server: %s", Version.getFullVersionString());
         if (channels != null) {
             for (AcceptingChannel<? extends StreamConnection> channel : channels) {
                 IoUtils.safeClose(channel);


=====================================
core/src/main/java/io/undertow/UndertowOptions.java
=====================================
@@ -339,6 +339,23 @@ public class UndertowOptions {
      */
     public static final Option<String> ENDPOINT_IDENTIFICATION_ALGORITHM = Option.simple(UndertowOptions.class, "ENDPOINT_IDENTIFICATION_ALGORITHM", String.class);
 
+    /**
+     * The maximum numbers of frames that can be queued before reads are suspended. Once this number is hit then reads will not be resumed until {@link #QUEUED_FRAMES_LOW_WATER_MARK}
+     * is hit.
+     *
+     * Defaults to 50
+     */
+    public static final Option<Integer> QUEUED_FRAMES_HIGH_WATER_MARK = Option.simple(UndertowOptions.class, "QUEUED_FRAMES_HIGH_WATER_MARK", Integer.class);
+
+    /**
+     * The point at which reads will resume again after hitting the high water mark
+     *
+     * Defaults to 10
+     */
+    public static final Option<Integer> QUEUED_FRAMES_LOW_WATER_MARK = Option.simple(UndertowOptions.class, "QUEUED_FRAMES_LOW_WATER_MARK", Integer.class);
+
+
+
     private UndertowOptions() {
 
     }


=====================================
core/src/main/java/io/undertow/conduits/StoredResponseStreamSinkConduit.java
=====================================
@@ -93,7 +93,7 @@ public final class StoredResponseStreamSinkConduit extends AbstractStreamSinkCon
         for (int i = 0; i < len; ++i) {
             ByteBuffer buf = srcs[i + offs];
             int pos = starts[i];
-            while (rem > 0 && pos <= buf.position()) {
+            while (rem > 0 && pos < buf.position()) {
                 outputStream.write(buf.get(pos));
                 pos++;
                 rem--;
@@ -130,7 +130,7 @@ public final class StoredResponseStreamSinkConduit extends AbstractStreamSinkCon
         for (int i = 0; i < len; ++i) {
             ByteBuffer buf = srcs[i + offs];
             int pos = starts[i];
-            while (rem > 0 && pos <= buf.position()) {
+            while (rem > 0 && pos < buf.position()) {
                 outputStream.write(buf.get(pos));
                 pos++;
                 rem--;


=====================================
core/src/main/java/io/undertow/protocols/http2/HpackDecoder.java
=====================================
@@ -18,13 +18,13 @@
 
 package io.undertow.protocols.http2;
 
+import static io.undertow.protocols.http2.Hpack.HeaderField;
+
 import java.nio.ByteBuffer;
 
 import io.undertow.UndertowMessages;
 import io.undertow.util.HttpString;
 
-import static io.undertow.protocols.http2.Hpack.HeaderField;
-
 /**
  * A decoder for HPACK.
  *
@@ -229,6 +229,9 @@ public class HpackDecoder {
             String string = readHpackString(buffer);
             if (string == null) {
                 return null;
+            } else if (string.isEmpty()) {
+                //don't allow empty header names
+                throw new HpackException();
             }
             return new HttpString(string);
         }
@@ -253,12 +256,19 @@ public class HpackDecoder {
         }
         String ret = stringBuilder.toString();
         stringBuilder.setLength(0);
+        if (ret.isEmpty()) {
+            //return the interned empty string, rather than allocating a new one each time
+            return "";
+        }
         return ret;
     }
 
     private String readHuffmanString(int length, ByteBuffer buffer) throws HpackException {
         HPackHuffman.decode(buffer, length, stringBuilder);
         String ret = stringBuilder.toString();
+        if (ret.isEmpty()) {
+            return "";
+        }
         stringBuilder.setLength(0);
         return ret;
     }


=====================================
core/src/main/java/io/undertow/protocols/http2/Http2Channel.java
=====================================
@@ -486,7 +486,7 @@ public class Http2Channel extends AbstractFramedChannel<Http2Channel, AbstractHt
                 boolean ack = Bits.anyAreSet(frameParser.flags, PING_FLAG_ACK);
                 channel = new Http2PingStreamSourceChannel(this, pingParser.getData(), ack);
                 if(!ack) { //not an ack from one of our pings, so send it back
-                    sendPing(pingParser.getData(), null, true);
+                    sendPing(pingParser.getData(),  new Http2ControlMessageExceptionHandler(), true);
                 }
                 break;
             }


=====================================
core/src/main/java/io/undertow/security/impl/BasicAuthenticationMechanism.java
=====================================
@@ -151,9 +151,9 @@ public class BasicAuthenticationMechanism implements AuthenticationMechanism {
                         }
 
                         plainChallenge = new String(decode.array(), decode.arrayOffset(), decode.limit(), charset);
-                        UndertowLogger.SECURITY_LOGGER.debugf("Found basic auth header %s (decoded using charset %s) in %s", plainChallenge, charset, exchange);
+                        UndertowLogger.SECURITY_LOGGER.debugf("Found basic auth header (decoded using charset %s) in %s", charset, exchange);
                     } catch (IOException e) {
-                        UndertowLogger.SECURITY_LOGGER.debugf(e, "Failed to decode basic auth header %s in %s", base64Challenge, exchange);
+                        UndertowLogger.SECURITY_LOGGER.debugf(e, "Failed to decode basic auth header in %s", exchange);
                     }
                     int colonPos;
                     if (plainChallenge != null && (colonPos = plainChallenge.indexOf(COLON)) > -1) {


=====================================
core/src/main/java/io/undertow/server/handlers/resource/CachedResource.java
=====================================
@@ -271,29 +271,29 @@ public class CachedResource implements Resource, RangeAwareResource {
                     existing.dereference();
                 }
             }
-            if(start > 0) {
-                long startDec = start;
-                long endCount = 0;
-                //handle the start of the range
-                for(ByteBuffer b : buffers) {
-                    if(endCount == end) {
-                        b.limit(b.position());
-                        continue;
-                    } else if(endCount + b.remaining() < end) {
-                        endCount += b.remaining();
-                    } else {
-                        b.limit((int) (b.position() + (end - endCount)));
-                        endCount = end;
-                    }
-                    if(b.remaining() >= startDec) {
-                        startDec = 0;
-                        b.position((int) (b.position() + startDec));
-                    } else {
-                        startDec -= b.remaining();
-                        b.position(b.limit());
-                    }
+            long endTarget = end + 1; //as it is inclusive
+            long startDec = start;
+            long endCount = 0;
+            //handle the start of the range
+            for (ByteBuffer b : buffers) {
+                if (endCount == endTarget) {
+                    b.limit(b.position());
+                    continue;
+                } else if (endCount + b.remaining() < endTarget) {
+                    endCount += b.remaining();
+                } else {
+                    b.limit((int) (b.position() + (endTarget - endCount)));
+                    endCount = endTarget;
+                }
+                if (b.remaining() >= startDec) {
+                    b.position((int) (b.position() + startDec));
+                    startDec = 0;
+                } else {
+                    startDec -= b.remaining();
+                    b.position(b.limit());
                 }
             }
+
             sender.send(buffers, new DereferenceCallback(existing, completionCallback));
         }
     }


=====================================
core/src/main/java/io/undertow/server/handlers/resource/PathResource.java
=====================================
@@ -153,8 +153,10 @@ public class PathResource implements RangeAwareResource {
             public void run() {
                 if(range && remaining == 0) {
                     //we are done
-                    pooled.close();
-                    pooled = null;
+                    if (pooled != null) {
+                        pooled.close();
+                        pooled = null;
+                    }
                     IoUtils.safeClose(fileChannel);
                     callback.onComplete(exchange, sender);
                     return;


=====================================
core/src/main/java/io/undertow/server/protocol/framed/AbstractFramedChannel.java
=====================================
@@ -55,6 +55,7 @@ import org.xnio.channels.ConnectedChannel;
 import org.xnio.channels.StreamSinkChannel;
 import org.xnio.channels.StreamSourceChannel;
 import org.xnio.channels.SuspendableWriteChannel;
+
 import io.undertow.UndertowLogger;
 import io.undertow.UndertowMessages;
 import io.undertow.UndertowOptions;
@@ -110,7 +111,10 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
     private volatile long frameDataRemaining;
     private volatile R receiver;
 
-    private volatile boolean receivesSuspended = true;
+    private volatile boolean receivesSuspendedByUser = true;
+    private volatile boolean receivesSuspendedTooManyQueuedMessages = false;
+    private volatile boolean receivesSuspendedTooManyBuffers = false;
+
 
     @SuppressWarnings("unused")
     private volatile int readsBroken = 0;
@@ -125,6 +129,8 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
     private final List<ChannelListener<C>> closeTasks = new CopyOnWriteArrayList<>();
     private volatile boolean flushingSenders = false;
 
+    private boolean partialRead = false;
+
     @SuppressWarnings("unused")
     private volatile int outstandingBuffers;
     private static final AtomicIntegerFieldUpdater<AbstractFramedChannel> outstandingBuffersUpdater = AtomicIntegerFieldUpdater.newUpdater(AbstractFramedChannel.class, "outstandingBuffers");
@@ -146,22 +152,25 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
     private volatile boolean requireExplicitFlush = false;
     private volatile boolean readChannelDone = false;
 
+    private final int queuedFrameHighWaterMark;
+    private final int queuedFrameLowWaterMark;
+
     private final ReferenceCountedPooled.FreeNotifier freeNotifier = new ReferenceCountedPooled.FreeNotifier() {
         @Override
         public void freed() {
             int res = outstandingBuffersUpdater.decrementAndGet(AbstractFramedChannel.this);
-            if(!receivesSuspended && res == maxQueuedBuffers - 1) {
+            if (!receivesSuspendedByUser && res == maxQueuedBuffers - 1) {
                 //we need to do the resume in the IO thread, as there is a risk of deadlock otherwise, as the calling thread is an application thread
                 //and may hold a lock on a stream source channel, see UNDERTOW-1312
                 getIoThread().execute(new Runnable() {
                     @Override
                     public void run() {
                         synchronized (AbstractFramedChannel.this) {
-                            if(outstandingBuffersUpdater.get(AbstractFramedChannel.this) < maxQueuedBuffers) {
-                                if(UndertowLogger.REQUEST_IO_LOGGER.isTraceEnabled()) {
+                            if (outstandingBuffersUpdater.get(AbstractFramedChannel.this) < maxQueuedBuffers) {
+                                if (UndertowLogger.REQUEST_IO_LOGGER.isTraceEnabled()) {
                                     UndertowLogger.REQUEST_IO_LOGGER.tracef("Resuming reads on %s as buffers have been consumed", AbstractFramedChannel.this);
                                 }
-                                channel.getSourceChannel().resumeReads();
+                                new UpdateResumeState(null, false, null).run();
                             }
                         }
                     }
@@ -229,6 +238,8 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
         FrameCloseListener closeListener = new FrameCloseListener();
         connectedStreamChannel.getSinkChannel().getCloseSetter().set(closeListener);
         connectedStreamChannel.getSourceChannel().getCloseSetter().set(closeListener);
+        this.queuedFrameHighWaterMark = settings.get(UndertowOptions.QUEUED_FRAMES_HIGH_WATER_MARK, 50);
+        this.queuedFrameLowWaterMark = settings.get(UndertowOptions.QUEUED_FRAMES_LOW_WATER_MARK, 10);
     }
 
     protected IdleTimeoutConduit createIdleTimeoutChannel(StreamConnection connectedStreamChannel) {
@@ -343,6 +354,7 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
             channel.getSourceChannel().shutdownReads();
             return null;
         }
+        partialRead = false;
         boolean requiresReinvoke = false;
         int reinvokeDataRemaining = 0;
         ReferenceCountedPooled pooled = this.readData;
@@ -461,6 +473,9 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
                     }
                     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) {
@@ -521,10 +536,10 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
                         //we need to re-read in a sync block, to prevent races
                         expect = outstandingBuffersUpdater.get(this);
                         if (expect == maxQueuedBuffers) {
-                            if(UndertowLogger.REQUEST_IO_LOGGER.isTraceEnabled()) {
+                            if (UndertowLogger.REQUEST_IO_LOGGER.isTraceEnabled()) {
                                 UndertowLogger.REQUEST_IO_LOGGER.tracef("Suspending reads on %s due to too many outstanding buffers", this);
                             }
-                            channel.getSourceChannel().suspendReads();
+                            getIoThread().execute(new UpdateResumeState(null, true, null));
                             return null;
                         }
                     }
@@ -668,6 +683,10 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
                         channel.getSinkChannel().setWriteListener(ChannelListeners.flushingChannelListener(null, null));
                         channel.getSinkChannel().resumeWrites();
                     }
+                } else if (pendingFrames.size() > queuedFrameHighWaterMark) {
+                    new UpdateResumeState(null, null, true).run();
+                } else if (receivesSuspendedTooManyQueuedMessages && pendingFrames.size() < queuedFrameLowWaterMark) {
+                    new UpdateResumeState(null, null, false).run();
                 }
 
             } catch (IOException|RuntimeException|Error e) {
@@ -765,35 +784,16 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
      * Suspend the receive of new frames via {@link #receive()}
      */
     public synchronized void suspendReceives() {
-        receivesSuspended = true;
-        if (receiver == null) {
-            if(Thread.currentThread() == channel.getIoThread()) {
-                channel.getSourceChannel().suspendReads();
-            } else {
-                channel.getIoThread().execute(new Runnable() {
-                    @Override
-                    public void run() {
-                        channel.getSourceChannel().suspendReads();
-                    }
-                });
-            }
-        }
+        receivesSuspendedByUser = true;
+        getIoThread().execute(new UpdateResumeState(true, null, null));
     }
 
     /**
      * Resume the receive of new frames via {@link #receive()}
      */
     public synchronized void resumeReceives() {
-        receivesSuspended = false;if(Thread.currentThread() == channel.getIoThread()) {
-            doResume();
-        } else {
-            channel.getIoThread().execute(new Runnable() {
-                @Override
-                public void run() {
-                    doResume();
-                }
-            });
-        }
+        receivesSuspendedByUser = false;
+        getIoThread().execute(new UpdateResumeState(false, null, null));
     }
 
     private void doResume() {
@@ -805,7 +805,7 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
     }
 
     public boolean isReceivesResumed() {
-        return !receivesSuspended;
+        return !receivesSuspendedByUser;
     }
 
     /**
@@ -813,11 +813,11 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
      */
     @Override
     public void close() throws IOException {
-        if(UndertowLogger.REQUEST_IO_LOGGER.isTraceEnabled()) {
+        if (UndertowLogger.REQUEST_IO_LOGGER.isTraceEnabled()) {
             UndertowLogger.REQUEST_IO_LOGGER.tracef(new ClosedChannelException(), "Channel %s is being closed", this);
         }
         safeClose(channel);
-        if(readData != null) {
+        if (readData != null) {
             readData.close();
             readData = null;
         }
@@ -862,7 +862,6 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
     protected abstract void closeSubChannels();
 
 
-
     /**
      * Called when a sub channel fails to fulfil its contract, and leaves the channel in an inconsistent state.
      * <p>
@@ -939,18 +938,22 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
             }
 
             final R receiver = AbstractFramedChannel.this.receiver;
-            if ((readChannelDone || receivesSuspended) && receiver == null) {
+            if ((readChannelDone || isReadsSuspended()) && receiver == null) {
                 channel.suspendReads();
                 return;
             } else {
                 ChannelListener listener = receiveSetter.get();
-                if(listener == null) {
+                if (listener == null) {
                     listener = DRAIN_LISTENER;
                 }
                 UndertowLogger.REQUEST_IO_LOGGER.tracef("Invoking receive listener", receiver);
                 ChannelListeners.invokeChannelListener(AbstractFramedChannel.this, listener);
             }
-            if (readData != null  && !readData.isFreed() && channel.isOpen()) {
+            final boolean partialRead;
+            synchronized (AbstractFramedChannel.this) {
+                partialRead = AbstractFramedChannel.this.partialRead;
+            }
+            if (readData != null && !readData.isFreed() && channel.isOpen() && !partialRead) {
                 try {
                     runInIoThread(new Runnable() {
                         @Override
@@ -962,9 +965,16 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
                     IoUtils.safeClose(AbstractFramedChannel.this);
                 }
             }
+            synchronized (AbstractFramedChannel.this) {
+                AbstractFramedChannel.this.partialRead = false;
+            }
         }
     }
 
+    private boolean isReadsSuspended() {
+        return receivesSuspendedByUser || receivesSuspendedTooManyBuffers || receivesSuspendedTooManyQueuedMessages;
+    }
+
     private class FrameWriteListener implements ChannelListener<StreamSinkChannel> {
         @Override
         public void handleEvent(final StreamSinkChannel channel) {
@@ -1104,9 +1114,6 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
     }
 
 
-
-
-
     protected ChannelExceptionHandler<SuspendableWriteChannel> writeExceptionHandler() {
         return new ChannelExceptionHandler<SuspendableWriteChannel>() {
             @Override
@@ -1127,4 +1134,35 @@ public abstract class AbstractFramedChannel<C extends AbstractFramedChannel<C, R
     protected OptionMap getSettings() {
         return settings;
     }
+
+    private class UpdateResumeState implements Runnable {
+
+        private final Boolean user;
+        private final Boolean buffers;
+        private final Boolean frames;
+
+        private UpdateResumeState(Boolean user, Boolean buffers, Boolean frames) {
+            this.user = user;
+            this.buffers = buffers;
+            this.frames = frames;
+        }
+
+        @Override
+        public void run() {
+            if (user != null) {
+                receivesSuspendedByUser = user;
+            }
+            if (buffers != null) {
+                receivesSuspendedTooManyBuffers = buffers;
+            }
+            if (frames != null) {
+                receivesSuspendedTooManyQueuedMessages = frames;
+            }
+            if (receivesSuspendedByUser || receivesSuspendedTooManyQueuedMessages || receivesSuspendedTooManyBuffers) {
+                channel.getSourceChannel().suspendReads();
+            } else {
+                doResume();
+            }
+        }
+    }
 }


=====================================
core/src/test/java/io/undertow/server/handlers/RangeRequestTestCase.java
=====================================
@@ -80,7 +80,60 @@ public class RangeRequestTestCase {
     }
     @Test
     public void testCachedResourceHandler() throws IOException, InterruptedException {
-        runTest("/cachedresource/range.txt", false);
+        for(int i = 0; i < 10; ++i) {
+            runTest("/cachedresource/range.txt", false);
+        }
+    }
+
+    @Test
+    public void testLargeCachedResourceHandler() throws IOException, InterruptedException {
+        String path = "/cachedresource/largerange.txt";
+        TestHttpClient client = new TestHttpClient();
+        try {
+            for(int i = 0; i < 3; ++i) {
+                HttpGet get = new HttpGet(DefaultServer.getDefaultServerURL() + path);
+                HttpResponse result = client.execute(get);
+                Assert.assertEquals(StatusCodes.OK, result.getStatusLine().getStatusCode());
+                client.getConnectionManager().shutdown();
+                client = new TestHttpClient();
+            }
+
+            for(int i = 0; i < 10; ++i) {
+                HttpGet get = new HttpGet(DefaultServer.getDefaultServerURL() + path);
+                get.addHeader(Headers.RANGE_STRING, "bytes=10-20");
+                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( "bytes 10-20/1034", result.getFirstHeader(Headers.CONTENT_RANGE_STRING).getValue());
+
+                get = new HttpGet(DefaultServer.getDefaultServerURL() + path);
+                get.addHeader(Headers.RANGE_STRING, "bytes=1000-1024");
+                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( "bytes 1000-1024/1034", result.getFirstHeader(Headers.CONTENT_RANGE_STRING).getValue());
+
+                get = new HttpGet(DefaultServer.getDefaultServerURL() + path);
+                get.addHeader(Headers.RANGE_STRING, "bytes=1001-1024");
+                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( "bytes 1001-1024/1034", result.getFirstHeader(Headers.CONTENT_RANGE_STRING).getValue());
+
+                get = new HttpGet(DefaultServer.getDefaultServerURL() + path);
+                get.addHeader(Headers.RANGE_STRING, "bytes=1025-1030");
+                result = client.execute(get);
+                Assert.assertEquals(StatusCodes.PARTIAL_CONTENT, result.getStatusLine().getStatusCode());
+                response = EntityUtils.toString(result.getEntity());
+                Assert.assertEquals("9abcde", response);
+                Assert.assertEquals( "bytes 1025-1030/1034", result.getFirstHeader(Headers.CONTENT_RANGE_STRING).getValue());
+            }
+        } finally {
+            client.getConnectionManager().shutdown();
+        }
     }
 
     public void runTest(String path, boolean etag) throws IOException, InterruptedException {


=====================================
core/src/test/java/io/undertow/server/handlers/largerange.txt
=====================================
@@ -0,0 +1,74 @@
+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


=====================================
core/src/test/java/io/undertow/util/ByteRangeTestCase.java
=====================================
@@ -0,0 +1,198 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2014 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package io.undertow.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ByteRangeTestCase {
+
+    @Test
+    public void testGetRanges() {
+        ByteRange byteRange = new ByteRange(
+                new ArrayList<>(Arrays.asList(
+                        new ByteRange.Range(3, 5),
+                        new ByteRange.Range(4, 8),
+                        new ByteRange.Range(3, 9))));
+
+        Assert.assertEquals(3, byteRange.getRanges());
+    }
+
+    @Test
+    public void testGetStart() {
+        ByteRange byteRange = new ByteRange(
+                new ArrayList<>(Arrays.asList(
+                        new ByteRange.Range(3, 5),
+                        new ByteRange.Range(4, 8),
+                        new ByteRange.Range(3, 9))));
+
+        Assert.assertEquals(3, byteRange.getStart(0));
+        Assert.assertEquals(4, byteRange.getStart(1));
+        Assert.assertEquals(3, byteRange.getStart(2));
+    }
+
+    @Test
+    public void testGetEnd() {
+        ByteRange byteRange = new ByteRange(
+                new ArrayList<>(Arrays.asList(
+                        new ByteRange.Range(3, 5),
+                        new ByteRange.Range(4, 8),
+                        new ByteRange.Range(3, 9))));
+
+        Assert.assertEquals(5, byteRange.getEnd(0));
+        Assert.assertEquals(8, byteRange.getEnd(1));
+        Assert.assertEquals(9, byteRange.getEnd(2));
+    }
+
+    @Test
+    public void testParse() {
+        Assert.assertNull(ByteRange.parse(null));
+        Assert.assertNull(ByteRange.parse("foo"));
+        Assert.assertNull(ByteRange.parse("bytes=1"));
+        Assert.assertNull(ByteRange.parse("bytes=a-"));
+        Assert.assertNull(ByteRange.parse("foobarbaz"));
+        Assert.assertNull(ByteRange.parse("bytes=--1"));
+
+        Assert.assertEquals(1, ByteRange.parse("bytes=2-").getRanges());
+        Assert.assertEquals(1, ByteRange.parse("bytes=-20").getRanges());
+    }
+
+    @Test
+    public void testGetResponseResult1() {
+        ByteRange byteRange = new ByteRange(
+                new ArrayList<>(Arrays.asList(
+                        new ByteRange.Range(3, 5),
+                        new ByteRange.Range(4, 8),
+                        new ByteRange.Range(3, 9))));
+
+        Assert.assertNull(byteRange.getResponseResult(0,
+                "\"1\"", new Date(1559820153000L), "foo"));
+        Assert.assertNull(byteRange.getResponseResult(0,
+                "Mon, 31 Mar 2014 09:24:49 GMT",
+                new Date(1559820153000L), "foo"));
+    }
+
+    @Test
+    public void testGetResponseResult2() {
+        ByteRange byteRange = new ByteRange(
+                new ArrayList<>(Arrays.asList(new ByteRange.Range(-1, -1))));
+
+        Assert.assertEquals(0, byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getStart());
+        Assert.assertEquals(0, byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getEnd());
+        Assert.assertEquals(0, byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getContentLength());
+        Assert.assertEquals("bytes */0", byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getContentRange());
+        Assert.assertEquals(416, byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getStatusCode());
+    }
+
+    @Test
+    public void testGetResponseResult3() {
+        ByteRange byteRange = new ByteRange(
+                new ArrayList<>(Arrays.asList(new ByteRange.Range(5, -1))));
+
+        Assert.assertEquals(0, byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getStart());
+        Assert.assertEquals(0, byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getEnd());
+        Assert.assertEquals(0, byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getContentLength());
+        Assert.assertEquals("bytes */0", byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getContentRange());
+        Assert.assertEquals(416, byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getStatusCode());
+    }
+
+    @Test
+    public void testGetResponseResult4() {
+        ByteRange byteRange = new ByteRange(
+                new ArrayList<>(Arrays.asList(new ByteRange.Range(0, -1))));
+
+        Assert.assertEquals(0, byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getStart());
+        Assert.assertEquals(-1, byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getEnd());
+        Assert.assertEquals(0, byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getContentLength());
+        Assert.assertEquals("bytes 0--1/0", byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getContentRange());
+        Assert.assertEquals(206, byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getStatusCode());
+    }
+
+    @Test
+    public void testGetResponseResult5() {
+        ByteRange byteRange = new ByteRange(
+                new ArrayList<>(Arrays.asList(new ByteRange.Range(3, 5))));
+
+        Assert.assertEquals(0, byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getStart());
+        Assert.assertEquals(0, byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getEnd());
+        Assert.assertEquals(0, byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getContentLength());
+        Assert.assertEquals("bytes */0", byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getContentRange());
+        Assert.assertEquals(416, byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getStatusCode());
+
+
+        Assert.assertEquals(3, byteRange.getResponseResult(6, null,
+                new Date(1559820153000L), "foo").getStart());
+        Assert.assertEquals(5, byteRange.getResponseResult(6, null,
+                new Date(1559820153000L), "foo").getEnd());
+        Assert.assertEquals(3, byteRange.getResponseResult(6, null,
+                new Date(1559820153000L), "foo").getContentLength());
+        Assert.assertEquals("bytes 3-5/6", byteRange.getResponseResult(6, null,
+                new Date(1559820153000L), "foo").getContentRange());
+        Assert.assertEquals(206, byteRange.getResponseResult(6, null,
+                new Date(1559820153000L), "foo").getStatusCode());
+    }
+
+    @Test
+    public void testGetResponseResult6() {
+        ByteRange byteRange = new ByteRange(
+                new ArrayList<>(Arrays.asList(new ByteRange.Range(-1, 5))));
+
+        Assert.assertEquals(0, byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getStart());
+        Assert.assertEquals(-1, byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getEnd());
+        Assert.assertEquals(0, byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getContentLength());
+        Assert.assertEquals("bytes 0--1/0", byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getContentRange());
+        Assert.assertEquals(206, byteRange.getResponseResult(0, null,
+                new Date(1559820153000L), "foo").getStatusCode());
+    }
+
+    @Test
+    public void testGetResponseResultNull() {
+        ByteRange byteRange = new ByteRange(new ArrayList<>());
+        Assert.assertNull(byteRange.getResponseResult(0, "1",
+                new Date(1559820153000L), "foo"));
+    }
+}


=====================================
coverage-report/pom.xml
=====================================
@@ -3,7 +3,7 @@
     <parent>
         <groupId>io.undertow</groupId>
         <artifactId>undertow-parent</artifactId>
-        <version>2.0.26.Final</version>
+        <version>2.0.27.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.26.Final</version>
+        <version>2.0.27.Final</version>
     </parent>
 
     <groupId>io.undertow</groupId>
     <artifactId>undertow-dist</artifactId>
-    <version>2.0.26.Final</version>
+    <version>2.0.27.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.26.Final</version>
+        <version>2.0.27.Final</version>
     </parent>
 
     <groupId>io.undertow</groupId>
     <artifactId>undertow-examples</artifactId>
-    <version>2.0.26.Final</version>
+    <version>2.0.27.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.26.Final</version>
+        <version>2.0.27.Final</version>
     </parent>
 
     <groupId>io.undertow</groupId>
     <artifactId>karaf</artifactId>
-    <version>2.0.26.Final</version>
+    <version>2.0.27.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.26.Final</version>
+        <version>2.0.27.Final</version>
     </parent>
 
     <groupId>io.undertow</groupId>
     <artifactId>undertow-parser-generator</artifactId>
-    <version>2.0.26.Final</version>
+    <version>2.0.27.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.26.Final</version>
+    <version>2.0.27.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.26.Final</version>
+        <version>2.0.27.Final</version>
     </parent>
 
     <groupId>io.undertow</groupId>
     <artifactId>undertow-servlet</artifactId>
-    <version>2.0.26.Final</version>
+    <version>2.0.27.Final</version>
 
     <name>Undertow Servlet</name>
 


=====================================
servlet/src/main/java/io/undertow/servlet/attribute/ServletRequestLineAttribute.java
=====================================
@@ -21,7 +21,11 @@ package io.undertow.servlet.attribute;
 import io.undertow.attribute.ExchangeAttribute;
 import io.undertow.attribute.ExchangeAttributeBuilder;
 import io.undertow.attribute.ReadOnlyAttributeException;
+import io.undertow.attribute.RequestLineAttribute;
 import io.undertow.server.HttpServerExchange;
+import io.undertow.servlet.handlers.ServletRequestContext;
+
+import javax.servlet.RequestDispatcher;
 
 /**
  * The request line
@@ -41,11 +45,19 @@ public class ServletRequestLineAttribute implements ExchangeAttribute {
 
     @Override
     public String readAttribute(final HttpServerExchange exchange) {
+        ServletRequestContext src = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
+        if (src == null) {
+            return RequestLineAttribute.INSTANCE.readAttribute(exchange);
+        }
         StringBuilder sb = new StringBuilder()
                 .append(exchange.getRequestMethod().toString())
                 .append(' ')
                 .append(ServletRequestURLAttribute.INSTANCE.readAttribute(exchange));
-        if (!exchange.getQueryString().isEmpty()) {
+        String query = (String) src.getServletRequest().getAttribute(RequestDispatcher.FORWARD_QUERY_STRING);
+        if (query != null && !query.isEmpty()) {
+            sb.append('?');
+            sb.append(query);
+        } else if (!exchange.getQueryString().isEmpty()) {
             sb.append('?');
             sb.append(exchange.getQueryString());
         }


=====================================
servlet/src/main/java/io/undertow/servlet/compat/rewrite/RewriteHandler.java
=====================================
@@ -218,6 +218,7 @@ public class RewriteHandler implements HttpHandler {
                 chunk.append(request.getContextPath());
                 chunk.append(urlString);
                 String requestPath = chunk.toString();
+                exchange.setRequestURI(requestPath);
                 exchange.setRequestPath(requestPath);
                 exchange.setRelativePath(urlString);
 


=====================================
servlet/src/test/java/io/undertow/servlet/test/compat/rewrite/RewriteTestCase.java
=====================================
@@ -50,7 +50,6 @@ import java.nio.charset.StandardCharsets;
 @RunWith(DefaultServer.class)
 public class RewriteTestCase {
 
-
     @BeforeClass
     public static void setup() throws ServletException {
         DeploymentUtils.setupServlet(new ServletExtension() {
@@ -68,12 +67,12 @@ public class RewriteTestCase {
                                              });
                                          }
                                      },
-                new ServletInfo("servlet", PathTestServlet.class)
-                        .addMapping("/"));
+                new ServletInfo("fooServlet", PathTestServlet.class).addMapping("/bar1")
+        );
     }
 
     @Test
-    public void testRewrite() throws Exception{
+    public void testRewrite() throws Exception {
 
         TestHttpClient client = new TestHttpClient();
         try {
@@ -81,7 +80,7 @@ public class RewriteTestCase {
             HttpResponse result = client.execute(get);
             Assert.assertEquals(StatusCodes.OK, result.getStatusLine().getStatusCode());
             String response = HttpClientUtils.readResponse(result);
-            Assert.assertEquals("pathInfo:null queryString:null servletPath:/bar1 requestUri:/servletContext/foo1", response);
+            Assert.assertEquals("pathInfo:null queryString:null servletPath:/bar1 requestUri:/servletContext/bar1", response);
 
         } finally {
             client.getConnectionManager().shutdown();


=====================================
servlet/src/test/java/io/undertow/servlet/test/dispatcher/DispatcherForwardTestCase.java
=====================================
@@ -191,22 +191,31 @@ public class DispatcherForwardTestCase {
 
 
     @Test
-    public void testIncludeAggregatesQueryString() throws IOException {
+    public void testIncludeAggregatesQueryString() throws IOException, InterruptedException {
         TestHttpClient client = new TestHttpClient();
+        String protocol = DefaultServer.isH2() ? Protocols.HTTP_2_0_STRING : Protocols.HTTP_1_1_STRING;
         try {
+            resetLatch();
             HttpGet get = new HttpGet(DefaultServer.getDefaultServerURL() + "/servletContext/dispatch?a=b");
             get.setHeader("forward", "/path");
             HttpResponse result = client.execute(get);
             Assert.assertEquals(StatusCodes.OK, result.getStatusLine().getStatusCode());
             String response = HttpClientUtils.readResponse(result);
             Assert.assertEquals("pathInfo:null queryString:a=b servletPath:/path requestUri:/servletContext/path", response);
+            latch.await(30, TimeUnit.SECONDS);
+            //UNDERTOW-327 and UNDERTOW-1599 - make sure that the access log includes the original path and query string
+            Assert.assertEquals("GET /servletContext/dispatch?a=b " + protocol + " /servletContext/dispatch /dispatch", message);
 
+            resetLatch();
             get = new HttpGet(DefaultServer.getDefaultServerURL() + "/servletContext/dispatch?a=b");
             get.setHeader("forward", "/path?foo=bar");
             result = client.execute(get);
             Assert.assertEquals(StatusCodes.OK, result.getStatusLine().getStatusCode());
             response = HttpClientUtils.readResponse(result);
             Assert.assertEquals("pathInfo:null queryString:foo=bar servletPath:/path requestUri:/servletContext/path", response);
+            latch.await(30, TimeUnit.SECONDS);
+            //UNDERTOW-327 and UNDERTOW-1599 - make sure that the access log includes the original path and query string
+            Assert.assertEquals("GET /servletContext/dispatch?a=b " + protocol + " /servletContext/dispatch /dispatch", message);
         } finally {
             client.getConnectionManager().shutdown();
         }


=====================================
websockets-jsr/pom.xml
=====================================
@@ -25,12 +25,12 @@
     <parent>
         <groupId>io.undertow</groupId>
         <artifactId>undertow-parent</artifactId>
-        <version>2.0.26.Final</version>
+        <version>2.0.27.Final</version>
     </parent>
 
     <groupId>io.undertow</groupId>
     <artifactId>undertow-websockets-jsr</artifactId>
-    <version>2.0.26.Final</version>
+    <version>2.0.27.Final</version>
 
     <name>Undertow WebSockets JSR356 implementations</name>
 



View it on GitLab: https://salsa.debian.org/java-team/undertow/commit/494faa11193de7546c31f169639214cf4d7f6e7a

-- 
View it on GitLab: https://salsa.debian.org/java-team/undertow/commit/494faa11193de7546c31f169639214cf4d7f6e7a
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/20191020/6fdc4fbd/attachment.html>


More information about the pkg-java-commits mailing list