diff --git a/VERSION.txt b/VERSION.txt index de55f426f3d..452b6627bcf 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1,6 +1,7 @@ jetty-7.2.2-SNAPSHOT - + 330188 Reject web-fragment.xml with same as another already loaded one + + 330210 Improve performance of writing large bytes arrays + 330208 Support new wording on servlet-mapping and filter-mapping merging from servlet3.0a + + 330188 Reject web-fragment.xml with same as another already loaded one jetty-7.2.1.v20101111 11 November 2010 + 324679 Fixed dedection of write before static content diff --git a/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/MonitoredDirAppProviderStartupTest.java b/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/MonitoredDirAppProviderStartupTest.java index 3dc89857932..c5b98bfb41e 100644 --- a/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/MonitoredDirAppProviderStartupTest.java +++ b/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/MonitoredDirAppProviderStartupTest.java @@ -31,7 +31,6 @@ public class MonitoredDirAppProviderStartupTest @BeforeClass public static void setupEnvironment() throws Exception { - Log.getLog().setDebugEnabled(true); jetty = new XmlConfiguredJetty(); jetty.addConfiguration("jetty.xml"); jetty.addConfiguration("jetty-deploymgr-contexts.xml"); diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpBuffers.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpBuffers.java index 351ef15938c..6c32d60002a 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpBuffers.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpBuffers.java @@ -69,9 +69,9 @@ public abstract class HttpBuffers extends AbstractLifeCycle public HttpBuffers() { super(); - _requestBuffers.setBufferSize(8*1024); + _requestBuffers.setBufferSize(12*1024); _requestBuffers.setHeaderSize(6*1024); - _responseBuffers.setBufferSize(12*1024); + _responseBuffers.setBufferSize(32*1024); _responseBuffers.setHeaderSize(6*1024); } 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 08c6b176697..1fcef6dabc5 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 @@ -184,10 +184,8 @@ public class HttpGenerator extends AbstractGenerator content.clear(); _content=null; } - else if (_endp != null && _buffer == null && content.length() > 0 && _last) + else if (_endp != null && (_buffer==null || _buffer.length()==0) && _content.length() > 0 && (_last || isCommitted() && _content.length()>1024)) { - // TODO - use bypass in more cases. - // Make _content a direct buffer _bypass = true; } else if (!_bufferChunked) @@ -851,7 +849,8 @@ public class HttpGenerator extends AbstractGenerator len = _endp.flush(_header); break; case 3: - throw new IllegalStateException(); // should never happen! + len = _endp.flush(_buffer, _content, null); + break; case 2: len = _endp.flush(_buffer); break; @@ -882,13 +881,13 @@ public class HttpGenerator extends AbstractGenerator { _buffer.put(_content); _content.clear(); - _content = null; + _content=null; } } } // Are we completely finished for now? - if (!_needCRLF && !_needEOC && (_content == null || _content.length() == 0)) + if (!_needCRLF && !_needEOC && (_content==null || _content.length()==0)) { if (_state == STATE_FLUSHING) _state = STATE_END; @@ -899,7 +898,7 @@ public class HttpGenerator extends AbstractGenerator // Try to prepare more to write. prepareBuffers(); } - } + } if (len > 0) total+=len; @@ -920,57 +919,80 @@ public class HttpGenerator extends AbstractGenerator if (!_bufferChunked) { // Refill buffer if possible - if (_content != null && _content.length() > 0 && _buffer != null && _buffer.space() > 0) + if (!_bypass && _content != null && _content.length() > 0 && _buffer != null && _buffer.space() > 0) { int len = _buffer.put(_content); _content.skip(len); if (_content.length() == 0) _content = null; } - + // Chunk buffer if need be if (_contentLength == HttpTokens.CHUNKED_CONTENT) { - int size = _buffer == null ? 0 : _buffer.length(); - if (size > 0) + if ((_buffer==null||_buffer.length()==0) && _content!=null) { - // Prepare a chunk! + // this is a bypass write + int size = _content.length(); _bufferChunked = true; - // Did we leave space at the start of the buffer. - //noinspection ConstantConditions - if (_buffer.getIndex() == CHUNK_SPACE) + // if we need CRLF add this to header + if (_needCRLF) { - // Oh yes, goodie! let's use it then! - _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2); - _buffer.setGetIndex(_buffer.getIndex() - 2); - BufferUtil.prependHexInt(_buffer, size); + if (_header.length() > 0) throw new IllegalStateException("EOC"); + _header.put(HttpTokens.CRLF); + _needCRLF = false; + } + // Add the chunk size to the header + BufferUtil.putHexInt(_header, size); + _header.put(HttpTokens.CRLF); + + // Need a CRLF after the content + _needCRLF=true; + } + else if (_buffer!=null) + { + int size = _buffer.length(); + if (size > 0) + { + // Prepare a chunk! + _bufferChunked = true; - if (_needCRLF) + // Did we leave space at the start of the buffer. + //noinspection ConstantConditions + if (_buffer.getIndex() == CHUNK_SPACE) { + // Oh yes, goodie! let's use it then! _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2); _buffer.setGetIndex(_buffer.getIndex() - 2); - _needCRLF = false; - } - } - else - { - // No space so lets use the header buffer. - if (_needCRLF) - { - if (_header.length() > 0) throw new IllegalStateException("EOC"); - _header.put(HttpTokens.CRLF); - _needCRLF = false; - } - BufferUtil.putHexInt(_header, size); - _header.put(HttpTokens.CRLF); - } + BufferUtil.prependHexInt(_buffer, size); - // Add end chunk trailer. - if (_buffer.space() >= 2) - _buffer.put(HttpTokens.CRLF); - else - _needCRLF = true; + if (_needCRLF) + { + _buffer.poke(_buffer.getIndex() - 2, HttpTokens.CRLF, 0, 2); + _buffer.setGetIndex(_buffer.getIndex() - 2); + _needCRLF = false; + } + } + else + { + // No space so lets use the header buffer. + if (_needCRLF) + { + if (_header.length() > 0) throw new IllegalStateException("EOC"); + _header.put(HttpTokens.CRLF); + _needCRLF = false; + } + BufferUtil.putHexInt(_header, size); + _header.put(HttpTokens.CRLF); + } + + // Add end chunk trailer. + if (_buffer.space() >= 2) + _buffer.put(HttpTokens.CRLF); + else + _needCRLF = true; + } } // If we need EOC and everything written diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java index 56d97cef67d..2f72a16ba52 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorClientTest.java @@ -256,6 +256,8 @@ public class HttpGeneratorClientTest hb.completeHeader(fields, Generator.LAST); } hb.complete(); + while(!hb.isComplete()) + hb.flushBuffer(); } @Override diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorTest.java index 56a1c786d11..4df839924bc 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorTest.java @@ -109,7 +109,7 @@ public class HttpGeneratorTest catch(IOException e) { if (tr[r].body!=null) - throw e; + throw new Exception(t,e); continue; } @@ -190,6 +190,9 @@ public class HttpGeneratorTest hb.completeHeader(fields, Generator.LAST); } hb.complete(); + + while(!hb.isComplete()) + hb.flushBuffer(); } @Override diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectorManager.java b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectorManager.java index 8e81bac44d2..76f36c467c0 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectorManager.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/nio/SelectorManager.java @@ -46,7 +46,7 @@ import org.eclipse.jetty.util.thread.Timeout.Task; public abstract class SelectorManager extends AbstractLifeCycle { // TODO Tune these by approx system speed. - private static final int __JVMBUG_THRESHHOLD=Integer.getInteger("org.eclipse.jetty.io.nio.JVMBUG_THRESHHOLD",512).intValue(); + private static final int __JVMBUG_THRESHHOLD=Integer.getInteger("org.eclipse.jetty.io.nio.JVMBUG_THRESHHOLD",0).intValue(); private static final int __MONITOR_PERIOD=Integer.getInteger("org.eclipse.jetty.io.nio.MONITOR_PERIOD",1000).intValue(); private static final int __MAX_SELECTS=Integer.getInteger("org.eclipse.jetty.io.nio.MAX_SELECTS",25000).intValue(); private static final int __BUSY_PAUSE=Integer.getInteger("org.eclipse.jetty.io.nio.BUSY_PAUSE",50).intValue(); @@ -482,7 +482,9 @@ public abstract class SelectorManager extends AbstractLifeCycle _selects++; now = System.currentTimeMillis(); _timeout.setNow(now); - checkJvmBugs(before, now, wait, selected); + + if (__JVMBUG_THRESHHOLD>0) + checkJvmBugs(before, now, wait, selected); } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java index e980b433bbf..5fd7d91c522 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java @@ -38,7 +38,6 @@ public class HttpOutput extends ServletOutputStream { protected final AbstractGenerator _generator; protected final long _maxIdleTime; - protected final ByteArrayBuffer _buf = new ByteArrayBuffer(AbstractGenerator.NO_BYTES); private boolean _closed; // These are held here for reuse by Writer @@ -94,9 +93,7 @@ public class HttpOutput extends ServletOutputStream @Override public void write(byte[] b, int off, int len) throws IOException { - _buf.wrap(b, off, len); - write(_buf); - _buf.wrap(AbstractGenerator.NO_BYTES); + write(new ByteArrayBuffer(b,off,len)); } /* ------------------------------------------------------------ */ @@ -106,9 +103,7 @@ public class HttpOutput extends ServletOutputStream @Override public void write(byte[] b) throws IOException { - _buf.wrap(b); - write(_buf); - _buf.wrap(AbstractGenerator.NO_BYTES); + write(new ByteArrayBuffer(b)); } /* ------------------------------------------------------------ */ @@ -167,18 +162,20 @@ public class HttpOutput extends ServletOutputStream _generator.addContent(buffer, Generator.MORE); // Have to flush and complete headers? - if (_generator.isBufferFull()) - flush(); if (_generator.isAllContentWritten()) { flush(); close(); - } + } + else if (_generator.isBufferFull()) + flush(); // Block until our buffer is free while (buffer.length() > 0 && _generator.isOpen()) + { _generator.blockForOutput(_maxIdleTime); + } } /* ------------------------------------------------------------ */ diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java index e9579ccb4c4..c1da1cce022 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java @@ -29,6 +29,7 @@ import org.eclipse.jetty.http.HttpHeaders; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.log.StdErrLog; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -148,31 +149,39 @@ public class HttpConnectionTest @Test public void testBad() throws Exception { - String response=connector.getResponses("GET & HTTP/1.1\n"+ - "Host: localhost\n"+ - "\015\012"); - checkContains(response,0,"HTTP/1.1 400"); + try + { + ((StdErrLog)Log.getLog()).setHideStacks(true); - response=connector.getResponses("GET http://localhost:WRONG/ HTTP/1.1\n"+ - "Host: localhost\n"+ - "\015\012"); - checkContains(response,0,"HTTP/1.1 400"); + String response=connector.getResponses("GET & HTTP/1.1\n"+ + "Host: localhost\n"+ + "\015\012"); + checkContains(response,0,"HTTP/1.1 400"); - response=connector.getResponses("GET /foo/bar%1 HTTP/1.1\n"+ - "Host: localhost\n"+ - "\015\012"); - checkContains(response,0,"HTTP/1.1 400"); + response=connector.getResponses("GET http://localhost:WRONG/ HTTP/1.1\n"+ + "Host: localhost\n"+ + "\015\012"); + checkContains(response,0,"HTTP/1.1 400"); - response=connector.getResponses("GET /foo/bar%c0%00 HTTP/1.1\n"+ - "Host: localhost\n"+ - "\015\012"); - checkContains(response,0,"pathInfo=/foo/bar?"); + response=connector.getResponses("GET /bad/encoding%1 HTTP/1.1\n"+ + "Host: localhost\n"+ + "\015\012"); + checkContains(response,0,"HTTP/1.1 400"); - response=connector.getResponses("GET /foo/bar%c1 HTTP/1.1\n"+ - "Host: localhost\n"+ - "\015\012"); - checkContains(response,0,"HTTP/1.1 400"); + response=connector.getResponses("GET /foo/bar%c0%00 HTTP/1.1\n"+ + "Host: localhost\n"+ + "\015\012"); + checkContains(response,0,"pathInfo=/foo/bar?"); + response=connector.getResponses("GET /bad/utf8%c1 HTTP/1.1\n"+ + "Host: localhost\n"+ + "\015\012"); + checkContains(response,0,"HTTP/1.1 400"); + } + finally + { + ((StdErrLog)Log.getLog()).setHideStacks(false); + } } @Test diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java index fd979164696..6e1c9e13338 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java @@ -32,11 +32,15 @@ import java.util.Arrays; import java.util.Random; import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import junit.framework.Assert; + import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.TypeUtil; import org.junit.Test; /** @@ -212,7 +216,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture // Read the response String response=readResponse(client); - + // Check the response assertEquals("response "+i,RESPONSE2,response); } @@ -435,6 +439,137 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture client.close(); } } + + + @Test + public void testBigBlocks() throws Exception + { + configureServer(new BigBlockHandler()); + + Socket client=newSocket(HOST,_connector.getLocalPort()); + client.setSoTimeout(10000); + try + { + OutputStream os=client.getOutputStream(); + BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream())); + + os.write(( + "GET / HTTP/1.1\r\n"+ + "host: "+HOST+":"+_connector.getLocalPort()+"\r\n"+ + "\r\n"+ + "GET / HTTP/1.1\r\n"+ + "host: "+HOST+":"+_connector.getLocalPort()+"\r\n"+ + "connection: close\r\n"+ + "\r\n" + ).getBytes()); + os.flush(); + + // read the chunked response header + boolean chunked=false; + boolean closed=false; + while(true) + { + String line=in.readLine(); + if (line==null || line.length()==0) + break; + + chunked|="Transfer-Encoding: chunked".equals(line); + closed|="Connection: close".equals(line); + } + Assert.assertTrue(chunked); + Assert.assertFalse(closed); + + // Read the chunks + int max=Integer.MIN_VALUE; + while(true) + { + String chunk=in.readLine(); + String line=in.readLine(); + if (line.length()==0) + break; + int len=line.length(); + Assert.assertEquals(Integer.valueOf(chunk,16).intValue(),len); + if (max