]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/server/Server.java
Update notes about password security
[gigi.git] / lib / jetty / org / eclipse / jetty / server / Server.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;
20
21 import java.io.IOException;
22 import java.lang.management.ManagementFactory;
23 import java.net.InetAddress;
24 import java.net.InetSocketAddress;
25 import java.net.URI;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collections;
29 import java.util.Date;
30 import java.util.Enumeration;
31 import java.util.List;
32 import java.util.concurrent.CopyOnWriteArrayList;
33 import java.util.concurrent.Future;
34 import java.util.concurrent.TimeUnit;
35 import javax.servlet.ServletContext;
36 import javax.servlet.ServletException;
37 import javax.servlet.http.HttpServletRequest;
38 import javax.servlet.http.HttpServletResponse;
39
40 import org.eclipse.jetty.http.DateGenerator;
41 import org.eclipse.jetty.http.HttpField;
42 import org.eclipse.jetty.http.HttpGenerator;
43 import org.eclipse.jetty.http.HttpHeader;
44 import org.eclipse.jetty.http.HttpMethod;
45 import org.eclipse.jetty.http.HttpStatus;
46 import org.eclipse.jetty.http.HttpURI;
47 import org.eclipse.jetty.server.handler.ContextHandler;
48 import org.eclipse.jetty.server.handler.HandlerWrapper;
49 import org.eclipse.jetty.util.Attributes;
50 import org.eclipse.jetty.util.AttributesMap;
51 import org.eclipse.jetty.util.Jetty;
52 import org.eclipse.jetty.util.MultiException;
53 import org.eclipse.jetty.util.URIUtil;
54 import org.eclipse.jetty.util.annotation.ManagedAttribute;
55 import org.eclipse.jetty.util.annotation.ManagedObject;
56 import org.eclipse.jetty.util.annotation.Name;
57 import org.eclipse.jetty.util.component.Graceful;
58 import org.eclipse.jetty.util.component.LifeCycle;
59 import org.eclipse.jetty.util.log.Log;
60 import org.eclipse.jetty.util.log.Logger;
61 import org.eclipse.jetty.util.thread.QueuedThreadPool;
62 import org.eclipse.jetty.util.thread.ShutdownThread;
63 import org.eclipse.jetty.util.thread.ThreadPool;
64 import org.eclipse.jetty.util.thread.ThreadPool.SizedThreadPool;
65
66 /* ------------------------------------------------------------ */
67 /** Jetty HTTP Servlet Server.
68  * This class is the main class for the Jetty HTTP Servlet server.
69  * It aggregates Connectors (HTTP request receivers) and request Handlers.
70  * The server is itself a handler and a ThreadPool.  Connectors use the ThreadPool methods
71  * to run jobs that will eventually call the handle method.
72  */
73 @ManagedObject(value="Jetty HTTP Servlet server")
74 public class Server extends HandlerWrapper implements Attributes
75 {
76     private static final Logger LOG = Log.getLogger(Server.class);
77
78     private final AttributesMap _attributes = new AttributesMap();
79     private final ThreadPool _threadPool;
80     private final List<Connector> _connectors = new CopyOnWriteArrayList<>();
81     private SessionIdManager _sessionIdManager;
82     private boolean _stopAtShutdown;
83     private boolean _dumpAfterStart=false;
84     private boolean _dumpBeforeStop=false;
85     
86     private volatile DateField _dateField;
87     
88     /* ------------------------------------------------------------ */
89     public Server()
90     {
91         this((ThreadPool)null);
92     }
93
94     /* ------------------------------------------------------------ */
95     /** Convenience constructor
96      * Creates server and a {@link ServerConnector} at the passed port.
97      * @param port The port of a network HTTP connector (or 0 for a randomly allocated port).
98      * @see NetworkConnector#getLocalPort()
99      */
100     public Server(@Name("port")int port)
101     {
102         this((ThreadPool)null);
103         ServerConnector connector=new ServerConnector(this);
104         connector.setPort(port);
105         setConnectors(new Connector[]{connector});
106     }
107
108     /* ------------------------------------------------------------ */
109     /** Convenience constructor
110      * Creates server and a {@link ServerConnector} at the passed address.
111      */
112     public Server(@Name("address")InetSocketAddress addr)
113     {
114         this((ThreadPool)null);
115         ServerConnector connector=new ServerConnector(this);
116         connector.setHost(addr.getHostName());
117         connector.setPort(addr.getPort());
118         setConnectors(new Connector[]{connector});
119     }
120
121
122
123     /* ------------------------------------------------------------ */
124     public Server(@Name("threadpool") ThreadPool pool)
125     {
126         _threadPool=pool!=null?pool:new QueuedThreadPool();
127         addBean(_threadPool);
128         setServer(this);
129     }
130
131
132     /* ------------------------------------------------------------ */
133     @ManagedAttribute("version of this server")
134     public static String getVersion()
135     {
136         return Jetty.VERSION;
137     }
138
139     /* ------------------------------------------------------------ */
140     public boolean getStopAtShutdown()
141     {
142         return _stopAtShutdown;
143     }
144
145     /* ------------------------------------------------------------ */
146     public void setStopAtShutdown(boolean stop)
147     {
148         //if we now want to stop
149         if (stop)
150         {
151             //and we weren't stopping before
152             if (!_stopAtShutdown)
153             {
154                 //only register to stop if we're already started (otherwise we'll do it in doStart())
155                 if (isStarted())
156                     ShutdownThread.register(this);
157             }
158         }
159         else
160             ShutdownThread.deregister(this);
161
162         _stopAtShutdown=stop;
163     }
164
165     /* ------------------------------------------------------------ */
166     /**
167      * @return Returns the connectors.
168      */
169     @ManagedAttribute(value="connectors for this server", readonly=true)
170     public Connector[] getConnectors()
171     {
172         List<Connector> connectors = new ArrayList<>(_connectors);
173         return connectors.toArray(new Connector[connectors.size()]);
174     }
175
176     /* ------------------------------------------------------------ */
177     public void addConnector(Connector connector)
178     {
179         if (connector.getServer() != this)
180             throw new IllegalArgumentException("Connector " + connector +
181                     " cannot be shared among server " + connector.getServer() + " and server " + this);
182         if (_connectors.add(connector))
183             addBean(connector);
184     }
185
186     /* ------------------------------------------------------------ */
187     /**
188      * Convenience method which calls {@link #getConnectors()} and {@link #setConnectors(Connector[])} to
189      * remove a connector.
190      * @param connector The connector to remove.
191      */
192     public void removeConnector(Connector connector)
193     {
194         if (_connectors.remove(connector))
195             removeBean(connector);
196     }
197
198     /* ------------------------------------------------------------ */
199     /** Set the connectors for this server.
200      * Each connector has this server set as it's ThreadPool and its Handler.
201      * @param connectors The connectors to set.
202      */
203     public void setConnectors(Connector[] connectors)
204     {
205         if (connectors != null)
206         {
207             for (Connector connector : connectors)
208             {
209                 if (connector.getServer() != this)
210                     throw new IllegalArgumentException("Connector " + connector +
211                             " cannot be shared among server " + connector.getServer() + " and server " + this);
212             }
213         }
214
215         Connector[] oldConnectors = getConnectors();
216         updateBeans(oldConnectors, connectors);
217         _connectors.removeAll(Arrays.asList(oldConnectors));
218         if (connectors != null)
219             _connectors.addAll(Arrays.asList(connectors));
220     }
221
222     /* ------------------------------------------------------------ */
223     /**
224      * @return Returns the threadPool.
225      */
226     @ManagedAttribute("the server thread pool")
227     public ThreadPool getThreadPool()
228     {
229         return _threadPool;
230     }
231
232     /**
233      * @return true if {@link #dumpStdErr()} is called after starting
234      */
235     @ManagedAttribute("dump state to stderr after start")
236     public boolean isDumpAfterStart()
237     {
238         return _dumpAfterStart;
239     }
240
241     /**
242      * @param dumpAfterStart true if {@link #dumpStdErr()} is called after starting
243      */
244     public void setDumpAfterStart(boolean dumpAfterStart)
245     {
246         _dumpAfterStart = dumpAfterStart;
247     }
248
249     /**
250      * @return true if {@link #dumpStdErr()} is called before stopping
251      */
252     @ManagedAttribute("dump state to stderr before stop")
253     public boolean isDumpBeforeStop()
254     {
255         return _dumpBeforeStop;
256     }
257
258     /**
259      * @param dumpBeforeStop true if {@link #dumpStdErr()} is called before stopping
260      */
261     public void setDumpBeforeStop(boolean dumpBeforeStop)
262     {
263         _dumpBeforeStop = dumpBeforeStop;
264     }
265
266     /* ------------------------------------------------------------ */
267     public HttpField getDateField()
268     {
269         long now=System.currentTimeMillis();
270         long seconds = now/1000;
271         DateField df = _dateField;
272         
273         if (df==null || df._seconds!=seconds)
274         {
275             synchronized (this) // Trade some contention for less garbage
276             {
277                 df = _dateField;
278                 if (df==null || df._seconds!=seconds)
279                 {
280                     HttpField field=new HttpGenerator.CachedHttpField(HttpHeader.DATE,DateGenerator.formatDate(now));
281                     _dateField=new DateField(seconds,field);
282                     return field;
283                 }
284             }
285         }
286         return df._dateField;
287     }
288
289     /* ------------------------------------------------------------ */
290     @Override
291     protected void doStart() throws Exception
292     {
293         if (getStopAtShutdown())
294         {
295             ShutdownThread.register(this);
296         }
297
298         ShutdownMonitor.getInstance().start(); // initialize
299
300         LOG.info("jetty-" + getVersion());
301         HttpGenerator.setJettyVersion(HttpConfiguration.SERVER_VERSION);
302         MultiException mex=new MultiException();
303
304         // check size of thread pool
305         SizedThreadPool pool = getBean(SizedThreadPool.class);
306         int max=pool==null?-1:pool.getMaxThreads();
307         int needed=1;
308         if (mex.size()==0)
309         {
310             for (Connector connector : _connectors)
311             {
312                 if (connector instanceof AbstractConnector)
313                     needed+=((AbstractConnector)connector).getAcceptors();
314                 if (connector instanceof ServerConnector)
315                     needed+=((ServerConnector)connector).getSelectorManager().getSelectorCount();
316             }
317         }
318
319         if (max>0 && needed>max)
320             throw new IllegalStateException("Insufficient max threads in ThreadPool: max="+max+" < needed="+needed);
321         
322         try
323         {
324             super.doStart();
325         }
326         catch(Throwable e)
327         {
328             mex.add(e);
329         }
330
331         // start connectors last
332         for (Connector connector : _connectors)
333         {
334             try
335             {   
336                 connector.start();
337             }
338             catch(Throwable e)
339             {
340                 mex.add(e);
341             }
342         }
343         
344         if (isDumpAfterStart())
345             dumpStdErr();
346
347         mex.ifExceptionThrow();
348
349         LOG.info(String.format("Started @%dms",ManagementFactory.getRuntimeMXBean().getUptime()));
350     }
351
352     @Override
353     protected void start(LifeCycle l) throws Exception
354     {
355         // start connectors last
356         if (!(l instanceof Connector))
357             super.start(l);
358     }
359
360     /* ------------------------------------------------------------ */
361     @Override
362     protected void doStop() throws Exception
363     {
364         if (isDumpBeforeStop())
365             dumpStdErr();
366
367         MultiException mex=new MultiException();
368
369         // list if graceful futures
370         List<Future<Void>> futures = new ArrayList<>();
371
372         // First close the network connectors to stop accepting new connections
373         for (Connector connector : _connectors)
374             futures.add(connector.shutdown());
375
376         // Then tell the contexts that we are shutting down
377         
378         Handler[] gracefuls = getChildHandlersByClass(Graceful.class);
379         for (Handler graceful : gracefuls)
380             futures.add(((Graceful)graceful).shutdown());
381
382         // Shall we gracefully wait for zero connections?
383         long stopTimeout = getStopTimeout();
384         if (stopTimeout>0)
385         {
386             long stop_by=System.currentTimeMillis()+stopTimeout;
387             LOG.debug("Graceful shutdown {} by ",this,new Date(stop_by));
388
389             // Wait for shutdowns
390             for (Future<Void> future: futures)
391             {
392                 try
393                 {
394                     if (!future.isDone())
395                         future.get(Math.max(1L,stop_by-System.currentTimeMillis()),TimeUnit.MILLISECONDS);
396                 }
397                 catch (Exception e)
398                 {
399                     mex.add(e.getCause());
400                 }
401             }
402         }
403
404         // Cancel any shutdowns not done
405         for (Future<Void> future: futures)
406             if (!future.isDone())
407                 future.cancel(true);
408
409         // Now stop the connectors (this will close existing connections)
410         for (Connector connector : _connectors)
411         {
412             try
413             {
414                 connector.stop();
415             }
416             catch (Throwable e)
417             {
418                 mex.add(e);
419             }
420         }
421
422         // And finally stop everything else
423         try
424         {
425             super.doStop();
426         }
427         catch (Throwable e)
428         {
429             mex.add(e);
430         }
431
432         if (getStopAtShutdown())
433             ShutdownThread.deregister(this);
434
435         mex.ifExceptionThrow();
436
437     }
438
439     /* ------------------------------------------------------------ */
440     /* Handle a request from a connection.
441      * Called to handle a request on the connection when either the header has been received,
442      * or after the entire request has been received (for short requests of known length), or
443      * on the dispatch of an async request.
444      */
445     public void handle(HttpChannel<?> connection) throws IOException, ServletException
446     {
447         final String target=connection.getRequest().getPathInfo();
448         final Request request=connection.getRequest();
449         final Response response=connection.getResponse();
450
451         if (LOG.isDebugEnabled())
452             LOG.debug(request.getDispatcherType()+" "+target+" on "+connection);
453
454         if ("*".equals(target))
455         {
456             handleOptions(request,response);
457             if (!request.isHandled())
458                 handle(target, request, request, response);
459         }
460         else
461             handle(target, request, request, response);
462
463         if (LOG.isDebugEnabled())
464             LOG.debug("RESPONSE "+target+"  "+connection.getResponse().getStatus()+" handled="+request.isHandled());
465     }
466
467     /* ------------------------------------------------------------ */
468     /* Handle Options request to server
469      */
470     protected void handleOptions(Request request,Response response) throws IOException
471     {
472         if (!HttpMethod.OPTIONS.is(request.getMethod()))
473             response.sendError(HttpStatus.BAD_REQUEST_400);
474         request.setHandled(true);
475         response.setStatus(200);
476         response.getHttpFields().put(HttpHeader.ALLOW,"GET,POST,HEAD,OPTIONS");
477         response.setContentLength(0);
478         response.closeOutput();
479     }
480
481     /* ------------------------------------------------------------ */
482     /* Handle a request from a connection.
483      * Called to handle a request on the connection when either the header has been received,
484      * or after the entire request has been received (for short requests of known length), or
485      * on the dispatch of an async request.
486      */
487     public void handleAsync(HttpChannel<?> connection) throws IOException, ServletException
488     {
489         final HttpChannelState state = connection.getRequest().getHttpChannelState();
490         final AsyncContextEvent event = state.getAsyncContextEvent();
491
492         final Request baseRequest=connection.getRequest();
493         final String path=event.getPath();
494
495         if (path!=null)
496         {
497             // this is a dispatch with a path
498             ServletContext context=event.getServletContext();
499             HttpURI uri = new HttpURI(context==null?path:URIUtil.addPaths(context.getContextPath(),path));
500             baseRequest.setUri(uri);
501             baseRequest.setRequestURI(null);
502             baseRequest.setPathInfo(baseRequest.getRequestURI());
503             if (uri.getQuery()!=null)
504                 baseRequest.mergeQueryParameters(uri.getQuery(), true); //we have to assume dispatch path and query are UTF8
505         }
506
507         final String target=baseRequest.getPathInfo();
508         final HttpServletRequest request=(HttpServletRequest)event.getSuppliedRequest();
509         final HttpServletResponse response=(HttpServletResponse)event.getSuppliedResponse();
510
511         if (LOG.isDebugEnabled())
512         {
513             LOG.debug(request.getDispatcherType()+" "+target+" on "+connection);
514             handle(target, baseRequest, request, response);
515             LOG.debug("RESPONSE "+target+"  "+connection.getResponse().getStatus());
516         }
517         else
518             handle(target, baseRequest, request, response);
519
520     }
521
522     /* ------------------------------------------------------------ */
523     public void join() throws InterruptedException
524     {
525         getThreadPool().join();
526     }
527
528     /* ------------------------------------------------------------ */
529     /**
530      * @return Returns the sessionIdManager.
531      */
532     public SessionIdManager getSessionIdManager()
533     {
534         return _sessionIdManager;
535     }
536
537     /* ------------------------------------------------------------ */
538     /**
539      * @param sessionIdManager The sessionIdManager to set.
540      */
541     public void setSessionIdManager(SessionIdManager sessionIdManager)
542     {
543         updateBean(_sessionIdManager,sessionIdManager);
544         _sessionIdManager=sessionIdManager;
545     }
546
547     /* ------------------------------------------------------------ */
548     /*
549      * @see org.eclipse.util.AttributesMap#clearAttributes()
550      */
551     @Override
552     public void clearAttributes()
553     {
554         Enumeration<String> names = _attributes.getAttributeNames();
555         while (names.hasMoreElements())
556             removeBean(_attributes.getAttribute(names.nextElement()));
557         _attributes.clearAttributes();
558     }
559
560     /* ------------------------------------------------------------ */
561     /*
562      * @see org.eclipse.util.AttributesMap#getAttribute(java.lang.String)
563      */
564     @Override
565     public Object getAttribute(String name)
566     {
567         return _attributes.getAttribute(name);
568     }
569
570     /* ------------------------------------------------------------ */
571     /*
572      * @see org.eclipse.util.AttributesMap#getAttributeNames()
573      */
574     @Override
575     public Enumeration<String> getAttributeNames()
576     {
577         return AttributesMap.getAttributeNamesCopy(_attributes);
578     }
579
580     /* ------------------------------------------------------------ */
581     /*
582      * @see org.eclipse.util.AttributesMap#removeAttribute(java.lang.String)
583      */
584     @Override
585     public void removeAttribute(String name)
586     {
587         Object bean=_attributes.getAttribute(name);
588         if (bean!=null)
589             removeBean(bean);
590         _attributes.removeAttribute(name);
591     }
592
593     /* ------------------------------------------------------------ */
594     /*
595      * @see org.eclipse.util.AttributesMap#setAttribute(java.lang.String, java.lang.Object)
596      */
597     @Override
598     public void setAttribute(String name, Object attribute)
599     {
600         addBean(attribute);
601         _attributes.setAttribute(name, attribute);
602     }
603
604     /* ------------------------------------------------------------ */
605     /**
606      * @return The URI of the first {@link NetworkConnector} and first {@link ContextHandler}, or null
607      */
608     public URI getURI()
609     {
610         NetworkConnector connector=null;
611         for (Connector c: _connectors)
612         {
613             if (c instanceof NetworkConnector)
614             {
615                 connector=(NetworkConnector)c;
616                 break;
617             }
618         }
619
620         if (connector==null)
621             return null;
622
623         ContextHandler context = getChildHandlerByClass(ContextHandler.class);
624
625         try
626         {
627             String scheme=connector.getDefaultConnectionFactory().getProtocol().startsWith("SSL-")?"https":"http";
628
629             String host=connector.getHost();
630             if (context!=null && context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
631                 host=context.getVirtualHosts()[0];
632             if (host==null)
633                 host=InetAddress.getLocalHost().getHostAddress();
634
635             String path=context==null?null:context.getContextPath();
636             if (path==null)
637                 path="/";
638             return new URI(scheme,null,host,connector.getLocalPort(),path,null,null);
639         }
640         catch(Exception e)
641         {
642             LOG.warn(e);
643             return null;
644         }
645     }
646
647     /* ------------------------------------------------------------ */
648     @Override
649     public String toString()
650     {
651         return this.getClass().getName()+"@"+Integer.toHexString(hashCode());
652     }
653
654     @Override
655     public void dump(Appendable out,String indent) throws IOException
656     {
657         dumpBeans(out,indent,Collections.singleton(new ClassLoaderDump(this.getClass().getClassLoader())));
658     }
659
660     /* ------------------------------------------------------------ */
661     public static void main(String...args) throws Exception
662     {
663         System.err.println(getVersion());
664     }
665
666     /* ------------------------------------------------------------ */
667     /* ------------------------------------------------------------ */
668     private static class DateField
669     {
670         final long _seconds;
671         final HttpField _dateField;
672         public DateField(long seconds, HttpField dateField)
673         {
674             super();
675             _seconds = seconds;
676             _dateField = dateField;
677         }
678         
679     }
680 }