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, System.lineSeparator(),
507 request.getMethod(), target, request.getProtocol(), System.lineSeparator(), request.getHttpFields());
508
509 if (HttpMethod.OPTIONS.is(request.getMethod()) || "*".equals(target))
510 {
511 if (!HttpMethod.OPTIONS.is(request.getMethod()))
512 response.sendError(HttpStatus.BAD_REQUEST_400);
513 handleOptions(request,response);
514 if (!request.isHandled())
515 handle(target, request, request, response);
516 }
517 else
518 handle(target, request, request, response);
519
520 if (LOG.isDebugEnabled())
521 LOG.debug("RESPONSE for {} h={}{}{} {}{}{}", target, request.isHandled(), System.lineSeparator(),
522 response.getStatus(), response.getReason(), System.lineSeparator(), response.getHttpFields());
523 }
524
525
526
527
528 protected void handleOptions(Request request,Response response) throws IOException
529 {
530 }
531
532
533
534
535
536
537
538 public void handleAsync(HttpChannel connection) throws IOException, ServletException
539 {
540 final HttpChannelState state = connection.getRequest().getHttpChannelState();
541 final AsyncContextEvent event = state.getAsyncContextEvent();
542
543 final Request baseRequest=connection.getRequest();
544 final String path=event.getPath();
545
546 if (path!=null)
547 {
548
549 ServletContext context=event.getServletContext();
550 String query=baseRequest.getQueryString();
551 baseRequest.setURIPathQuery(URIUtil.addPaths(context==null?null:context.getContextPath(), path));
552 HttpURI uri = baseRequest.getHttpURI();
553 baseRequest.setPathInfo(uri.getDecodedPath());
554 if (uri.getQuery()!=null)
555 baseRequest.mergeQueryParameters(query,uri.getQuery(), true);
556 }
557
558 final String target=baseRequest.getPathInfo();
559 final HttpServletRequest request=(HttpServletRequest)event.getSuppliedRequest();
560 final HttpServletResponse response=(HttpServletResponse)event.getSuppliedResponse();
561
562 if (LOG.isDebugEnabled())
563 {
564 LOG.debug(request.getDispatcherType()+" "+request.getMethod()+" "+target+" on "+connection);
565 handle(target, baseRequest, request, response);
566 LOG.debug("RESPONSE "+target+" "+connection.getResponse().getStatus());
567 }
568 else
569 handle(target, baseRequest, request, response);
570
571 }
572
573
574 public void join() throws InterruptedException
575 {
576 getThreadPool().join();
577 }
578
579
580
581
582
583 public SessionIdManager getSessionIdManager()
584 {
585 return _sessionIdManager;
586 }
587
588
589
590
591
592 public void setSessionIdManager(SessionIdManager sessionIdManager)
593 {
594 updateBean(_sessionIdManager,sessionIdManager);
595 _sessionIdManager=sessionIdManager;
596 }
597
598
599
600
601
602 @Override
603 public void clearAttributes()
604 {
605 Enumeration<String> names = _attributes.getAttributeNames();
606 while (names.hasMoreElements())
607 removeBean(_attributes.getAttribute(names.nextElement()));
608 _attributes.clearAttributes();
609 }
610
611
612
613
614
615 @Override
616 public Object getAttribute(String name)
617 {
618 return _attributes.getAttribute(name);
619 }
620
621
622
623
624
625 @Override
626 public Enumeration<String> getAttributeNames()
627 {
628 return AttributesMap.getAttributeNamesCopy(_attributes);
629 }
630
631
632
633
634
635 @Override
636 public void removeAttribute(String name)
637 {
638 Object bean=_attributes.getAttribute(name);
639 if (bean!=null)
640 removeBean(bean);
641 _attributes.removeAttribute(name);
642 }
643
644
645
646
647
648 @Override
649 public void setAttribute(String name, Object attribute)
650 {
651 addBean(attribute);
652 _attributes.setAttribute(name, attribute);
653 }
654
655
656
657
658
659 public URI getURI()
660 {
661 NetworkConnector connector=null;
662 for (Connector c: _connectors)
663 {
664 if (c instanceof NetworkConnector)
665 {
666 connector=(NetworkConnector)c;
667 break;
668 }
669 }
670
671 if (connector==null)
672 return null;
673
674 ContextHandler context = getChildHandlerByClass(ContextHandler.class);
675
676 try
677 {
678 String protocol = connector.getDefaultConnectionFactory().getProtocol();
679 String scheme="http";
680 if (protocol.startsWith("SSL-") || protocol.equals("SSL"))
681 scheme = "https";
682
683 String host=connector.getHost();
684 if (context!=null && context.getVirtualHosts()!=null && context.getVirtualHosts().length>0)
685 host=context.getVirtualHosts()[0];
686 if (host==null)
687 host=InetAddress.getLocalHost().getHostAddress();
688
689 String path=context==null?null:context.getContextPath();
690 if (path==null)
691 path="/";
692 return new URI(scheme,null,host,connector.getLocalPort(),path,null,null);
693 }
694 catch(Exception e)
695 {
696 LOG.warn(e);
697 return null;
698 }
699 }
700
701
702 @Override
703 public String toString()
704 {
705 return this.getClass().getName()+"@"+Integer.toHexString(hashCode());
706 }
707
708
709 @Override
710 public void dump(Appendable out,String indent) throws IOException
711 {
712 dumpBeans(out,indent,Collections.singleton(new ClassLoaderDump(this.getClass().getClassLoader())));
713 }
714
715
716 public static void main(String...args) throws Exception
717 {
718 System.err.println(getVersion());
719 }
720
721
722
723 private static class DateField
724 {
725 final long _seconds;
726 final HttpField _dateField;
727 public DateField(long seconds, HttpField dateField)
728 {
729 super();
730 _seconds = seconds;
731 _dateField = dateField;
732 }
733
734 }
735 }