1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.server;
20
21 import java.io.IOException;
22 import java.net.InetAddress;
23 import java.net.InetSocketAddress;
24 import java.net.URI;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collections;
28 import java.util.Date;
29 import java.util.Enumeration;
30 import java.util.List;
31 import java.util.concurrent.CopyOnWriteArrayList;
32 import java.util.concurrent.Future;
33 import java.util.concurrent.TimeUnit;
34
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.http.PreEncodedHttpField;
48 import org.eclipse.jetty.server.handler.ContextHandler;
49 import org.eclipse.jetty.server.handler.HandlerWrapper;
50 import org.eclipse.jetty.server.handler.StatisticsHandler;
51 import org.eclipse.jetty.util.Attributes;
52 import org.eclipse.jetty.util.AttributesMap;
53 import org.eclipse.jetty.util.Jetty;
54 import org.eclipse.jetty.util.MultiException;
55 import org.eclipse.jetty.util.URIUtil;
56 import org.eclipse.jetty.util.Uptime;
57 import org.eclipse.jetty.util.annotation.ManagedAttribute;
58 import org.eclipse.jetty.util.annotation.ManagedObject;
59 import org.eclipse.jetty.util.annotation.Name;
60 import org.eclipse.jetty.util.component.Graceful;
61 import org.eclipse.jetty.util.component.LifeCycle;
62 import org.eclipse.jetty.util.log.Log;
63 import org.eclipse.jetty.util.log.Logger;
64 import org.eclipse.jetty.util.thread.Locker;
65 import org.eclipse.jetty.util.thread.QueuedThreadPool;
66 import org.eclipse.jetty.util.thread.ShutdownThread;
67 import org.eclipse.jetty.util.thread.ThreadPool;
68 import org.eclipse.jetty.util.thread.ThreadPool.SizedThreadPool;
69
70
71
72
73
74
75
76
77 @ManagedObject(value="Jetty HTTP Servlet server")
78 public class Server extends HandlerWrapper implements Attributes
79 {
80 private static final Logger LOG = Log.getLogger(Server.class);
81
82 private final AttributesMap _attributes = new AttributesMap();
83 private final ThreadPool _threadPool;
84 private final List<Connector> _connectors = new CopyOnWriteArrayList<>();
85 private SessionIdManager _sessionIdManager;
86 private boolean _stopAtShutdown;
87 private boolean _dumpAfterStart=false;
88 private boolean _dumpBeforeStop=false;
89 private RequestLog _requestLog;
90
91 private final Locker _dateLocker = new Locker();
92 private volatile DateField _dateField;
93
94
95
96 public Server()
97 {
98 this((ThreadPool)null);
99 }
100
101
102
103
104
105
106
107 public Server(@Name("port")int port)
108 {
109 this((ThreadPool)null);
110 ServerConnector connector=new ServerConnector(this);
111 connector.setPort(port);
112 setConnectors(new Connector[]{connector});
113 }
114
115
116
117
118
119
120
121
122 public Server(@Name("address")InetSocketAddress addr)
123 {
124 this((ThreadPool)null);
125 ServerConnector connector=new ServerConnector(this);
126 connector.setHost(addr.getHostName());
127 connector.setPort(addr.getPort());
128 setConnectors(new Connector[]{connector});
129 }
130
131
132 public Server(@Name("threadpool") ThreadPool pool)
133 {
134 _threadPool=pool!=null?pool:new QueuedThreadPool();
135 addBean(_threadPool);
136 setServer(this);
137 }
138
139
140 public RequestLog getRequestLog()
141 {
142 return _requestLog;
143 }
144
145
146 public void setRequestLog(RequestLog requestLog)
147 {
148 updateBean(_requestLog,requestLog);
149 _requestLog = requestLog;
150 }
151
152
153 @ManagedAttribute("version of this server")
154 public static String getVersion()
155 {
156 return Jetty.VERSION;
157 }
158
159
160 public boolean getStopAtShutdown()
161 {
162 return _stopAtShutdown;
163 }
164
165
166
167
168
169
170
171
172
173 @Override
174 public void setStopTimeout(long stopTimeout)
175 {
176 super.setStopTimeout(stopTimeout);
177 }
178
179
180
181
182
183
184
185
186 public void setStopAtShutdown(boolean stop)
187 {
188
189 if (stop)
190 {
191
192 if (!_stopAtShutdown)
193 {
194
195 if (isStarted())
196 ShutdownThread.register(this);
197 }
198 }
199 else
200 ShutdownThread.deregister(this);
201
202 _stopAtShutdown=stop;
203 }
204
205
206
207
208
209 @ManagedAttribute(value="connectors for this server", readonly=true)
210 public Connector[] getConnectors()
211 {
212 List<Connector> connectors = new ArrayList<>(_connectors);
213 return connectors.toArray(new Connector[connectors.size()]);
214 }
215
216
217 public void addConnector(Connector connector)
218 {
219 if (connector.getServer() != this)
220 throw new IllegalArgumentException("Connector " + connector +
221 " cannot be shared among server " + connector.getServer() + " and server " + this);
222 if (_connectors.add(connector))
223 addBean(connector);
224 }
225
226
227
228
229
230
231
232 public void removeConnector(Connector connector)
233 {
234 if (_connectors.remove(connector))
235 removeBean(connector);
236 }
237
238
239
240
241
242
243 public void setConnectors(Connector[] connectors)
244 {
245 if (connectors != null)
246 {
247 for (Connector connector : connectors)
248 {
249 if (connector.getServer() != this)
250 throw new IllegalArgumentException("Connector " + connector +
251 " cannot be shared among server " + connector.getServer() + " and server " + this);
252 }
253 }
254
255 Connector[] oldConnectors = getConnectors();
256 updateBeans(oldConnectors, connectors);
257 _connectors.removeAll(Arrays.asList(oldConnectors));
258 if (connectors != null)
259 _connectors.addAll(Arrays.asList(connectors));
260 }
261
262
263
264
265
266 @ManagedAttribute("the server thread pool")
267 public ThreadPool getThreadPool()
268 {
269 return _threadPool;
270 }
271
272
273
274
275 @ManagedAttribute("dump state to stderr after start")
276 public boolean isDumpAfterStart()
277 {
278 return _dumpAfterStart;
279 }
280
281
282
283
284 public void setDumpAfterStart(boolean dumpAfterStart)
285 {
286 _dumpAfterStart = dumpAfterStart;
287 }
288
289
290
291
292 @ManagedAttribute("dump state to stderr before stop")
293 public boolean isDumpBeforeStop()
294 {
295 return _dumpBeforeStop;
296 }
297
298
299
300
301 public void setDumpBeforeStop(boolean dumpBeforeStop)
302 {
303 _dumpBeforeStop = dumpBeforeStop;
304 }
305
306
307 public HttpField getDateField()
308 {
309 long now=System.currentTimeMillis();
310 long seconds = now/1000;
311 DateField df = _dateField;
312
313 if (df==null || df._seconds!=seconds)
314 {
315 try(Locker.Lock lock = _dateLocker.lock())
316 {
317 df = _dateField;
318 if (df==null || df._seconds!=seconds)
319 {
320 HttpField field=new PreEncodedHttpField(HttpHeader.DATE,DateGenerator.formatDate(now));
321 _dateField=new DateField(seconds,field);
322 return field;
323 }
324 }
325 }
326 return df._dateField;
327 }
328
329
330 @Override
331 protected void doStart() throws Exception
332 {
333
334
335 if (getStopAtShutdown())
336 ShutdownThread.register(this);
337
338
339
340 ShutdownMonitor.register(this);
341
342
343 ShutdownMonitor.getInstance().start();
344
345 LOG.info("jetty-" + getVersion());
346 if (!Jetty.STABLE)
347 {
348 LOG.warn("THIS IS NOT A STABLE RELEASE! DO NOT USE IN PRODUCTION!");
349 LOG.warn("Download a stable release from http://download.eclipse.org/jetty/");
350 }
351
352 HttpGenerator.setJettyVersion(HttpConfiguration.SERVER_VERSION);
353 MultiException mex=new MultiException();
354
355
356 SizedThreadPool pool = getBean(SizedThreadPool.class);
357 int max=pool==null?-1:pool.getMaxThreads();
358 int selectors=0;
359 int acceptors=0;
360 if (mex.size()==0)
361 {
362 for (Connector connector : _connectors)
363 {
364 if (connector instanceof AbstractConnector)
365 acceptors+=((AbstractConnector)connector).getAcceptors();
366
367 if (connector instanceof ServerConnector)
368 selectors+=((ServerConnector)connector).getSelectorManager().getSelectorCount();
369 }
370 }
371
372 int needed=1+selectors+acceptors;
373 if (max>0 && needed>max)
374 throw new IllegalStateException(String.format("Insufficient threads: max=%d < needed(acceptors=%d + selectors=%d + request=1)",max,acceptors,selectors));
375
376 try
377 {
378 super.doStart();
379 }
380 catch(Throwable e)
381 {
382 mex.add(e);
383 }
384
385
386 for (Connector connector : _connectors)
387 {
388 try
389 {
390 connector.start();
391 }
392 catch(Throwable e)
393 {
394 mex.add(e);
395 }
396 }
397
398 if (isDumpAfterStart())
399 dumpStdErr();
400
401 mex.ifExceptionThrow();
402
403 LOG.info(String.format("Started @%dms",Uptime.getUptime()));
404 }
405
406 @Override
407 protected void start(LifeCycle l) throws Exception
408 {
409
410 if (!(l instanceof Connector))
411 super.start(l);
412 }
413
414
415 @Override
416 protected void doStop() throws Exception
417 {
418 if (isDumpBeforeStop())
419 dumpStdErr();
420
421 if (LOG.isDebugEnabled())
422 LOG.debug("doStop {}",this);
423
424 MultiException mex=new MultiException();
425
426
427 List<Future<Void>> futures = new ArrayList<>();
428
429
430 for (Connector connector : _connectors)
431 futures.add(connector.shutdown());
432
433
434 Handler[] gracefuls = getChildHandlersByClass(Graceful.class);
435 for (Handler graceful : gracefuls)
436 futures.add(((Graceful)graceful).shutdown());
437
438
439 long stopTimeout = getStopTimeout();
440 if (stopTimeout>0)
441 {
442 long stop_by=System.currentTimeMillis()+stopTimeout;
443 if (LOG.isDebugEnabled())
444 LOG.debug("Graceful shutdown {} by ",this,new Date(stop_by));
445
446
447 for (Future<Void> future: futures)
448 {
449 try
450 {
451 if (!future.isDone())
452 future.get(Math.max(1L,stop_by-System.currentTimeMillis()),TimeUnit.MILLISECONDS);
453 }
454 catch (Exception e)
455 {
456 mex.add(e);
457 }
458 }
459 }
460
461
462 for (Future<Void> future: futures)
463 if (!future.isDone())
464 future.cancel(true);
465
466
467 for (Connector connector : _connectors)
468 {
469 try
470 {
471 connector.stop();
472 }
473 catch (Throwable e)
474 {
475 mex.add(e);
476 }
477 }
478
479
480 try
481 {
482 super.doStop();
483 }
484 catch (Throwable e)
485 {
486 mex.add(e);
487 }
488
489 if (getStopAtShutdown())
490 ShutdownThread.deregister(this);
491
492
493
494 ShutdownMonitor.deregister(this);
495
496 mex.ifExceptionThrow();
497 }
498
499
500
501
502
503
504
505 public void handle(HttpChannel connection) throws IOException, ServletException
506 {
507 final String target=connection.getRequest().getPathInfo();
508 final Request request=connection.getRequest();
509 final Response response=connection.getResponse();
510
511 if (LOG.isDebugEnabled())
512 LOG.debug("{} on {}{}{} {} {}{}{}", request.getDispatcherType(), connection, System.lineSeparator(),
513 request.getMethod(), target, request.getProtocol(), System.lineSeparator(), request.getHttpFields());
514
515 if (HttpMethod.OPTIONS.is(request.getMethod()) || "*".equals(target))
516 {
517 if (!HttpMethod.OPTIONS.is(request.getMethod()))
518 response.sendError(HttpStatus.BAD_REQUEST_400);
519 handleOptions(request,response);
520 if (!request.isHandled())
521 handle(target, request, request, response);
522 }
523 else
524 handle(target, request, request, response);
525
526 if (LOG.isDebugEnabled())
527 LOG.debug("RESPONSE for {} h={}{}{} {}{}{}", target, request.isHandled(), System.lineSeparator(),
528 response.getStatus(), response.getReason(), System.lineSeparator(), response.getHttpFields());
529 }
530
531
532
533
534 protected void handleOptions(Request request,Response response) throws IOException
535 {
536 }
537
538
539
540
541
542
543
544 public void handleAsync(HttpChannel connection) throws IOException, ServletException
545 {
546 final HttpChannelState state = connection.getRequest().getHttpChannelState();
547 final AsyncContextEvent event = state.getAsyncContextEvent();
548
549 final Request baseRequest=connection.getRequest();
550 final String path=event.getPath();
551
552 if (path!=null)
553 {
554
555 ServletContext context=event.getServletContext();
556 String query=baseRequest.getQueryString();
557 baseRequest.setURIPathQuery(URIUtil.addPaths(context==null?null:context.getContextPath(), path));
558 HttpURI uri = baseRequest.getHttpURI();
559 baseRequest.setPathInfo(uri.getDecodedPath());
560 if (uri.getQuery()!=null)
561 baseRequest.mergeQueryParameters(query,uri.getQuery(), true);
562 }
563
564 final String target=baseRequest.getPathInfo();
565 final HttpServletRequest request=(HttpServletRequest)event.getSuppliedRequest();
566 final HttpServletResponse response=(HttpServletResponse)event.getSuppliedResponse();
567
568 if (LOG.isDebugEnabled())
569 {
570 LOG.debug(request.getDispatcherType()+" "+request.getMethod()+" "+target+" on "+connection);
571 handle(target, baseRequest, request, response);
572 LOG.debug("RESPONSE "+target+" "+connection.getResponse().getStatus());
573 }
574 else
575 handle(target, baseRequest, request, response);
576
577 }
578
579
580 public void join() throws InterruptedException
581 {
582 getThreadPool().join();
583 }
584
585
586
587
588
589 public SessionIdManager getSessionIdManager()
590 {
591 return _sessionIdManager;
592 }
593
594
595
596
597
598 public void setSessionIdManager(SessionIdManager sessionIdManager)
599 {
600 updateBean(_sessionIdManager,sessionIdManager);
601 _sessionIdManager=sessionIdManager;
602 }
603
604
605
606
607
608 @Override
609 public void clearAttributes()
610 {
611 Enumeration<String> names = _attributes.getAttributeNames();
612 while (names.hasMoreElements())
613 removeBean(_attributes.getAttribute(names.nextElement()));
614 _attributes.clearAttributes();
615 }
616
617
618
619
620
621 @Override
622 public Object getAttribute(String name)
623 {
624 return _attributes.getAttribute(name);
625 }
626
627
628
629
630
631 @Override
632 public Enumeration<String> getAttributeNames()
633 {
634 return AttributesMap.getAttributeNamesCopy(_attributes);
635 }
636
637
638
639
640
641 @Override
642 public void removeAttribute(String name)
643 {
644 Object bean=_attributes.getAttribute(name);
645 if (bean!=null)
646 removeBean(bean);
647 _attributes.removeAttribute(name);
648 }
649
650
651
652
653
654 @Override
655 public void setAttribute(String name, Object attribute)
656 {
657 addBean(attribute);
658 _attributes.setAttribute(name, attribute);
659 }
660
661
662
663
664
665 public URI getURI()
666 {
667 NetworkConnector connector=null;
668 for (Connector c: _connectors)
669 {
670 if (c instanceof NetworkConnector)
671 {
672 connector=(NetworkConnector)c;
673 break;
674 }
675 }
676
677 if (connector==null)
678 return null;
679
680 ContextHandler context = getChildHandlerByClass(ContextHandler.class);
681
682 try
683 {
684 String protocol = connector.getDefaultConnectionFactory().getProtocol();
685 String scheme="http";
686 if (protocol.startsWith("SSL-") || protocol.equals("SSL"))
687 scheme = "https";
688
689 String host=connector.getHost();
690 if (context!=null && context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
691 host=context.getVirtualHosts()[0];
692 if (host==null)
693 host=InetAddress.getLocalHost().getHostAddress();
694
695 String path=context==null?null:context.getContextPath();
696 if (path==null)
697 path="/";
698 return new URI(scheme,null,host,connector.getLocalPort(),path,null,null);
699 }
700 catch(Exception e)
701 {
702 LOG.warn(e);
703 return null;
704 }
705 }
706
707
708 @Override
709 public String toString()
710 {
711 return this.getClass().getName()+"@"+Integer.toHexString(hashCode());
712 }
713
714
715 @Override
716 public void dump(Appendable out,String indent) throws IOException
717 {
718 dumpBeans(out,indent,Collections.singleton(new ClassLoaderDump(this.getClass().getClassLoader())));
719 }
720
721
722 public static void main(String...args) throws Exception
723 {
724 System.err.println(getVersion());
725 }
726
727
728
729 private static class DateField
730 {
731 final long _seconds;
732 final HttpField _dateField;
733 public DateField(long seconds, HttpField dateField)
734 {
735 super();
736 _seconds = seconds;
737 _dateField = dateField;
738 }
739
740 }
741 }