]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/server/handler/ShutdownHandler.java
updating jetty to jetty-9.2.16.v2016040
[gigi.git] / lib / jetty / org / eclipse / jetty / server / handler / ShutdownHandler.java
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4 //  ------------------------------------------------------------------------
5 //  All rights reserved. This program and the accompanying materials
6 //  are made available under the terms of the Eclipse Public License v1.0
7 //  and Apache License v2.0 which accompanies this distribution.
8 //
9 //      The Eclipse Public License is available at
10 //      http://www.eclipse.org/legal/epl-v10.html
11 //
12 //      The Apache License v2.0 is available at
13 //      http://www.opensource.org/licenses/apache2.0.php
14 //
15 //  You may elect to redistribute this code under either of these licenses.
16 //  ========================================================================
17 //
18
19 package org.eclipse.jetty.server.handler;
20
21 import java.io.IOException;
22 import java.net.HttpURLConnection;
23 import java.net.InetSocketAddress;
24 import java.net.SocketException;
25 import java.net.URL;
26
27 import javax.servlet.ServletException;
28 import javax.servlet.http.HttpServletRequest;
29 import javax.servlet.http.HttpServletResponse;
30
31 import org.eclipse.jetty.server.Connector;
32 import org.eclipse.jetty.server.NetworkConnector;
33 import org.eclipse.jetty.server.Request;
34 import org.eclipse.jetty.server.Server;
35 import org.eclipse.jetty.util.log.Log;
36 import org.eclipse.jetty.util.log.Logger;
37
38 /* ------------------------------------------------------------ */
39 /**
40  * A handler that shuts the server down on a valid request. Used to do "soft" restarts from Java.
41  * If _exitJvm is set to true a hard System.exit() call is being made.
42  * If _sendShutdownAtStart is set to true, starting the server will try to shut down an existing server at the same port.
43  * If _sendShutdownAtStart is set to true, make a http call to
44  * "http://localhost:" + port + "/shutdown?token=" + shutdownCookie
45  * in order to shut down the server.
46  *
47  * This handler is a contribution from Johannes Brodwall: https://bugs.eclipse.org/bugs/show_bug.cgi?id=357687
48  *
49  * Usage:
50  *
51  * <pre>
52     Server server = new Server(8080);
53     HandlerList handlers = new HandlerList();
54     handlers.setHandlers(new Handler[]
55     { someOtherHandler, new ShutdownHandler(&quot;secret password&quot;, false, true) });
56     server.setHandler(handlers);
57     server.start();
58    </pre>
59  *
60    <pre>
61    public static void attemptShutdown(int port, String shutdownCookie) {
62         try {
63             URL url = new URL("http://localhost:" + port + "/shutdown?token=" + shutdownCookie);
64             HttpURLConnection connection = (HttpURLConnection)url.openConnection();
65             connection.setRequestMethod("POST");
66             connection.getResponseCode();
67             logger.info("Shutting down " + url + ": " + connection.getResponseMessage());
68         } catch (SocketException e) {
69             logger.debug("Not running");
70             // Okay - the server is not running
71         } catch (IOException e) {
72             throw new RuntimeException(e);
73         }
74     }
75   </pre>
76  */
77 public class ShutdownHandler extends HandlerWrapper
78 {
79     private static final Logger LOG = Log.getLogger(ShutdownHandler.class);
80
81     private final String _shutdownToken;
82     private boolean _sendShutdownAtStart;
83     private boolean _exitJvm = false;
84
85
86     /**
87      * Creates a listener that lets the server be shut down remotely (but only from localhost).
88      *
89      * @param server
90      *            the Jetty instance that should be shut down
91      * @param shutdownToken
92      *            a secret password to avoid unauthorized shutdown attempts
93      */
94     @Deprecated
95     public ShutdownHandler(Server server, String shutdownToken)
96     {
97         this(shutdownToken);
98     }
99
100     public ShutdownHandler(String shutdownToken)
101     {
102         this(shutdownToken,false,false);
103     }
104     
105     /**
106      * @param shutdownToken
107      *            a secret password to avoid unauthorized shutdown attempts
108      * @param exitJVM If true, when the shutdown is executed, the handler class System.exit()
109      * @param sendShutdownAtStart If true, a shutdown is sent as a HTTP post
110      *            during startup, which will shutdown any previously running instances of
111      *            this server with an identically configured ShutdownHandler
112      */
113     public ShutdownHandler(String shutdownToken, boolean exitJVM, boolean sendShutdownAtStart)
114     {
115         this._shutdownToken = shutdownToken;
116         setExitJvm(exitJVM);
117         setSendShutdownAtStart(sendShutdownAtStart);
118     }
119     
120
121     public void sendShutdown() throws IOException
122     {
123         URL url = new URL(getServerUrl() + "/shutdown?token=" + _shutdownToken);
124         try
125         {
126             HttpURLConnection connection = (HttpURLConnection)url.openConnection();
127             connection.setRequestMethod("POST");
128             connection.getResponseCode();
129             LOG.info("Shutting down " + url + ": " + connection.getResponseCode() + " " + connection.getResponseMessage());
130         }
131         catch (SocketException e)
132         {
133             LOG.debug("Not running");
134             // Okay - the server is not running
135         }
136         catch (IOException e)
137         {
138             throw new RuntimeException(e);
139         }
140     }
141
142     @SuppressWarnings("resource")
143     private String getServerUrl()
144     {
145         NetworkConnector connector=null;
146         for (Connector c: getServer().getConnectors())
147         {
148             if (c instanceof NetworkConnector)
149             {
150                 connector=(NetworkConnector)c;
151                 break;
152             }
153         }
154
155         if (connector==null)
156             return "http://localhost";
157
158         return "http://localhost:" + connector.getPort();
159     }
160     
161     
162     @Override
163     protected void doStart() throws Exception
164     {
165         super.doStart();
166         if (_sendShutdownAtStart)
167             sendShutdown();
168     }
169     
170     @Override
171     public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
172     {
173         if (!target.equals("/shutdown"))
174         {
175             super.handle(target,baseRequest,request,response);
176             return;
177         }
178
179         if (!request.getMethod().equals("POST"))
180         {
181             response.sendError(HttpServletResponse.SC_BAD_REQUEST);
182             return;
183         }
184         if (!hasCorrectSecurityToken(request))
185         {
186             LOG.warn("Unauthorized tokenless shutdown attempt from " + request.getRemoteAddr());
187             response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
188             return;
189         }
190         if (!requestFromLocalhost(baseRequest))
191         {
192             LOG.warn("Unauthorized non-loopback shutdown attempt from " + request.getRemoteAddr());
193             response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
194             return;
195         }
196
197         LOG.info("Shutting down by request from " + request.getRemoteAddr());
198         doShutdown(baseRequest, response);
199     }
200
201     protected void doShutdown(Request baseRequest, HttpServletResponse response) throws IOException {
202         for (Connector connector : getServer().getConnectors()) {
203             connector.shutdown();
204         }
205
206         response.sendError(200, "Connectors closed, commencing full shutdown");
207         baseRequest.setHandled(true);
208
209         final Server server=getServer();
210         new Thread()
211         {
212             @Override
213             public void run ()
214             {
215                 try
216                 {
217                     shutdownServer(server);
218                 }
219                 catch (InterruptedException e)
220                 {
221                     LOG.ignore(e);
222                 }
223                 catch (Exception e)
224                 {
225                     throw new RuntimeException("Shutting down server",e);
226                 }
227             }
228         }.start();
229     }
230
231     private boolean requestFromLocalhost(Request request)
232     {
233         InetSocketAddress addr = request.getRemoteInetSocketAddress();
234         if (addr == null)
235         {
236             return false;
237         }
238         return addr.getAddress().isLoopbackAddress();
239     }
240
241     private boolean hasCorrectSecurityToken(HttpServletRequest request)
242     {
243         String tok = request.getParameter("token");
244         if (LOG.isDebugEnabled())
245             LOG.debug("Token: {}", tok);
246         return _shutdownToken.equals(tok);
247     }
248
249     private void shutdownServer(Server server) throws Exception
250     {
251         server.stop();
252
253         if (_exitJvm)
254         {
255             System.exit(0);
256         }
257     }
258
259     public void setExitJvm(boolean exitJvm)
260     {
261         this._exitJvm = exitJvm;
262     }
263
264     public boolean isSendShutdownAtStart()
265     {
266         return _sendShutdownAtStart;
267     }
268
269     public void setSendShutdownAtStart(boolean sendShutdownAtStart)
270     {
271         _sendShutdownAtStart = sendShutdownAtStart;
272     }
273
274     public String getShutdownToken()
275     {
276         return _shutdownToken;
277     }
278
279     public boolean isExitJvm()
280     {
281         return _exitJvm;
282     }
283
284 }