]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/server/handler/ShutdownHandler.java
Importing upstream Jetty jetty-9.2.1.v20140609
[gigi.git] / lib / jetty / org / eclipse / jetty / server / handler / ShutdownHandler.java
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2014 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. If _exitJvm ist set to true a hard System.exit() call is being
41  * made.
42  *
43  * This handler is a contribution from Johannes Brodwall: https://bugs.eclipse.org/bugs/show_bug.cgi?id=357687
44  *
45  * Usage:
46  *
47  * <pre>
48     Server server = new Server(8080);
49     HandlerList handlers = new HandlerList();
50     handlers.setHandlers(new Handler[]
51     { someOtherHandler, new ShutdownHandler(&quot;secret password&quot;) });
52     server.setHandler(handlers);
53     server.start();
54    </pre>
55  *
56    <pre>
57    public static void attemptShutdown(int port, String shutdownCookie) {
58         try {
59             URL url = new URL("http://localhost:" + port + "/shutdown?token=" + shutdownCookie);
60             HttpURLConnection connection = (HttpURLConnection)url.openConnection();
61             connection.setRequestMethod("POST");
62             connection.getResponseCode();
63             logger.info("Shutting down " + url + ": " + connection.getResponseMessage());
64         } catch (SocketException e) {
65             logger.debug("Not running");
66             // Okay - the server is not running
67         } catch (IOException e) {
68             throw new RuntimeException(e);
69         }
70     }
71   </pre>
72  */
73 public class ShutdownHandler extends HandlerWrapper
74 {
75     private static final Logger LOG = Log.getLogger(ShutdownHandler.class);
76
77     private final String _shutdownToken;
78     private boolean _sendShutdownAtStart;
79     private boolean _exitJvm = false;
80
81
82     /**
83      * Creates a listener that lets the server be shut down remotely (but only from localhost).
84      *
85      * @param server
86      *            the Jetty instance that should be shut down
87      * @param shutdownToken
88      *            a secret password to avoid unauthorized shutdown attempts
89      */
90     @Deprecated
91     public ShutdownHandler(Server server, String shutdownToken)
92     {
93         this(shutdownToken);
94     }
95
96     public ShutdownHandler(String shutdownToken)
97     {
98         this(shutdownToken,false,false);
99     }
100     
101     /**
102      * @param shutdownToken
103      * @param sendShutdownAtStart If true, a shutdown is sent as a HTTP post
104      * during startup, which will shutdown any previously running instances of
105      * this server with an identically configured ShutdownHandler
106      */
107     public ShutdownHandler(String shutdownToken, boolean exitJVM, boolean sendShutdownAtStart)
108     {
109         this._shutdownToken = shutdownToken;
110         setExitJvm(exitJVM);
111         setSendShutdownAtStart(sendShutdownAtStart);
112     }
113     
114
115     public void sendShutdown() throws IOException
116     {
117         URL url = new URL(getServerUrl() + "/shutdown?token=" + _shutdownToken);
118         try
119         {
120             HttpURLConnection connection = (HttpURLConnection)url.openConnection();
121             connection.setRequestMethod("POST");
122             connection.getResponseCode();
123             LOG.info("Shutting down " + url + ": " + connection.getResponseMessage());
124         }
125         catch (SocketException e)
126         {
127             LOG.debug("Not running");
128             // Okay - the server is not running
129         }
130         catch (IOException e)
131         {
132             throw new RuntimeException(e);
133         }
134     }
135
136     private String getServerUrl()
137     {
138         NetworkConnector connector=null;
139         for (Connector c: getServer().getConnectors())
140         {
141             if (c instanceof NetworkConnector)
142             {
143                 connector=(NetworkConnector)c;
144                 break;
145             }
146         }
147
148         if (connector==null)
149             return "http://localhost";
150
151         return "http://localhost:" + connector.getPort();
152     }
153     
154     
155     @Override
156     protected void doStart() throws Exception
157     {
158         super.doStart();
159         if (_sendShutdownAtStart)
160             sendShutdown();
161     }
162     
163     @Override
164     public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
165     {
166         if (!target.equals("/shutdown"))
167         {
168             super.handle(target,baseRequest,request,response);
169             return;
170         }
171
172         if (!request.getMethod().equals("POST"))
173         {
174             response.sendError(HttpServletResponse.SC_BAD_REQUEST);
175             return;
176         }
177         if (!hasCorrectSecurityToken(request))
178         {
179             LOG.warn("Unauthorized tokenless shutdown attempt from " + request.getRemoteAddr());
180             response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
181             return;
182         }
183         if (!requestFromLocalhost(baseRequest))
184         {
185             LOG.warn("Unauthorized non-loopback shutdown attempt from " + request.getRemoteAddr());
186             response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
187             return;
188         }
189
190         LOG.info("Shutting down by request from " + request.getRemoteAddr());
191
192         final Server server=getServer();
193         new Thread()
194         {
195             @Override
196             public void run ()
197             {
198                 try
199                 {
200                     shutdownServer(server);
201                 }
202                 catch (InterruptedException e)
203                 {
204                     LOG.ignore(e);
205                 }
206                 catch (Exception e)
207                 {
208                     throw new RuntimeException("Shutting down server",e);
209                 }
210             }
211         }.start();
212     }
213
214     private boolean requestFromLocalhost(Request request)
215     {
216         InetSocketAddress addr = request.getRemoteInetSocketAddress();
217         if (addr == null)
218         {
219             return false;
220         }
221         return addr.getAddress().isLoopbackAddress();
222     }
223
224     private boolean hasCorrectSecurityToken(HttpServletRequest request)
225     {
226         String tok = request.getParameter("token");
227         LOG.debug("Token: {}", tok);
228         return _shutdownToken.equals(tok);
229     }
230
231     private void shutdownServer(Server server) throws Exception
232     {
233         server.stop();
234
235         if (_exitJvm)
236         {
237             System.exit(0);
238         }
239     }
240
241     public void setExitJvm(boolean exitJvm)
242     {
243         this._exitJvm = exitJvm;
244     }
245
246     public boolean isSendShutdownAtStart()
247     {
248         return _sendShutdownAtStart;
249     }
250
251     public void setSendShutdownAtStart(boolean sendShutdownAtStart)
252     {
253         _sendShutdownAtStart = sendShutdownAtStart;
254     }
255
256     public String getShutdownToken()
257     {
258         return _shutdownToken;
259     }
260
261     public boolean isExitJvm()
262     {
263         return _exitJvm;
264     }
265
266 }