diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/GZIPContentDecoder.java b/jetty-client/src/main/java/org/eclipse/jetty/client/GZIPContentDecoder.java index 8a457a6e89f..ab8f2a40b5f 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/GZIPContentDecoder.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/GZIPContentDecoder.java @@ -246,14 +246,12 @@ public class GZIPContentDecoder implements ContentDecoder if (output == null) { // Save the inflated bytes and loop to see if we have finished - output = new byte[decoded]; - System.arraycopy(bytes, 0, output, 0, decoded); + output = Arrays.copyOf(bytes, decoded); } else { // Accumulate inflated bytes and loop to see if we have finished - byte[] newOutput = new byte[output.length + decoded]; - System.arraycopy(output, 0, newOutput, 0, output.length); + byte[] newOutput = Arrays.copyOf(output, output.length+decoded); System.arraycopy(bytes, 0, newOutput, output.length, decoded); output = newOutput; } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/util/BufferingResponseListener.java b/jetty-client/src/main/java/org/eclipse/jetty/client/util/BufferingResponseListener.java index bc5f8146f8e..e30c6b90155 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/util/BufferingResponseListener.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/util/BufferingResponseListener.java @@ -18,11 +18,14 @@ package org.eclipse.jetty.client.util; +import java.io.ByteArrayInputStream; +import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; +import java.util.Arrays; import java.util.Locale; import org.eclipse.jetty.client.api.Response; @@ -30,6 +33,7 @@ import org.eclipse.jetty.client.api.Response.Listener; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.util.BufferUtil; /** *
Implementation of {@link Listener} that buffers the content up to a maximum length
@@ -40,7 +44,7 @@ import org.eclipse.jetty.http.HttpHeader;
public abstract class BufferingResponseListener extends Listener.Adapter
{
private final int maxLength;
- private volatile byte[] buffer = new byte[0];
+ private volatile ByteBuffer buffer;
private volatile String encoding;
/**
@@ -58,53 +62,57 @@ public abstract class BufferingResponseListener extends Listener.Adapter
*/
public BufferingResponseListener(int maxLength)
{
- this.maxLength = maxLength;
+ this.maxLength=maxLength;
}
@Override
public void onHeaders(Response response)
{
+ super.onHeaders(response);
+
HttpFields headers = response.getHeaders();
long length = headers.getLongField(HttpHeader.CONTENT_LENGTH.asString());
if (length > maxLength)
{
response.abort(new IllegalArgumentException("Buffering capacity exceeded"));
+ return;
}
- else
+
+ buffer=BufferUtil.allocate((length > 0)?(int)length:1024);
+
+ String contentType = headers.get(HttpHeader.CONTENT_TYPE);
+ if (contentType != null)
{
- String contentType = headers.get(HttpHeader.CONTENT_TYPE);
- if (contentType != null)
+ String charset = "charset=";
+ int index = contentType.toLowerCase(Locale.ENGLISH).indexOf(charset);
+ if (index > 0)
{
- String charset = "charset=";
- int index = contentType.toLowerCase(Locale.ENGLISH).indexOf(charset);
+ String encoding = contentType.substring(index + charset.length());
+ // Sometimes charsets arrive with an ending semicolon
+ index = encoding.indexOf(';');
if (index > 0)
- {
- String encoding = contentType.substring(index + charset.length());
- // Sometimes charsets arrive with an ending semicolon
- index = encoding.indexOf(';');
- if (index > 0)
- encoding = encoding.substring(0, index);
- this.encoding = encoding;
- }
+ encoding = encoding.substring(0, index);
+ this.encoding = encoding;
}
}
}
@Override
public void onContent(Response response, ByteBuffer content)
- {
- long newLength = buffer.length + content.remaining();
- if (newLength > maxLength)
+ {
+ int length = content.remaining();
+ if (length>BufferUtil.space(buffer))
{
- response.abort(new IllegalArgumentException("Buffering capacity exceeded"));
- }
- else
- {
- byte[] newBuffer = new byte[(int)newLength];
- System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
- content.get(newBuffer, buffer.length, content.remaining());
- buffer = newBuffer;
+ int requiredCapacity = buffer==null?0:buffer.capacity()+length;
+ if (requiredCapacity>maxLength)
+ response.abort(new IllegalArgumentException("Buffering capacity exceeded"));
+
+ int newCapacity = Math.min(Integer.highestOneBit(requiredCapacity) << 1, maxLength);
+ buffer = BufferUtil.ensureCapacity(buffer,newCapacity);
}
+
+ BufferUtil.append(buffer, content);
+
}
@Override
@@ -121,7 +129,9 @@ public abstract class BufferingResponseListener extends Listener.Adapter
*/
public byte[] getContent()
{
- return buffer;
+ if (buffer==null)
+ return new byte[0];
+ return BufferUtil.toArray(buffer);
}
/**
@@ -144,14 +154,9 @@ public abstract class BufferingResponseListener extends Listener.Adapter
*/
public String getContentAsString(String encoding)
{
- try
- {
- return new String(getContent(), encoding);
- }
- catch (UnsupportedEncodingException x)
- {
- throw new UnsupportedCharsetException(encoding);
- }
+ if (buffer==null)
+ return null;
+ return BufferUtil.toString(buffer, Charset.forName(encoding));
}
/**
@@ -161,6 +166,19 @@ public abstract class BufferingResponseListener extends Listener.Adapter
*/
public String getContentAsString(Charset encoding)
{
- return new String(getContent(), encoding);
+ if (buffer==null)
+ return null;
+ return BufferUtil.toString(buffer, encoding);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @return Content as InputStream
+ */
+ public InputStream getContentAsInputStream()
+ {
+ if (buffer==null)
+ return new ByteArrayInputStream(new byte[]{});
+ return new ByteArrayInputStream(buffer.array(), buffer.arrayOffset(), buffer.remaining());
}
}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
index 14de5bd7ace..9b77675ff08 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
@@ -37,12 +37,14 @@ import java.util.List;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.zip.GZIPOutputStream;
+
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
@@ -1047,26 +1049,33 @@ public class HttpClientTest extends AbstractHttpClientServerTest
}
});
- final AtomicInteger complete = new AtomicInteger();
+ final Exchanger
- *
*/
public class HttpChannelState
{
@@ -57,16 +42,22 @@ public class HttpChannelState
private final static long DEFAULT_TIMEOUT=30000L;
+ /** The dispatched state of the HttpChannel, used to control the overall livecycle
+ */
public enum State
{
- IDLE, // Idle request
- DISPATCHED, // Request dispatched to filter/servlet
- ASYNCWAIT, // Suspended and parked
- ASYNCIO, // Has been dispatched for async IO
- COMPLETING, // Request is completable
- COMPLETED // Request is complete
+ IDLE, // Idle request
+ DISPATCHED, // Request dispatched to filter/servlet
+ ASYNC_WAIT, // Suspended and parked
+ ASYNC_WOKEN, // A thread has been dispatch to handle from ASYNCWAIT
+ ASYNC_IO, // Has been dispatched for async IO
+ COMPLETING, // Request is completable
+ COMPLETED // Request is complete
}
+ /**
+ * The actions to take as the channel moves from state to state.
+ */
public enum Action
{
REQUEST_DISPATCH, // handle a normal request dispatch
@@ -78,6 +69,11 @@ public class HttpChannelState
COMPLETE // Complete the channel
}
+ /**
+ * The state of the servlet async API. This can lead or follow the
+ * channel dispatch state and also includes reasons such as expired,
+ * dispatched or completed.
+ */
public enum Async
{
STARTED,
@@ -188,16 +184,18 @@ public class HttpChannelState
case COMPLETED:
return Action.WAIT;
- case ASYNCWAIT:
+ case ASYNC_WAIT:
+ LOG.warn("How did I get here?", new Throwable());
+ case ASYNC_WOKEN:
if (_asyncRead)
{
- _state=State.ASYNCIO;
+ _state=State.ASYNC_IO;
_asyncRead=false;
return Action.READ_CALLBACK;
}
if (_asyncWrite)
{
- _state=State.ASYNCIO;
+ _state=State.ASYNC_IO;
_asyncWrite=false;
return Action.WRITE_CALLBACK;
}
@@ -293,7 +291,7 @@ public class HttpChannelState
switch(_state)
{
case DISPATCHED:
- case ASYNCIO:
+ case ASYNC_IO:
break;
default:
throw new IllegalStateException(this.getStatusString());
@@ -301,7 +299,7 @@ public class HttpChannelState
if (_asyncRead)
{
- _state=State.ASYNCIO;
+ _state=State.ASYNC_IO;
_asyncRead=false;
return Action.READ_CALLBACK;
}
@@ -309,7 +307,7 @@ public class HttpChannelState
if (_asyncWrite)
{
_asyncWrite=false;
- _state=State.ASYNCIO;
+ _state=State.ASYNC_IO;
return Action.WRITE_CALLBACK;
}
@@ -333,7 +331,7 @@ public class HttpChannelState
case EXPIRING:
case STARTED:
scheduleTimeout();
- _state=State.ASYNCWAIT;
+ _state=State.ASYNC_WAIT;
return Action.WAIT;
}
}
@@ -360,7 +358,7 @@ public class HttpChannelState
switch(_state)
{
case DISPATCHED:
- case ASYNCIO:
+ case ASYNC_IO:
dispatch=false;
break;
default:
@@ -411,8 +409,11 @@ public class HttpChannelState
if (_async==Async.EXPIRING)
{
_async=Async.EXPIRED;
- if (_state==State.ASYNCWAIT)
+ if (_state==State.ASYNC_WAIT)
+ {
+ _state=State.ASYNC_WOKEN;
dispatch=true;
+ }
}
}
@@ -423,13 +424,17 @@ public class HttpChannelState
public void complete()
{
// just like resume, except don't set _dispatched=true;
- boolean handle;
+ boolean handle=false;
synchronized (this)
{
if (_async!=Async.STARTED && _async!=Async.EXPIRING)
throw new IllegalStateException(this.getStatusString());
_async=Async.COMPLETE;
- handle=_state==State.ASYNCWAIT;
+ if (_state==State.ASYNC_WAIT)
+ {
+ handle=true;
+ _state=State.ASYNC_WOKEN;
+ }
}
cancelTimeout();
@@ -511,7 +516,7 @@ public class HttpChannelState
switch(_state)
{
case DISPATCHED:
- case ASYNCIO:
+ case ASYNC_IO:
throw new IllegalStateException(getStatusString());
default:
break;
@@ -571,7 +576,7 @@ public class HttpChannelState
{
synchronized(this)
{
- return _state==State.ASYNCWAIT || _state==State.DISPATCHED && _async==Async.STARTED;
+ return _state==State.ASYNC_WAIT || _state==State.DISPATCHED && _async==Async.STARTED;
}
}
@@ -665,12 +670,16 @@ public class HttpChannelState
public void onReadPossible()
{
- boolean handle;
+ boolean handle=false;
synchronized (this)
{
_asyncRead=true;
- handle=_state==State.ASYNCWAIT;
+ if (_state==State.ASYNC_WAIT)
+ {
+ _state=State.ASYNC_WOKEN;
+ handle=true;
+ }
}
if (handle)
@@ -679,12 +688,16 @@ public class HttpChannelState
public void onWritePossible()
{
- boolean handle;
+ boolean handle=false;
synchronized (this)
{
_asyncWrite=true;
- handle=_state==State.ASYNCWAIT;
+ if (_state==State.ASYNC_WAIT)
+ {
+ _state=State.ASYNC_WOKEN;
+ handle=true;
+ }
}
if (handle)
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
index 333aecdcc97..397cde1fd13 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
@@ -1214,20 +1214,17 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
_protectedTargets = null;
return;
}
-
- _protectedTargets = new String[targets.length];
- System.arraycopy(targets, 0, _protectedTargets, 0, targets.length);
+
+ _protectedTargets = Arrays.copyOf(targets, targets.length);
}
/* ------------------------------------------------------------ */
- public String[] getProtectedTargets ()
+ public String[] getProtectedTargets()
{
if (_protectedTargets == null)
return null;
- String[] tmp = new String[_protectedTargets.length];
- System.arraycopy(_protectedTargets, 0, tmp, 0, _protectedTargets.length);
- return tmp;
+ return Arrays.copyOf(_protectedTargets, _protectedTargets.length);
}
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerTest.java
index 9e5745b7e0a..5dd6aa28765 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ResourceHandlerTest.java
@@ -18,6 +18,7 @@
package org.eclipse.jetty.server.handler;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
@@ -25,6 +26,8 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
@@ -43,12 +46,12 @@ import org.junit.Test;
/**
* Resource Handler test
- *
+ *
* TODO: increase the testing going on here
*/
public class ResourceHandlerTest
{
- private static final String LN = System.getProperty("line.separator");
+ private static String LN = System.getProperty("line.separator");
private static Server _server;
private static HttpConfiguration _config;
private static ServerConnector _connector;
@@ -60,29 +63,56 @@ public class ResourceHandlerTest
{
File dir = MavenTestingUtils.getTargetFile("test-classes/simple");
File huge = new File(dir,"huge.txt");
- File big=new File(dir,"big.txt");
- try (OutputStream out = new FileOutputStream(huge)) {
- for (int i=0;i<100;i++)
+ File big = new File(dir,"big.txt");
+ try (OutputStream out = new FileOutputStream(huge))
+ {
+ for (int i = 0; i < 100; i++)
{
- try (InputStream in=new FileInputStream(big))
+ try (InputStream in = new FileInputStream(big))
{
IO.copy(in,out);
}
}
}
huge.deleteOnExit();
-
+
+ // determine how the SCM of choice checked out the big.txt EOL
+ // we can't just use whatever is the OS default.
+ // because, for example, a windows system using git can be configured for EOL handling using
+ // local, remote, file lists, patterns, etc, rendering assumptions about the OS EOL choice
+ // wrong for unit tests.
+ LN = System.getProperty("line.separator");
+ try (BufferedReader reader = Files.newBufferedReader(big.toPath(),StandardCharsets.UTF_8))
+ {
+ // a buffer large enough to capture at least 1 EOL
+ char cbuf[] = new char[128];
+ reader.read(cbuf);
+ String sample = new String(cbuf);
+ if (sample.contains("\r\n"))
+ {
+ LN = "\r\n";
+ }
+ else if (sample.contains("\n\r"))
+ {
+ LN = "\n\r";
+ }
+ else
+ {
+ LN = "\n";
+ }
+ }
+
_server = new Server();
- _config=new HttpConfiguration();
+ _config = new HttpConfiguration();
_config.setOutputBufferSize(2048);
_connector = new ServerConnector(_server,new HttpConnectionFactory(_config));
-
+
_server.setConnectors(new Connector[] { _connector });
_resourceHandler = new ResourceHandler();
_resourceHandler.setMinAsyncContentLength(4096);
_resourceHandler.setMinMemoryMappedContentLength(8192);
-
+
_resourceHandler.setResourceBase(MavenTestingUtils.getTargetFile("test-classes/simple").getAbsolutePath());
_contextHandler = new ContextHandler("/resource");
@@ -96,7 +126,7 @@ public class ResourceHandlerTest
{
_server.stop();
}
-
+
@Before
public void before()
{
@@ -107,45 +137,44 @@ public class ResourceHandlerTest
public void testMissing() throws Exception
{
SimpleRequest sr = new SimpleRequest(new URI("http://localhost:" + _connector.getLocalPort()));
- Assert.assertNotNull("missing jetty.css" , sr.getString("/resource/jetty-dir.css"));
+ Assert.assertNotNull("missing jetty.css",sr.getString("/resource/jetty-dir.css"));
}
-
+
@Test
public void testSimple() throws Exception
{
SimpleRequest sr = new SimpleRequest(new URI("http://localhost:" + _connector.getLocalPort()));
- Assert.assertEquals("simple text", sr.getString("/resource/simple.txt"));
+ Assert.assertEquals("simple text",sr.getString("/resource/simple.txt"));
}
-
@Test
public void testBigFile() throws Exception
{
_config.setOutputBufferSize(2048);
SimpleRequest sr = new SimpleRequest(new URI("http://localhost:" + _connector.getLocalPort()));
- String response=sr.getString("/resource/big.txt");
+ String response = sr.getString("/resource/big.txt");
Assert.assertThat(response,Matchers.startsWith(" 1\tThis is a big file"));
- Assert.assertThat(response,Matchers.endsWith(" 400\tThis is a big file"+LN));
+ Assert.assertThat(response,Matchers.endsWith(" 400\tThis is a big file" + LN));
}
-
+
@Test
public void testBigFileBigBuffer() throws Exception
{
- _config.setOutputBufferSize(16*1024);
+ _config.setOutputBufferSize(16 * 1024);
SimpleRequest sr = new SimpleRequest(new URI("http://localhost:" + _connector.getLocalPort()));
- String response=sr.getString("/resource/big.txt");
+ String response = sr.getString("/resource/big.txt");
Assert.assertThat(response,Matchers.startsWith(" 1\tThis is a big file"));
- Assert.assertThat(response,Matchers.endsWith(" 400\tThis is a big file"+LN));
+ Assert.assertThat(response,Matchers.endsWith(" 400\tThis is a big file" + LN));
}
-
+
@Test
public void testBigFileLittleBuffer() throws Exception
{
_config.setOutputBufferSize(8);
SimpleRequest sr = new SimpleRequest(new URI("http://localhost:" + _connector.getLocalPort()));
- String response=sr.getString("/resource/big.txt");
+ String response = sr.getString("/resource/big.txt");
Assert.assertThat(response,Matchers.startsWith(" 1\tThis is a big file"));
- Assert.assertThat(response,Matchers.endsWith(" 400\tThis is a big file"+LN));
+ Assert.assertThat(response,Matchers.endsWith(" 400\tThis is a big file" + LN));
}
@Test
@@ -157,8 +186,8 @@ public class ResourceHandlerTest
Thread.sleep(1000);
String response = IO.toString(socket.getInputStream());
Assert.assertThat(response,Matchers.startsWith("HTTP/1.1 200 OK"));
- Assert.assertThat(response,Matchers.containsString(" 400\tThis is a big file"+LN+" 1\tThis is a big file"));
- Assert.assertThat(response,Matchers.endsWith(" 400\tThis is a big file"+LN));
+ Assert.assertThat(response,Matchers.containsString(" 400\tThis is a big file" + LN + " 1\tThis is a big file"));
+ Assert.assertThat(response,Matchers.endsWith(" 400\tThis is a big file" + LN));
}
}
}
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/HeadersBlockGenerator.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/HeadersBlockGenerator.java
index e5cf6be9762..bf3dfa668a3 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/HeadersBlockGenerator.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/generator/HeadersBlockGenerator.java
@@ -22,6 +22,7 @@ import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.List;
import java.util.Locale;
@@ -62,8 +63,7 @@ public class HeadersBlockGenerator
for (int i = 1; i < values.size(); ++i)
{
byte[] moreValueBytes = values.get(i).getBytes(iso1);
- byte[] newValueBytes = new byte[valueBytes.length + 1 + moreValueBytes.length];
- System.arraycopy(valueBytes, 0, newValueBytes, 0, valueBytes.length);
+ byte[] newValueBytes = Arrays.copyOf(valueBytes,valueBytes.length + 1 + moreValueBytes.length);
newValueBytes[valueBytes.length] = 0;
System.arraycopy(moreValueBytes, 0, newValueBytes, valueBytes.length + 1, moreValueBytes.length);
valueBytes = newValueBytes;
diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/HeadersBlockParser.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/HeadersBlockParser.java
index e0fdd564711..f597fbf15bc 100644
--- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/HeadersBlockParser.java
+++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/parser/HeadersBlockParser.java
@@ -21,6 +21,7 @@ package org.eclipse.jetty.spdy.parser;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.zip.ZipException;
import org.eclipse.jetty.spdy.CompressionDictionary;
@@ -114,16 +115,14 @@ public abstract class HeadersBlockParser
int needed = length - accumulated;
if (remaining < needed)
{
- byte[] local = new byte[accumulated + remaining];
- System.arraycopy(data, 0, local, 0, accumulated);
+ byte[] local = Arrays.copyOf(data,accumulated + remaining);
buffer.get(local, accumulated, remaining);
data = local;
return false;
}
else
{
- byte[] local = new byte[length];
- System.arraycopy(data, 0, local, 0, accumulated);
+ byte[] local = Arrays.copyOf(data,length);
buffer.get(local, accumulated, needed);
data = local;
return true;
@@ -199,8 +198,7 @@ public abstract class HeadersBlockParser
else
{
// Last pass needed to decompress, merge decompressed bytes
- byte[] result = new byte[decompressed.length + count];
- System.arraycopy(decompressed, 0, result, 0, decompressed.length);
+ byte[] result = Arrays.copyOf(decompressed,decompressed.length+count);
System.arraycopy(buffer, 0, result, decompressed.length, count);
return ByteBuffer.wrap(result);
}
@@ -214,8 +212,7 @@ public abstract class HeadersBlockParser
}
else
{
- byte[] result = new byte[decompressed.length + buffer.length];
- System.arraycopy(decompressed, 0, result, 0, decompressed.length);
+ byte[] result = Arrays.copyOf(decompressed,decompressed.length+buffer.length);
System.arraycopy(buffer, 0, result, decompressed.length, buffer.length);
decompressed = result;
}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/PathMatchers.java b/jetty-start/src/main/java/org/eclipse/jetty/start/PathMatchers.java
index 58ce0ca9756..74a9a42210f 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/PathMatchers.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/PathMatchers.java
@@ -59,7 +59,7 @@ public class PathMatchers
* the raw pattern (can contain "glob:" or "regex:" syntax indicator)
* @return the Path version of the pattern provided.
*/
- private static Path asPath(String pattern)
+ private static Path asPath(final String pattern)
{
String test = pattern;
if (test.startsWith("glob:"))
@@ -119,43 +119,89 @@ public class PathMatchers
*/
public static Path getSearchRoot(final String pattern)
{
- Path path = asPath(pattern);
- Path test = path.getRoot();
+ StringBuilder root = new StringBuilder();
- boolean isSyntaxed = pattern.startsWith("glob:") || pattern.startsWith("regex:");
-
- int len = path.getNameCount();
- for (int i = 0; i < len; i++)
+ int start = 0;
+ boolean syntaxed = false;
+ if (pattern.startsWith("glob:"))
{
- Path part = path.getName(i);
- if (isGlob(part.toString(),isSyntaxed))
+ start = "glob:".length();
+ syntaxed = true;
+ }
+ else if (pattern.startsWith("regex:"))
+ {
+ start = "regex:".length();
+ syntaxed = true;
+ }
+ int len = pattern.length();
+ int lastSep = 0;
+ for (int i = start; i < len; i++)
+ {
+ int cp = pattern.codePointAt(i);
+ if (cp < 127)
{
- // found a glob part, return prior parts now
- break;
- }
+ char c = (char)cp;
- // is this the last entry?
- if (i == (len - 1))
- {
- // always return prior entries
- break;
- }
+ // unix path case
+ if (c == '/')
+ {
+ root.append(c);
+ lastSep = root.length();
+ }
+ else if (c == '\\')
+ {
+ root.append("\\");
+ lastSep = root.length();
- if (test == null)
- {
- test = part;
+ // possible escaped sequence.
+ // only really interested in windows escape sequences "\\"
+ int count = countChars(pattern,i+1,'\\');
+ if (count > 0)
+ {
+ // skip extra slashes
+ i += count;
+ }
+ }
+ else
+ {
+ if (isGlob(c,syntaxed))
+ {
+ break;
+ }
+ root.append(c);
+ }
}
else
{
- test = test.resolve(part);
+ root.appendCodePoint(cp);
}
}
- if (test == null)
+ String rootPath = root.substring(0,lastSep);
+ if (rootPath.length() <= 0)
{
return EMPTY_PATH;
}
- return test;
+
+ return asPath(rootPath);
+ }
+
+ private static int countChars(String pattern, int offset, char c)
+ {
+ int count = 0;
+ int len = pattern.length();
+ for (int i = offset; i < len; i++)
+ {
+ if (pattern.charAt(i) == c)
+ {
+ count++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ return count;
}
/**
@@ -184,29 +230,24 @@ public class PathMatchers
* true if overall pattern is syntaxed with
- * STATE ACTION
- * handling() startAsync() unhandle() dispatch() complete() completed()
- * IDLE: DISPATCHED COMPLETECALLED??
- * DISPATCHED: ASYNCSTARTED COMPLETING
- * ASYNCSTARTED: ASYNCWAIT REDISPATCHING COMPLETECALLED
- * REDISPATCHING: REDISPATCHED
- * ASYNCWAIT: REDISPATCH COMPLETECALLED
- * REDISPATCH: REDISPATCHED
- * REDISPATCHED: ASYNCSTARTED COMPLETING
- * COMPLETECALLED: COMPLETING COMPLETING
- * COMPLETING: COMPLETING COMPLETED
- * COMPLETED: "glob:"
or "regex:"
* @return true if part has glob characters
*/
- private static boolean isGlob(String part, boolean syntaxed)
+ private static boolean isGlob(char c, boolean syntaxed)
{
- int len = part.length();
- for (int i = 0; i < len; i++)
+ for (char g : GLOB_CHARS)
{
- char c = part.charAt(i);
- for (char g : GLOB_CHARS)
+ if (c == g)
+ {
+ return true;
+ }
+ }
+ if (syntaxed)
+ {
+ for (char g : SYNTAXED_GLOB_CHARS)
{
if (c == g)
{
return true;
}
}
- if (syntaxed)
- {
- for (char g : SYNTAXED_GLOB_CHARS)
- {
- if (c == g)
- {
- return true;
- }
- }
- }
}
return false;
}
diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java b/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java
index cde9f562f36..0a433806f5d 100644
--- a/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java
+++ b/jetty-start/src/main/java/org/eclipse/jetty/start/StartArgs.java
@@ -361,7 +361,10 @@ public class StartArgs
StartLog.debug("rawlibref = " + rawlibref);
String libref = properties.expand(rawlibref);
StartLog.debug("expanded = " + libref);
-
+
+ // perform path escaping (needed by windows)
+ libref = libref.replaceAll("\\\\([^\\\\])","\\\\\\\\$1");
+
for (Path libpath : baseHome.getPaths(libref))
{
classpath.addComponent(libpath.toFile());
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java
index a8003c3bdf7..2043d6b5ae9 100644
--- a/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/MainTest.java
@@ -18,7 +18,7 @@
package org.eclipse.jetty.start;
-import static org.hamcrest.Matchers.*;
+import static org.hamcrest.Matchers.is;
import java.io.File;
import java.util.ArrayList;
@@ -38,7 +38,7 @@ public class MainTest
System.setProperty("jetty.home","");
System.setProperty("jetty.base","");
}
-
+
@Test
public void testBasicProcessing() throws Exception
{
@@ -69,12 +69,12 @@ public class MainTest
StartArgs args = main.processCommandLine(cmdLineArgs.toArray(new String[cmdLineArgs.size()]));
System.err.println(args);
- //Assert.assertEquals("--stop should not build module tree", 0, args.getEnabledModules().size());
+ // Assert.assertEquals("--stop should not build module tree", 0, args.getEnabledModules().size());
Assert.assertEquals("--stop missing port","10000",args.getProperties().getString("STOP.PORT"));
Assert.assertEquals("--stop missing key","foo",args.getProperties().getString("STOP.KEY"));
Assert.assertEquals("--stop missing wait","300",args.getProperties().getString("STOP.WAIT"));
}
-
+
@Test
@Ignore("Just a bit noisy for general testing")
public void testListConfig() throws Exception
@@ -90,7 +90,7 @@ public class MainTest
StartArgs args = main.processCommandLine(cmdLineArgs.toArray(new String[cmdLineArgs.size()]));
main.listConfig(args);
}
-
+
@Test
@Ignore("Just a bit noisy for general testing")
public void testHelp() throws Exception
@@ -107,12 +107,12 @@ public class MainTest
File homePath = MavenTestingUtils.getTestResourceDir("usecases/home").getAbsoluteFile();
cmdLineArgs.add("jetty.home=" + homePath);
cmdLineArgs.add("user.dir=" + homePath);
-
+
// JVM args
cmdLineArgs.add("--exec");
cmdLineArgs.add("-Xms1024m");
cmdLineArgs.add("-Xmx1024m");
-
+
// Arbitrary Libs
File extraJar = MavenTestingUtils.getTestResourceFile("extra-libs/example.jar");
File extraDir = MavenTestingUtils.getTestResourceDir("extra-resources");
@@ -127,9 +127,9 @@ public class MainTest
StartArgs args = main.processCommandLine(cmdLineArgs.toArray(new String[cmdLineArgs.size()]));
BaseHome baseHome = main.getBaseHome();
-
- Assert.assertThat("jetty.home", baseHome.getHome(), is(homePath.getAbsolutePath()));
- Assert.assertThat("jetty.base", baseHome.getBase(), is(homePath.getAbsolutePath()));
+
+ Assert.assertThat("jetty.home",baseHome.getHome(),is(homePath.getAbsolutePath()));
+ Assert.assertThat("jetty.base",baseHome.getBase(),is(homePath.getAbsolutePath()));
ConfigurationAssert.assertConfiguration(baseHome,args,"assert-home-with-jvm.txt");
}
@@ -146,10 +146,10 @@ public class MainTest
Main main = new Main();
StartArgs args = main.processCommandLine(cmdLineArgs.toArray(new String[cmdLineArgs.size()]));
BaseHome baseHome = main.getBaseHome();
-
- Assert.assertThat("jetty.home", baseHome.getHome(), is(homePath.getAbsolutePath()));
- Assert.assertThat("jetty.base", baseHome.getBase(), is(homePath.getAbsolutePath()));
-
+
+ Assert.assertThat("jetty.home",baseHome.getHome(),is(homePath.getAbsolutePath()));
+ Assert.assertThat("jetty.base",baseHome.getBase(),is(homePath.getAbsolutePath()));
+
ConfigurationAssert.assertConfiguration(baseHome,args,"assert-home-with-spaces.txt");
}
}
diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/PathMatchersAbsoluteTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/PathMatchersAbsoluteTest.java
new file mode 100755
index 00000000000..c1324807903
--- /dev/null
+++ b/jetty-start/src/test/java/org/eclipse/jetty/start/PathMatchersAbsoluteTest.java
@@ -0,0 +1,82 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.start;
+
+import static org.hamcrest.Matchers.is;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.toolchain.test.OS;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class PathMatchersAbsoluteTest
+{
+ @Parameters(name="{0} -> {1}")
+ public static List