Merge remote-tracking branch 'origin/jetty-9.2.x'

Conflicts:
	jetty-io/src/main/java/org/eclipse/jetty/io/SelectChannelEndPoint.java
	jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppContext.java
This commit is contained in:
Greg Wilkins 2014-11-27 13:34:45 +11:00
commit 1159111c52
13 changed files with 340 additions and 102 deletions

View File

@ -27,7 +27,9 @@
<configuration>
<instructions>
<Bundle-Description>Jetty-specific ServletContainerInitializer for Jasper</Bundle-Description>
<Export-Package>org.eclipse.jetty.apache.jsp.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"</Export-Package>
<Export-Package>org.eclipse.jetty.apache.jsp.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}",
org.eclipse.jetty.jsp.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}"
</Export-Package>
<Require-Capability>osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)"</Require-Capability>
<Provide-Capability>osgi.serviceloader; osgi.serviceloader=javax.servlet.ServletContainerInitializer</Provide-Capability>
<_nouses>true</_nouses>
@ -68,6 +70,11 @@
<artifactId>jetty-util</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Schemas -->
<dependency>

View File

@ -0,0 +1,101 @@
//
// ========================================================================
// 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.jsp;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.jasper.servlet.JspServlet;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.Resource;
/**
* JettyJspServlet
*
* Wrapper for the jsp servlet that handles receiving requests mapped from
* jsp-property-groups. Mappings could be wildcard urls like "/*", which would
* include welcome files, but we need those to be handled by the DefaultServlet.
*/
public class JettyJspServlet extends JspServlet
{
/**
*
*/
private static final long serialVersionUID = -5387857473125086791L;
@Override
public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
HttpServletRequest request = null;
if (req instanceof HttpServletRequest)
request = (HttpServletRequest)req;
else
throw new ServletException("Request not HttpServletRequest");
String servletPath=null;
String pathInfo=null;
if (request.getAttribute("javax.servlet.include.request_uri")!=null)
{
servletPath=(String)request.getAttribute("javax.servlet.include.servlet_path");
pathInfo=(String)request.getAttribute("javax.servlet.include.path_info");
if (servletPath==null)
{
servletPath=request.getServletPath();
pathInfo=request.getPathInfo();
}
}
else
{
servletPath = request.getServletPath();
pathInfo = request.getPathInfo();
}
String pathInContext = URIUtil.addPaths(servletPath,pathInfo);
if (pathInContext.endsWith("/"))
{
//dispatch via forward to the default servlet
getServletContext().getNamedDispatcher("default").forward(req, resp);
return;
}
else
{
//check if it resolves to a directory
Resource resource = ((ContextHandler.Context)getServletContext()).getContextHandler().getResource(pathInContext);
if (resource!=null && resource.isDirectory())
{
//dispatch via forward to the default servlet
getServletContext().getNamedDispatcher("default").forward(req, resp);
return;
}
}
//fall through to the normal jsp servlet handling
super.service(req, resp);
}
}

View File

@ -477,8 +477,8 @@
<goal>copy-dependencies</goal>
</goals>
<configuration>
<includeGroupIds>org.eclipse.jetty.orbit,org.glassfish.web, org.glassfish, javax.el, javax.servlet.jsp, org.eclipse.jetty.toolchain</includeGroupIds>
<includeArtifactIds>org.eclipse.jdt.core, javax.servlet.jsp-api, javax.servlet.jsp, jetty-jsp-jdt, javax.el-api, javax.el</includeArtifactIds>
<includeGroupIds>org.eclipse.jetty.orbit,org.glassfish.web, org.glassfish, javax.el, javax.servlet.jsp, org.eclipse.jetty.toolchain, org.eclipse.jetty</includeGroupIds>
<includeArtifactIds>org.eclipse.jdt.core, javax.servlet.jsp-api, javax.servlet.jsp, jetty-jsp-jdt, javax.el-api, javax.el, jetty-jsp</includeArtifactIds>
<includeTypes>jar</includeTypes>
<outputDirectory>${assembly-directory}/lib/jsp</outputDirectory>
</configuration>

View File

@ -251,10 +251,10 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements SelectorMa
{
int oldInterestOps = _key.interestOps();
int newInterestOps = _interestOps;
if (LOG.isDebugEnabled())
LOG.debug("Key interests update {} -> {} for {}", oldInterestOps, newInterestOps, this);
if (oldInterestOps != newInterestOps)
_key.interestOps(newInterestOps);
if (LOG.isDebugEnabled())
LOG.debug("Key interests updated {} -> {} on {}", oldInterestOps, newInterestOps, this);
}
catch (CancelledKeyException x)
{

View File

@ -46,7 +46,8 @@ abstract public class WriteFlusher
{
private static final Logger LOG = Log.getLogger(WriteFlusher.class);
private static final boolean DEBUG = LOG.isDebugEnabled(); // Easy for the compiler to remove the code if DEBUG==false
private static final ByteBuffer[] EMPTY_BUFFERS = new ByteBuffer[0];
private static final ByteBuffer[] NO_BUFFERS = new ByteBuffer[0];
private static final ByteBuffer[] EMPTY_BUFFERS = new ByteBuffer[]{BufferUtil.EMPTY_BUFFER};
private static final EnumMap<StateType, Set<StateType>> __stateTransitions = new EnumMap<>(StateType.class);
private static final State __IDLE = new IdleState();
private static final State __WRITING = new WritingState();
@ -245,7 +246,7 @@ abstract public class WriteFlusher
private PendingState(ByteBuffer[] buffers, Callback callback)
{
super(StateType.PENDING);
_buffers = compact(buffers);
_buffers = buffers;
_callback = callback;
}
@ -269,41 +270,6 @@ abstract public class WriteFlusher
if (_callback!=null)
_callback.succeeded();
}
/**
* Compacting the buffers is needed because the semantic of WriteFlusher is
* to write the buffers and if the caller sees that the buffer is consumed,
* then it can recycle it.
* If we do not compact, then it is possible that we store a consumed buffer,
* which is then recycled and refilled; when the WriteFlusher is invoked to
* complete the write, it will write the refilled bytes, garbling the content.
*
* @param buffers the buffers to compact
* @return the compacted buffers
*/
private ByteBuffer[] compact(ByteBuffer[] buffers)
{
int length = buffers.length;
// Just one element, no need to compact
if (length < 2)
return buffers;
// How many still have content ?
int consumed = 0;
while (consumed < length && BufferUtil.isEmpty(buffers[consumed]))
++consumed;
// All of them still have content, no need to compact
if (consumed == 0)
return buffers;
// None has content, return empty
if (consumed == length)
return EMPTY_BUFFERS;
return Arrays.copyOfRange(buffers,consumed,length);
}
}
/**
@ -436,36 +402,36 @@ abstract public class WriteFlusher
*/
protected ByteBuffer[] flush(ByteBuffer[] buffers) throws IOException
{
// try the simple direct flush first, which also ensures that any null buffer
// flushes are passed through (for commits etc.)
if (_endPoint.flush(buffers))
return null;
// We were not fully flushed, so let's try again iteratively while we can make
// some progress
boolean progress=true;
while(true)
while(progress && buffers!=null)
{
// Compact buffers array?
int not_empty=0;
while(not_empty<buffers.length && BufferUtil.isEmpty(buffers[not_empty]))
not_empty++;
if (not_empty==buffers.length)
return null;
if (not_empty>0)
buffers=Arrays.copyOfRange(buffers,not_empty,buffers.length);
int before=buffers[0].remaining();
if (!progress)
break;
// try to flush the remainder
int r=buffers[0].remaining();
if (_endPoint.flush(buffers))
return null;
progress=r!=buffers[0].remaining();
}
int r=buffers[0].remaining();
progress=before!=r;
int not_empty=0;
while(r==0)
{
if (++not_empty==buffers.length)
{
buffers=null;
not_empty=0;
break;
}
r=buffers[not_empty].remaining();
}
if (not_empty>0)
buffers=Arrays.copyOfRange(buffers,not_empty,buffers.length);
}
return buffers;
// If buffers is null, then flush has returned false but has consumed all the data!
// This is probably SSL being unable to flush the encrypted buffer, so return EMPTY_BUFFERS
// and that will keep this WriteFlusher pending.
return buffers==null?EMPTY_BUFFERS:buffers;
}
/* ------------------------------------------------------------ */

View File

@ -35,6 +35,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSocket;
import org.eclipse.jetty.io.SelectorManager.ManagedSelector;
import org.eclipse.jetty.io.ssl.SslConnection;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.BufferUtil;
@ -88,14 +89,45 @@ public class SslConnectionTest
@Override
protected SelectChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey selectionKey) throws IOException
{
SelectChannelEndPoint endp = new SelectChannelEndPoint(channel,selectSet, selectionKey, getScheduler(), 60000);
SelectChannelEndPoint endp = new TestEP(channel,selectSet, selectionKey, getScheduler(), 60000);
_lastEndp=endp;
return endp;
}
};
// Must be volatile or the test may fail spuriously
protected volatile int _blockAt=0;
static final AtomicInteger __startBlocking = new AtomicInteger();
static final AtomicInteger __blockFor = new AtomicInteger();
private static class TestEP extends SelectChannelEndPoint
{
public TestEP(SocketChannel channel, ManagedSelector selector, SelectionKey key, Scheduler scheduler, long idleTimeout)
{
super(channel,selector,key,scheduler,idleTimeout);
}
@Override
protected void onIncompleteFlush()
{
super.onIncompleteFlush();
}
@Override
public boolean flush(ByteBuffer... buffers) throws IOException
{
if (__startBlocking.get()==0 || __startBlocking.decrementAndGet()==0)
{
if (__blockFor.get()>0 && __blockFor.getAndDecrement()>0)
{
return false;
}
}
String s=BufferUtil.toDetailString(buffers[0]);
boolean flushed=super.flush(buffers);
return flushed;
}
}
@BeforeClass
public static void initSslEngine() throws Exception
@ -138,7 +170,7 @@ public class SslConnectionTest
public TestConnection(EndPoint endp)
{
super(endp, _threadPool,false);
super(endp, _threadPool,true);
}
@Override
@ -245,7 +277,7 @@ public class SslConnectionTest
len=5;
while(len>0)
len-=client.getInputStream().read(buffer);
Assert.assertEquals(0, _dispatches.get());
Assert.assertEquals(1, _dispatches.get());
client.close();
}
@ -270,6 +302,36 @@ public class SslConnectionTest
Assert.assertEquals(null,_writeCallback.get(100,TimeUnit.MILLISECONDS));
client.close();
}
@Test
public void testBlockedWrite() throws Exception
{
Socket client = newClient();
client.setSoTimeout(60000);
SocketChannel server = _connector.accept();
server.configureBlocking(false);
_manager.accept(server);
__startBlocking.set(5);
__blockFor.set(3);
client.getOutputStream().write("Hello".getBytes(StandardCharsets.UTF_8));
byte[] buffer = new byte[1024];
int len=client.getInputStream().read(buffer);
Assert.assertEquals(5, len);
Assert.assertEquals("Hello",new String(buffer,0,len,StandardCharsets.UTF_8));
_dispatches.set(0);
client.getOutputStream().write("World".getBytes(StandardCharsets.UTF_8));
len=5;
while(len>0)
len-=client.getInputStream().read(buffer);
Assert.assertEquals(0, len);
client.close();
}
@Test
public void testManyLines() throws Exception

View File

@ -18,6 +18,20 @@
</build>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<!-- Schemas -->
<dependency>
<groupId>org.eclipse.jetty.toolchain</groupId>

View File

@ -0,0 +1,102 @@
//
// ========================================================================
// 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.jsp;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.jasper.servlet.JspServlet;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.resource.Resource;
/**
* JettyJspServlet
*
* Wrapper for the jsp servlet that handles receiving requests mapped from
* jsp-property-groups. Mappings could be wildcard urls like "/*", which would
* include welcome files, but we need those to be handled by the DefaultServlet.
*/
public class JettyJspServlet extends JspServlet
{
/**
*
*/
private static final long serialVersionUID = -5387857473125086791L;
@Override
public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
HttpServletRequest request = null;
if (req instanceof HttpServletRequest)
request = (HttpServletRequest)req;
else
throw new ServletException("Request not HttpServletRequest");
String servletPath=null;
String pathInfo=null;
if (request.getAttribute("javax.servlet.include.request_uri")!=null)
{
servletPath=(String)request.getAttribute("javax.servlet.include.servlet_path");
pathInfo=(String)request.getAttribute("javax.servlet.include.path_info");
if (servletPath==null)
{
servletPath=request.getServletPath();
pathInfo=request.getPathInfo();
}
}
else
{
servletPath = request.getServletPath();
pathInfo = request.getPathInfo();
}
String pathInContext = URIUtil.addPaths(servletPath,pathInfo);
if (pathInContext.endsWith("/"))
{
//dispatch via forward to the default servlet
getServletContext().getNamedDispatcher("default").forward(req, resp);
return;
}
else
{
//check if it resolves to a directory
Resource resource = ((ContextHandler.Context)getServletContext()).getContextHandler().getResource(pathInContext);
if (resource!=null && resource.isDirectory())
{
//dispatch via forward to the default servlet
getServletContext().getNamedDispatcher("default").forward(req, resp);
return;
}
}
//fall through to the normal jsp servlet handling
super.service(req, resp);
}
}

View File

@ -163,6 +163,7 @@
org.apache.taglibs.standard.tei;version="1.2";resolution:=optional,
org.apache.taglibs.standard.tlv;version="1.2";resolution:=optional,
org.apache.tomcat;version="[8.0.9,9)";resolution:=optional,
org.eclipse.jetty.jsp;version="[9.2,10)";resolution:=optional,
org.osgi.*,
org.xml.*;resolution:=optional,
org.xml.sax.*;resolution:=optional,
@ -172,7 +173,7 @@
javax.xml.parser;resolution:=optional
</Import-Package>
<_nouses>true</_nouses>
<DynamicImport-Package>org.apache.jasper.*;version="8.0.9",org.apache.el.*;version="8.0.9"</DynamicImport-Package>
<DynamicImport-Package>org.eclipse.jetty.jsp.*;version="9.2.6",org.apache.jasper.*;version="8.0.9",org.apache.el.*;version="8.0.9"</DynamicImport-Package>
</instructions>
</configuration>
</plugin>

View File

@ -557,7 +557,7 @@ public class Server extends HandlerWrapper implements Attributes
if (LOG.isDebugEnabled())
{
LOG.debug(request.getDispatcherType()+" "+target+" on "+connection);
LOG.debug(request.getDispatcherType()+" "+request.getMethod()+" "+target+" on "+connection);
handle(target, baseRequest, request, response);
LOG.debug("RESPONSE "+target+" "+connection.getResponse().getStatus());
}

View File

@ -201,13 +201,13 @@
<!-- ==================================================================== -->
<!-- JSP Servlet -->
<!-- This is the jasper JSP servlet from the jakarta project -->
<!-- This is the jasper JSP servlet. -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- The JSP page compiler and execution servlet, which is the mechanism -->
<!-- used by Glassfish to support JSP pages. Traditionally, this servlet -->
<!-- is mapped to URL patterh "*.jsp". This servlet supports the -->
<!-- following initialization parameters (default values are in square -->
<!-- brackets): -->
<!-- used by the jsp container to support JSP pages. Traditionally, -->
<!-- this servlet is mapped to URL pattern "*.jsp". This servlet -->
<!-- supports the following initialization parameters (default values -->
<!-- are in square brackets): -->
<!-- -->
<!-- checkInterval If development is false and reloading is true, -->
<!-- background compiles are enabled. checkInterval -->
@ -215,7 +215,7 @@
<!-- if a JSP page needs to be recompiled. [300] -->
<!-- -->
<!-- compiler Which compiler Ant should use to compile JSP -->
<!-- pages. See the Ant documenation for more -->
<!-- pages. See the Ant documentation for more -->
<!-- information. [javac] -->
<!-- -->
<!-- classdebuginfo Should the class file be compiled with -->
@ -276,17 +276,10 @@
<!-- xpoweredBy Determines whether X-Powered-By response -->
<!-- header is added by generated servlet [false] -->
<!-- -->
<!-- If you wish to use Jikes to compile JSP pages: -->
<!-- Set the init parameter "compiler" to "jikes". Define -->
<!-- the property "-Dbuild.compiler.emacs=true" when starting Jetty -->
<!-- to cause Jikes to emit error messages in a format compatible with -->
<!-- Jasper. -->
<!-- If you get an error reporting that jikes can't use UTF-8 encoding, -->
<!-- try setting the init parameter "javaEncoding" to "ISO-8859-1". -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<servlet id="jsp">
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<servlet-class>org.eclipse.jetty.jsp.JettyJspServlet</servlet-class>
<init-param>
<param-name>logVerbosityLevel</param-name>
<param-value>DEBUG</param-value>

View File

@ -18,7 +18,6 @@
package org.eclipse.jetty.webapp;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
@ -34,21 +33,20 @@ import java.util.Set;
import javax.servlet.DispatcherType;
import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletException;
import javax.servlet.SessionTrackingMode;
import org.eclipse.jetty.security.ConstraintAware;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.authentication.FormAuthenticator;
import org.eclipse.jetty.servlet.BaseHolder.Source;
import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.FilterMapping;
import org.eclipse.jetty.servlet.BaseHolder.Source;
import org.eclipse.jetty.servlet.JspPropertyGroupServlet;
import org.eclipse.jetty.servlet.ListenerHolder;
import org.eclipse.jetty.servlet.ServletContextHandler.JspConfig;
import org.eclipse.jetty.servlet.ServletContextHandler.JspPropertyGroup;
import org.eclipse.jetty.servlet.ServletContextHandler.TagLib;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlet.ServletMapping;
import org.eclipse.jetty.util.ArrayUtil;
@ -1398,10 +1396,10 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
List<String> paths = new ArrayList<String>();
while (iter.hasNext())
{
JspPropertyGroup jpg = new JspPropertyGroup();
config.addJspPropertyGroup(jpg);
XmlParser.Node group = iter.next();
//url-patterns
Iterator<XmlParser.Node> iter2 = group.iterator("url-pattern");
while (iter2.hasNext())
@ -1440,19 +1438,11 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
if (LOG.isDebugEnabled()) LOG.debug(config.toString());
}
//add mappings to the jsp servlet from the property-group mappings
if (paths.size() > 0)
{
ServletHandler handler = context.getServletHandler();
ServletHolder jsp_pg_servlet = _servletHolderMap.get(JspPropertyGroupServlet.NAME);
if (jsp_pg_servlet==null)
{
jsp_pg_servlet=new ServletHolder(JspPropertyGroupServlet.NAME,new JspPropertyGroupServlet(context,handler));
_servletHolderMap.put(JspPropertyGroupServlet.NAME,jsp_pg_servlet);
_servletHolders.add(jsp_pg_servlet);
}
ServletMapping mapping = new ServletMapping();
mapping.setServletName(JspPropertyGroupServlet.NAME);
mapping.setServletName("jsp");
mapping.setPathSpecs(paths.toArray(new String[paths.size()]));
_servletMappings.add(mapping);
}

View File

@ -118,6 +118,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
"org.eclipse.jetty.util.log.", // webapp should use server log
"org.eclipse.jetty.servlet.ServletContextHandler.Decorator", // for CDI / weld use
"org.eclipse.jetty.servlet.DefaultServlet", // webapp cannot change default servlets
"org.eclipse.jetty.jsp.JettyJspServlet", //webapp cannot change jetty jsp servlet
"org.eclipse.jetty.servlets.PushCacheFilter" //must be loaded by container classpath
} ;
@ -132,6 +133,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
"-org.eclipse.jetty.jaas.", // don't hide jaas classes
"-org.eclipse.jetty.servlets.", // don't hide jetty servlets
"-org.eclipse.jetty.servlet.DefaultServlet", // don't hide default servlet
"-org.eclipse.jetty.jsp.", //don't hide jsp servlet
"-org.eclipse.jetty.servlet.listener.", // don't hide useful listeners
"-org.eclipse.jetty.websocket.", // don't hide websocket classes from webapps (allow webapp to use ones from system classloader)
"-org.eclipse.jetty.apache.", // don't hide jetty apache impls