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 HttpGenerator.setJettyVersion(HttpConfiguration.SERVER_VERSION);
347 MultiException mex=new MultiException();
348
349
350 SizedThreadPool pool = getBean(SizedThreadPool.class);
351 int max=pool==null?-1:pool.getMaxThreads();
352 int selectors=0;
353 int acceptors=0;
354 if (mex.size()==0)
355 {
356 for (Connector connector : _connectors)
357 {
358 if (connector instanceof AbstractConnector)
359 acceptors+=((AbstractConnector)connector).getAcceptors();
360
361 if (connector instanceof ServerConnector)
362 selectors+=((ServerConnector)connector).getSelectorManager().getSelectorCount();
363 }
364 }
365
366 int needed=1+selectors+acceptors;
367 if (max>0 && needed>max)
368 throw new IllegalStateException(String.format("Insufficient threads: max=%d < needed(acceptors=%d + selectors=%d + request=1)",max,acceptors,selectors));
369
370 try
371 {
372 super.doStart();
373 }
374 catch(Throwable e)
375 {
376 mex.add(e);
377 }
378
379
380 for (Connector connector : _connectors)
381 {
382 try
383 {
384 connector.start();
385 }
386 catch(Throwable e)
387 {
388 mex.add(e);
389 }
390 }
391
392 if (isDumpAfterStart())
393 dumpStdErr();
394
395 mex.ifExceptionThrow();
396
397 LOG.info(String.format("Started @%dms",Uptime.getUptime()));
398 }
399
400 @Override
401 protected void start(LifeCycle l) throws Exception
402 {
403
404 if (!(l instanceof Connector))
405 super.start(l);
406 }
407
408
409 @Override
410 protected void doStop() throws Exception
411 {
412 if (isDumpBeforeStop())
413 dumpStdErr();
414
415 if (LOG.isDebugEnabled())
416 LOG.debug("doStop {}",this);
417
418 MultiException mex=new MultiException();
419
420
421 List<Future<Void>> futures = new ArrayList<>();
422
423
424 for (Connector connector : _connectors)
425 futures.add(connector.shutdown());
426
427
428 Handler[] gracefuls = getChildHandlersByClass(Graceful.class);
429 for (Handler graceful : gracefuls)
430 futures.add(((Graceful)graceful).shutdown());
431
432
433 long stopTimeout = getStopTimeout();
434 if (stopTimeout>0)
435 {
436 long stop_by=System.currentTimeMillis()+stopTimeout;
437 if (LOG.isDebugEnabled())
438 LOG.debug("Graceful shutdown {} by ",this,new Date(stop_by));
439
440
441 for (Future<Void> future: futures)
442 {
443 try
444 {
445 if (!future.isDone())
446 future.get(Math.max(1L,stop_by-System.currentTimeMillis()),TimeUnit.MILLISECONDS);
447 }
448 catch (Exception e)
449 {
450 mex.add(e);
451 }
452 }
453 }
454
455
456 for (Future<Void> future: futures)
457 if (!future.isDone())
458 future.cancel(true);
459
460
461 for (Connector connector : _connectors)
462 {
463 try
464 {
465 connector.stop();
466 }
467 catch (Throwable e)
468 {
469 mex.add(e);
470 }
471 }
472
473
474 try
475 {
476 super.doStop();
477 }
478 catch (Throwable e)
479 {
480 mex.add(e);
481 }
482
483 if (getStopAtShutdown())
484 ShutdownThread.deregister(this);
485
486
487
488 ShutdownMonitor.deregister(this);
489
490 mex.ifExceptionThrow();
491 }
492
493
494
495
496
497
498
499 public void handle(HttpChannel connection) throws IOException, ServletException
500 {
501 final String target=connection.getRequest().getPathInfo();
502 final Request request=connection.getRequest();
503 final Response response=connection.getResponse();
504
505 if (LOG.isDebugEnabled())
506 LOG.debug("{} on {}{}",request.getDispatcherType(),connection,"\n"+request.getMethod()+" "+request.getHttpURI()+"\n"+request.getHttpFields());
507
508 if (HttpMethod.OPTIONS.is(request.getMethod()) || "*".equals(target))
509 {
510 if (!HttpMethod.OPTIONS.is(request.getMethod()))
511 response.sendError(HttpStatus.BAD_REQUEST_400);
512 handleOptions(request,response);
513 if (!request.isHandled())
514 handle(target, request, request, response);
515 }
516 else
517 handle(target, request, request, response);
518
519 if (LOG.isDebugEnabled())
520 LOG.debug("RESPONSE for {} h={}{}",target,request.isHandled(),"\n"+response.getStatus()+" "+response.getReason()+"\n"+response.getHttpFields());
521 }
522
523
524
525
526 protected void handleOptions(Request request,Response response) throws IOException
527 {
528 }
529
530
531
532
533
534
535
536 public void handleAsync(HttpChannel connection) throws IOException, ServletException
537 {
538 final HttpChannelState state = connection.getRequest().getHttpChannelState();
539 final AsyncContextEvent event = state.getAsyncContextEvent();
540
541 final Request baseRequest=connection.getRequest();
542 final String path=event.getPath();
543
544 if (path!=null)
545 {
546
547 ServletContext context=event.getServletContext();
548 String query=baseRequest.getQueryString();
549 baseRequest.setURIPathQuery(URIUtil.addPaths(context==null?null:context.getContextPath(), path));
550 HttpURI uri = baseRequest.getHttpURI();
551 baseRequest.setPathInfo(uri.getDecodedPath());
552 if (uri.getQuery()!=null)
553 baseRequest.mergeQueryParameters(query,uri.getQuery(), true);
554 }
555
556 final String target=baseRequest.getPathInfo();
557 final HttpServletRequest request=(HttpServletRequest)event.getSuppliedRequest();
558 final HttpServletResponse response=(HttpServletResponse)event.getSuppliedResponse();
559
560 if (LOG.isDebugEnabled())
561 {
562 LOG.debug(request.getDispatcherType()+" "+request.getMethod()+" "+target+" on "+connection);
563 handle(target, baseRequest, request, response);
564 LOG.debug("RESPONSE "+target+" "+connection.getResponse().getStatus());
565 }
566 else
567 handle(target, baseRequest, request, response);
568
569 }
570
571
572 public void join() throws InterruptedException
573 {
574 getThreadPool().join();
575 }
576
577
578
579
580
581 public SessionIdManager getSessionIdManager()
582 {
583 return _sessionIdManager;
584 }
585
586
587
588
589
590 public void setSessionIdManager(SessionIdManager sessionIdManager)
591 {
592 updateBean(_sessionIdManager,sessionIdManager);
593 _sessionIdManager=sessionIdManager;
594 }
595
596
597
598
599
600 @Override
601 public void clearAttributes()
602 {
603 Enumeration<String> names = _attributes.getAttributeNames();
604 while (names.hasMoreElements())
605 removeBean(_attributes.getAttribute(names.nextElement()));
606 _attributes.clearAttributes();
607 }
608
609
610
611
612
613 @Override
614 public Object getAttribute(String name)
615 {
616 return _attributes.getAttribute(name);
617 }
618
619
620
621
622
623 @Override
624 public Enumeration<String> getAttributeNames()
625 {
626 return AttributesMap.getAttributeNamesCopy(_attributes);
627 }
628
629
630
631
632
633 @Override
634 public void removeAttribute(String name)
635 {
636 Object bean=_attributes.getAttribute(name);
637 if (bean!=null)
638 removeBean(bean);
639 _attributes.removeAttribute(name);
640 }
641
642
643
644
645
646 @Override
647 public void setAttribute(String name, Object attribute)
648 {
649 addBean(attribute);
650 _attributes.setAttribute(name, attribute);
651 }
652
653
654
655
656
657 @SuppressWarnings("resource")
658 public URI getURI()
659 {
660 NetworkConnector connector=null;
661 for (Connector c: _connectors)
662 {
663 if (c instanceof NetworkConnector)
664 {
665 connector=(NetworkConnector)c;
666 break;
667 }
668 }
669
670 if (connector==null)
671 return null;
672
673 ContextHandler context = getChildHandlerByClass(ContextHandler.class);
674
675 try
676 {
677 String scheme=connector.getDefaultConnectionFactory().getProtocol().startsWith("SSL-")?"https":"http";
678
679 String host=connector.getHost();
680 if (context!=null && context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
681 host=context.getVirtualHosts()[0];
682 if (host==null)
683 host=InetAddress.getLocalHost().getHostAddress();
684
685 String path=context==null?null:context.getContextPath();
686 if (path==null)
687 path="/";
688 return new URI(scheme,null,host,connector.getLocalPort(),path,null,null);
689 }
690 catch(Exception e)
691 {
692 LOG.warn(e);
693 return null;
694 }
695 }
696
697
698 @Override
699 public String toString()
700 {
701 return this.getClass().getName()+"@"+Integer.toHexString(hashCode());
702 }
703
704
705 @Override
706 public void dump(Appendable out,String indent) throws IOException
707 {
708 dumpBeans(out,indent,Collections.singleton(new ClassLoaderDump(this.getClass().getClassLoader())));
709 }
710
711
712 public static void main(String...args) throws Exception
713 {
714 System.err.println(getVersion());
715 }
716
717
718
719 private static class DateField
720 {
721 final long _seconds;
722 final HttpField _dateField;
723 public DateField(long seconds, HttpField dateField)
724 {
725 super();
726 _seconds = seconds;
727 _dateField = dateField;
728 }
729
730 }
731 }