| author | Thomas Becker | 2011-09-30 05:54:35 (EDT) |
|---|---|---|
| committer | Jesse McConnell | 2011-09-30 08:58:39 (EDT) |
| commit | 37845a1a4fcedd6f583d0de74d3dfbc629f6b0f2 (patch) (side-by-side diff) | |
| tree | 79b803be33d60c7fa45e35419c0519a6f837f660 | |
| parent | 53b9f1cc7e4275e17aa579f46ca20cc4211dced4 (diff) | |
| download | org.eclipse.jetty.project-37845a1a4fcedd6f583d0de74d3dfbc629f6b0f2.zip org.eclipse.jetty.project-37845a1a4fcedd6f583d0de74d3dfbc629f6b0f2.tar.gz org.eclipse.jetty.project-37845a1a4fcedd6f583d0de74d3dfbc629f6b0f2.tar.bz2 | |
357687: Add ShutdownHandler + UnitTest
| -rw-r--r-- | jetty-server/pom.xml | 6 | ||||
| -rw-r--r-- | jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java | 125 | ||||
| -rw-r--r-- | jetty-server/src/test/java/org/eclipse/jetty/server/handler/ShutdownHandlerTest.java | 67 | ||||
| -rw-r--r-- | pom.xml | 5 |
4 files changed, 203 insertions, 0 deletions
diff --git a/jetty-server/pom.xml b/jetty-server/pom.xml index 13f653d..edd48f0 100644 --- a/jetty-server/pom.xml +++ b/jetty-server/pom.xml @@ -107,5 +107,11 @@ <version>${project.version}</version> <optional>true</optional> </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>1.8.5</version> + <scope>test</scope> + </dependency> </dependencies> </project> diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java new file mode 100644 index 0000000..a0edc40 --- a/dev/null +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ShutdownHandler.java @@ -0,0 +1,125 @@ +// ======================================================================== +// Copyright (c) 2009-2009 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.server.handler; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + +/* ------------------------------------------------------------ */ +/** + * A handler that shuts the server down on a valid request. Used to do "soft" restarts from Java. This handler is a contribution from Johannes Brodwall: + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=357687 + * + * Usage: + * + * <pre> + * Server server = new Server(8080); + * HandlerList handlers = new HandlerList(); + * handlers.setHandlers(new Handler[] + * { someOtherHandler, new ShutdownHandler(server,"secret password") }); + * server.setHandler(handlers); + * server.start(); + * </pre> + */ +public class ShutdownHandler extends AbstractHandler +{ + private static final Logger LOG = Log.getLogger(ShutdownHandler.class); + + private final String shutdownToken; + + private final Server jettyServer; + + private boolean exitJvm = false; + + /** + * Creates a listener that lets the server be shut down remotely (but only from localhost). + * + * @param server + * the Jetty instance that should be shut down + * @param shutdownToken + * a secret password to avoid unauthorized shutdown attempts + */ + public ShutdownHandler(Server server, String shutdownToken) + { + this.jettyServer = server; + this.shutdownToken = shutdownToken; + } + + public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + if (!target.equals("/shutdown")) + { + return; + } + + if (!request.getMethod().equals("POST")) + { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + return; + } + if (!hasCorrectSecurityToken(request)) + { + LOG.warn("Unauthorized shutdown attempt from " + getRemoteAddr(request)); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + return; + } + if (!requestFromLocalhost(request)) + { + LOG.warn("Unauthorized shutdown attempt from " + getRemoteAddr(request)); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + return; + } + + LOG.info("Shutting down by request from " + getRemoteAddr(request)); + try + { + shutdownServer(); + } + catch (Exception e) + { + throw new RuntimeException("Shutting down server",e); + } + } + + private boolean requestFromLocalhost(HttpServletRequest request) + { + return "127.0.0.1".equals(getRemoteAddr(request)); + } + + protected String getRemoteAddr(HttpServletRequest request) + { + return request.getRemoteAddr(); + } + + private boolean hasCorrectSecurityToken(HttpServletRequest request) + { + return shutdownToken.equals(request.getParameter("token")); + } + + void shutdownServer() throws Exception + { + jettyServer.stop(); + if (exitJvm) + System.exit(0); + } + +} diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ShutdownHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ShutdownHandlerTest.java new file mode 100644 index 0000000..0f274ec --- a/dev/null +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ShutdownHandlerTest.java @@ -0,0 +1,67 @@ +package org.eclipse.jetty.server.handler; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.server.Server; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +public class ShutdownHandlerTest +{ + @Mock private HttpServletRequest request; + @Mock private HttpServletResponse response; + + private Server server = new Server(0); + private String shutdownToken = "asdlnsldgnklns"; + + // class under test + private ShutdownHandler shutdownHandler; + + @Before + public void startServer() throws Exception + { + MockitoAnnotations.initMocks(this); + server.start(); + shutdownHandler = new ShutdownHandler(server,shutdownToken); + } + + @Test + public void shutdownServerWithCorrectTokenAndIPTest() throws Exception + { + setDefaultExpectations(); + shutdownHandler.handle("/shutdown",null,request,response); + assertEquals("Server should be stopped","STOPPED",server.getState()); + } + + @Test + public void wrongTokenTest() throws Exception + { + setDefaultExpectations(); + when(request.getParameter("token")).thenReturn("anothertoken"); + shutdownHandler.handle("/shutdown",null,request,response); + assertEquals("Server should be running","STARTED",server.getState()); + } + + @Test + public void shutdownRequestNotFromLocalhostTest() throws Exception + { + setDefaultExpectations(); + when(request.getRemoteAddr()).thenReturn("192.168.3.3"); + shutdownHandler.handle("/shutdown",null,request,response); + assertEquals("Server should be running","STARTED",server.getState()); + } + + private void setDefaultExpectations() + { + when(request.getMethod()).thenReturn("POST"); + when(request.getParameter("token")).thenReturn(shutdownToken); + when(request.getRemoteAddr()).thenReturn("127.0.0.1"); + } + +} @@ -407,6 +407,11 @@ <artifactId>junit</artifactId> <version>${junit-version}</version> </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>1.8.5</version> + </dependency> </dependencies> </dependencyManagement> <!-- |

