[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