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 ex = new Exchanger(); BufferingResponseListener listener = new BufferingResponseListener() { @Override public void onComplete(Result result) { - complete.incrementAndGet(); + try + { + ex.exchange(result.getResponse()); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } } }; - ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) + + client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) - .onResponseContent(listener) - .onComplete(listener) - .send(); + .send(listener); + + Response response = ex.exchange(null); Assert.assertEquals(200, response.getStatus()); - Assert.assertEquals(1, complete.get()); Assert.assertArrayEquals(content, listener.getContent()); - Assert.assertArrayEquals(content, response.getContent()); + } @Test diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java index e99d3cc8153..a51e4ba7b85 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; +import java.util.Arrays; import org.eclipse.jetty.http.HttpTokens.EndOfContent; import org.eclipse.jetty.util.BufferUtil; @@ -916,10 +917,8 @@ public class HttpGenerator line[versionLength+6+reason.length()]=HttpTokens.LINE_FEED; __preprepared[i] = new PreparedResponse(); - __preprepared[i]._reason=new byte[line.length-versionLength-7] ; - System.arraycopy(line,versionLength+5,__preprepared[i]._reason,0,line.length-versionLength-7); - __preprepared[i]._schemeCode=new byte[versionLength+5]; - System.arraycopy(line,0,__preprepared[i]._schemeCode,0,versionLength+5); + __preprepared[i]._schemeCode = Arrays.copyOfRange(line, 0,versionLength+5); + __preprepared[i]._reason = Arrays.copyOfRange(line, versionLength+5, line.length-2); __preprepared[i]._responseLine=line; } } @@ -1091,8 +1090,7 @@ public class HttpGenerator { super(header,value); int cbl=header.getBytesColonSpace().length; - _bytes=new byte[cbl+value.length()+2]; - System.arraycopy(header.getBytesColonSpace(),0,_bytes,0,cbl); + _bytes=Arrays.copyOf(header.getBytesColonSpace(), cbl+value.length()+2); System.arraycopy(value.getBytes(StandardCharsets.ISO_8859_1),0,_bytes,cbl,value.length()); _bytes[_bytes.length-2]=(byte)'\r'; _bytes[_bytes.length-1]=(byte)'\n'; diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java b/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java index dd44e531e32..fccc6229552 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/WriteFlusher.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.WritePendingException; +import java.util.Arrays; import java.util.EnumMap; import java.util.EnumSet; import java.util.Set; @@ -301,10 +302,7 @@ abstract public class WriteFlusher if (consumed == length) return EMPTY_BUFFERS; - int newLength = length - consumed; - ByteBuffer[] result = new ByteBuffer[newLength]; - System.arraycopy(buffers, consumed, result, 0, newLength); - return result; + return Arrays.copyOfRange(buffers,consumed,length); } } diff --git a/jetty-osgi/jetty-osgi-boot/pom.xml b/jetty-osgi/jetty-osgi-boot/pom.xml index 5a02af5fdbf..5a074b92430 100644 --- a/jetty-osgi/jetty-osgi-boot/pom.xml +++ b/jetty-osgi/jetty-osgi-boot/pom.xml @@ -112,6 +112,7 @@ javax.servlet.http;version="[3.1,3.2)", javax.transaction;version="1.1.0";resolution:=optional, javax.transaction.xa;version="1.1.0";resolution:=optional, + org.objectweb.asm;version=4;resolution:=optional, org.eclipse.jetty.annotations;version="9.0.0";resolution:=optional, org.osgi.framework, org.osgi.service.cm;version="1.2.0", diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java index 455edff54fc..9ff6b37a855 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelState.java @@ -35,21 +35,6 @@ import org.eclipse.jetty.util.thread.Scheduler; /** * Implementation of AsyncContext interface that holds the state of request-response cycle. - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
STATEACTION
handling() startAsync()unhandle() dispatch() complete() completed()
IDLE: DISPATCHED COMPLETECALLED??
DISPATCHED: ASYNCSTARTEDCOMPLETING
ASYNCSTARTED: ASYNCWAIT REDISPATCHINGCOMPLETECALLED
REDISPATCHING: REDISPATCHED
ASYNCWAIT: REDISPATCH COMPLETECALLED
REDISPATCH: REDISPATCHED
REDISPATCHED: ASYNCSTARTEDCOMPLETING
COMPLETECALLED:COMPLETING COMPLETING
COMPLETING: COMPLETING COMPLETED
COMPLETED:
*/ 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 "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 data() + { + List cases = new ArrayList<>(); + + if(OS.IS_UNIX) + { + cases.add(new Object[]{"/opt/app",true}); + cases.add(new Object[]{"/opt/app",true}); + cases.add(new Object[]{"/opt/florb",true}); + cases.add(new Object[]{"/home/user/benfranklin",true}); + cases.add(new Object[]{"glob:/home/user/benfranklin/*.jar",true}); + cases.add(new Object[]{"glob:/**/*.jar",true}); + cases.add(new Object[]{"regex:/*-[^dev].ini",true}); + } + + if(OS.IS_WINDOWS) + { + // normal declaration + cases.add(new Object[]{"D:\\code\\jetty\\jetty-start\\src\\test\\resources\\extra-libs\\example.jar",true}); + // escaped declaration + cases.add(new Object[]{"C:\\\\System32",true}); + cases.add(new Object[]{"C:\\\\Program Files",true}); + } + + cases.add(new Object[]{"etc",false}); + cases.add(new Object[]{"lib",false}); + cases.add(new Object[]{"${user.dir}",false}); + cases.add(new Object[]{"**/*.jar",false}); + cases.add(new Object[]{"glob:*.ini",false}); + cases.add(new Object[]{"regex:*-[^dev].ini",false}); + + return cases; + } + + @Parameter(value=0) + public String pattern; + @Parameter(value=1) + public boolean expected; + + @Test + public void testIsAbsolute() + { + Assert.assertThat("isAbsolute(\""+pattern+"\")",PathMatchers.isAbsolute(pattern),is(expected)); + } +} diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/PathMatchersSearchRootTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/PathMatchersSearchRootTest.java new file mode 100644 index 00000000000..7fff06e1bb7 --- /dev/null +++ b/jetty-start/src/test/java/org/eclipse/jetty/start/PathMatchersSearchRootTest.java @@ -0,0 +1,89 @@ +// +// ======================================================================== +// 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.nio.file.Path; +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 PathMatchersSearchRootTest +{ + @Parameters(name="{0}") + public static List data() + { + List cases = new ArrayList<>(); + + if (OS.IS_UNIX) + { + // absolute first + cases.add(new String[]{"/opt/app/*.jar","/opt/app"}); + cases.add(new String[]{"/lib/jvm/**/jre/lib/*.jar","/lib/jvm"}); + cases.add(new String[]{"glob:/var/lib/*.xml","/var/lib"}); + cases.add(new String[]{"glob:/var/lib/*.{xml,java}","/var/lib"}); + cases.add(new String[]{"glob:/opt/corporate/lib-{dev,prod}/*.ini","/opt/corporate"}); + cases.add(new String[]{"regex:/opt/jetty/.*/lib-(dev|prod)/*.ini","/opt/jetty"}); + + cases.add(new String[]{"/*.ini","/"}); + cases.add(new String[]{"/etc/jetty.conf","/etc"}); + cases.add(new String[]{"/common.conf","/"}); + } + + if (OS.IS_WINDOWS) + { + // absolute declaration + cases.add(new String[]{"D:\\code\\jetty\\jetty-start\\src\\test\\resources\\extra-libs\\example.jar", + "D:\\code\\jetty\\jetty-start\\src\\test\\resources\\extra-libs"}); + // escaped declaration + // absolute patterns (complete with required windows slash escaping) + cases.add(new String[]{"C:\\\\corp\\\\lib\\\\*.jar","C:\\corp\\lib"}); + cases.add(new String[]{"D:\\\\lib\\\\**\\\\jre\\\\lib\\\\*.jar","D:\\lib"}); + } + + // some relative paths + cases.add(new String[]{"lib/*.jar","lib"}); + cases.add(new String[]{"etc/jetty.xml","etc"}); + cases.add(new String[]{"start.ini","."}); + cases.add(new String[]{"start.d/","start.d"}); + return cases; + } + + @Parameter(value=0) + public String pattern; + @Parameter(value=1) + public String expectedSearchRoot; + + @Test + public void testSearchRoot() + { + Path actual = PathMatchers.getSearchRoot(pattern); + String expectedNormal = FS.separators(expectedSearchRoot); + Assert.assertThat(".getSearchRoot(\"" + pattern + "\")",actual.toString(),is(expectedNormal)); + } +} diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/PathMatchersTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/PathMatchersTest.java deleted file mode 100644 index 460f88d2a65..00000000000 --- a/jetty-start/src/test/java/org/eclipse/jetty/start/PathMatchersTest.java +++ /dev/null @@ -1,105 +0,0 @@ -// -// ======================================================================== -// 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.*; - -import java.nio.file.Path; - -import org.eclipse.jetty.toolchain.test.OS; -import org.junit.Assert; -import org.junit.Test; - -public class PathMatchersTest -{ - private void assertIsAbsolute(String pattern, boolean expected) - { - Assert.assertThat("isAbsolute(\"" + pattern + "\")",PathMatchers.isAbsolute(pattern),is(expected)); - } - - @Test - public void testIsAbsolute() - { - if (OS.IS_UNIX) - { - assertIsAbsolute("/opt/app",true); - assertIsAbsolute("/opt/florb",true); - assertIsAbsolute("/home/user/benfranklin",true); - assertIsAbsolute("glob:/home/user/benfranklin/*.jar",true); - assertIsAbsolute("glob:/**/*.jar",true); - assertIsAbsolute("regex:/*-[^dev].ini",true); - } - - if (OS.IS_WINDOWS) - { - assertIsAbsolute("C:\\\\System32",true); - assertIsAbsolute("C:\\\\Program Files",true); - } - } - - @Test - public void testIsNotAbsolute() - { - assertIsAbsolute("etc",false); - assertIsAbsolute("lib",false); - assertIsAbsolute("${user.dir}",false); - assertIsAbsolute("**/*.jar",false); - assertIsAbsolute("glob:*.ini",false); - assertIsAbsolute("regex:*-[^dev].ini",false); - } - - private void assertSearchRoot(String pattern, String expectedSearchRoot) - { - Path actual = PathMatchers.getSearchRoot(pattern); - String expectedNormal = FS.separators(expectedSearchRoot); - Assert.assertThat(".getSearchRoot(\"" + pattern + "\")",actual.toString(),is(expectedNormal)); - } - - @Test - public void testGetSearchRoot() - { - if (OS.IS_UNIX) - { - // absolute first - assertSearchRoot("/opt/app/*.jar","/opt/app"); - assertSearchRoot("/lib/jvm/**/jre/lib/*.jar","/lib/jvm"); - assertSearchRoot("glob:/var/lib/*.xml","/var/lib"); - assertSearchRoot("glob:/var/lib/*.{xml,java}","/var/lib"); - assertSearchRoot("glob:/opt/corporate/lib-{dev,prod}/*.ini","/opt/corporate"); - assertSearchRoot("regex:/opt/jetty/.*/lib-(dev|prod)/*.ini","/opt/jetty"); - - assertSearchRoot("/*.ini","/"); - assertSearchRoot("/etc/jetty.conf","/etc"); - assertSearchRoot("/common.conf","/"); - } - - if (OS.IS_WINDOWS) - { - // absolute patterns (complete with required windows slash escaping) - assertSearchRoot("C:\\\\corp\\\\lib\\\\*.jar","C:\\corp\\lib"); - assertSearchRoot("D:\\\\lib\\\\**\\\\jre\\\\lib\\\\*.jar","C:\\lib"); - } - - // some relative paths - assertSearchRoot("lib/*.jar","lib"); - assertSearchRoot("etc/jetty.xml","etc"); - assertSearchRoot("start.ini","."); - assertSearchRoot("start.d/","."); - } -} diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java index d397716bcc0..fdca4ce1350 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.util; import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -107,11 +108,12 @@ public class ArrayTernaryTrie extends AbstractTrie */ public ArrayTernaryTrie(ArrayTernaryTrie trie, double factor) { - this(trie.isCaseInsensitive(),(int)(trie._value.length*factor)); + super(trie.isCaseInsensitive()); + int capacity=(int)(trie._value.length*factor); _rows=trie._rows; - System.arraycopy(trie._value,0,_value,0,trie._value.length); - System.arraycopy(trie._tree,0,_tree,0,trie._tree.length); - System.arraycopy(trie._key,0,_key,0,trie._key.length); + _value=Arrays.copyOf(trie._value, capacity); + _tree=Arrays.copyOf(trie._tree, capacity*ROW_SIZE); + _key=Arrays.copyOf(trie._key, capacity); } /* ------------------------------------------------------------ */ diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/BufferUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/BufferUtil.java index 16d3701a2da..bd7cb06ecd1 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/BufferUtil.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/BufferUtil.java @@ -30,6 +30,7 @@ import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.util.Arrays; import org.eclipse.jetty.util.resource.Resource; @@ -215,15 +216,18 @@ public class BufferUtil */ public static byte[] toArray(ByteBuffer buffer) { - byte[] to = new byte[buffer.remaining()]; if (buffer.hasArray()) { byte[] array = buffer.array(); - System.arraycopy(array, buffer.arrayOffset() + buffer.position(), to, 0, to.length); + int from=buffer.arrayOffset() + buffer.position(); + return Arrays.copyOfRange(array,from,from+buffer.remaining()); } else + { + byte[] to = new byte[buffer.remaining()]; buffer.slice().get(to); - return to; + return to; + } } /* ------------------------------------------------------------ */ @@ -1022,6 +1026,20 @@ public class BufferUtil return true; } + public static ByteBuffer ensureCapacity(ByteBuffer buffer, int capacity) + { + if (buffer==null) + return allocate(capacity); + + if (buffer.capacity()>=capacity) + return buffer; + + if (buffer.hasArray()) + return ByteBuffer.wrap(Arrays.copyOfRange(buffer.array(), buffer.arrayOffset(), buffer.arrayOffset()+capacity),buffer.position(),buffer.remaining()); + + throw new UnsupportedOperationException(); + } + } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ByteArrayISO8859Writer.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ByteArrayISO8859Writer.java index cf1e23ffea1..80df88198be 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/ByteArrayISO8859Writer.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ByteArrayISO8859Writer.java @@ -22,6 +22,7 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.StandardCharsets; +import java.util.Arrays; /* ------------------------------------------------------------ */ @@ -253,19 +254,14 @@ public class ByteArrayISO8859Writer extends Writer { if (_fixed) throw new IOException("Buffer overflow: "+_buf.length); - byte[] buf = new byte[(_buf.length+n)*4/3]; - System.arraycopy(_buf,0,buf,0,_size); - _buf=buf; + _buf=Arrays.copyOf(_buf,(_buf.length+n)*4/3); } } - /* ------------------------------------------------------------ */ public byte[] getByteArray() { - byte[] data=new byte[_size]; - System.arraycopy(_buf,0,data,0,_size); - return data; + return Arrays.copyOf(_buf,_size); } } diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/BufferUtilTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/BufferUtilTest.java index 042d6b28b41..fbe60670368 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/BufferUtilTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/BufferUtilTest.java @@ -289,6 +289,41 @@ public class BufferUtilTest int capacity = BufferUtil.TEMP_BUFFER_SIZE*2+1024; testWriteToWithBufferThatDoesNotExposeArray(capacity); } + + + @Test + public void testEnsureCapacity() throws Exception + { + ByteBuffer b = BufferUtil.toBuffer("Goodbye Cruel World"); + assertTrue(b==BufferUtil.ensureCapacity(b, 0)); + assertTrue(b==BufferUtil.ensureCapacity(b, 10)); + assertTrue(b==BufferUtil.ensureCapacity(b, b.capacity())); + + + ByteBuffer b1 = BufferUtil.ensureCapacity(b, 64); + assertTrue(b!=b1); + assertEquals(64, b1.capacity()); + assertEquals("Goodbye Cruel World", BufferUtil.toString(b1)); + + b1.position(8); + b1.limit(13); + assertEquals("Cruel", BufferUtil.toString(b1)); + ByteBuffer b2 = b1.slice(); + assertEquals("Cruel", BufferUtil.toString(b2)); + System.err.println(BufferUtil.toDetailString(b2)); + assertEquals(8, b2.arrayOffset()); + assertEquals(5, b2.capacity()); + + assertTrue(b2==BufferUtil.ensureCapacity(b2, 5)); + + ByteBuffer b3 = BufferUtil.ensureCapacity(b2, 64); + assertTrue(b2!=b3); + assertEquals(64, b3.capacity()); + assertEquals("Cruel", BufferUtil.toString(b3)); + assertEquals(0, b3.arrayOffset()); + + } + private void testWriteToWithBufferThatDoesNotExposeArray(int capacity) throws IOException { diff --git a/jetty-websocket/javax-websocket-server-impl/pom.xml b/jetty-websocket/javax-websocket-server-impl/pom.xml index d03b6885fdb..32e39012fdc 100644 --- a/jetty-websocket/javax-websocket-server-impl/pom.xml +++ b/jetty-websocket/javax-websocket-server-impl/pom.xml @@ -63,7 +63,7 @@ javax.websocket.server Implementation org.eclipse.jetty.websocket.jsr356.server.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}" - osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)" + osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional osgi.serviceloader; osgi.serviceloader=javax.servlet.ServletContainerInitializer diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/masks/FixedMasker.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/masks/FixedMasker.java index 6f44aef8c59..2b3fc76f3b5 100644 --- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/masks/FixedMasker.java +++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/masks/FixedMasker.java @@ -18,6 +18,8 @@ package org.eclipse.jetty.websocket.client.masks; +import java.util.Arrays; + import org.eclipse.jetty.websocket.common.WebSocketFrame; public class FixedMasker implements Masker @@ -32,10 +34,9 @@ public class FixedMasker implements Masker public FixedMasker(byte[] mask) { - this.mask = new byte[4]; // Copy to avoid that external code keeps a reference // to the array parameter to modify masking on-the-fly - System.arraycopy(mask,0,mask,0,4); + this.mask=Arrays.copyOf(mask, 4); } @Override diff --git a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketFrame.java b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketFrame.java index b4b811cd789..4ed27a08237 100644 --- a/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketFrame.java +++ b/jetty-websocket/websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketFrame.java @@ -154,10 +154,7 @@ public abstract class WebSocketFrame implements Frame masked = copy.masked; mask = null; if (copy.mask != null) - { - mask = new byte[copy.mask.length]; - System.arraycopy(copy.mask,0,mask,0,mask.length); - } + mask = Arrays.copyOf(copy.mask, copy.mask.length); } @Override diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AbstractABCase.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AbstractABCase.java index 1b282b93026..4d0930c2396 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AbstractABCase.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AbstractABCase.java @@ -20,6 +20,7 @@ package org.eclipse.jetty.websocket.server.ab; import java.net.URI; import java.nio.ByteBuffer; +import java.util.Arrays; import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.util.BufferUtil; @@ -113,9 +114,7 @@ public abstract class AbstractABCase implements Fuzzed */ protected ByteBuffer copyOf(byte[] payload) { - byte copy[] = new byte[payload.length]; - System.arraycopy(payload,0,copy,0,payload.length); - return ByteBuffer.wrap(copy); + return ByteBuffer.wrap(Arrays.copyOf(payload,payload.length)); } /**