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