View Javadoc

1   // ========================================================================
2   // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses.
12  // ========================================================================
13  
14  package org.eclipse.jetty.server;
15  
16  import java.io.IOException;
17  import java.net.InetAddress;
18  import java.net.Socket;
19  import java.net.UnknownHostException;
20  import java.util.concurrent.atomic.AtomicLong;
21  
22  import javax.servlet.ServletRequest;
23  
24  import org.eclipse.jetty.http.HttpBuffers;
25  import org.eclipse.jetty.http.HttpFields;
26  import org.eclipse.jetty.http.HttpHeaders;
27  import org.eclipse.jetty.http.HttpSchemes;
28  import org.eclipse.jetty.io.Connection;
29  import org.eclipse.jetty.io.EndPoint;
30  import org.eclipse.jetty.io.EofException;
31  import org.eclipse.jetty.util.component.AbstractLifeCycle;
32  import org.eclipse.jetty.util.component.AggregateLifeCycle;
33  import org.eclipse.jetty.util.component.Dumpable;
34  import org.eclipse.jetty.util.component.LifeCycle;
35  import org.eclipse.jetty.util.log.Log;
36  import org.eclipse.jetty.util.log.Logger;
37  import org.eclipse.jetty.util.statistic.CounterStatistic;
38  import org.eclipse.jetty.util.statistic.SampleStatistic;
39  import org.eclipse.jetty.util.thread.ThreadPool;
40  
41  /**
42   * Abstract Connector implementation. This abstract implementation of the Connector interface provides:
43   * <ul>
44   * <li>AbstractLifeCycle implementation</li>
45   * <li>Implementations for connector getters and setters</li>
46   * <li>Buffer management</li>
47   * <li>Socket configuration</li>
48   * <li>Base acceptor thread</li>
49   * <li>Optional reverse proxy headers checking</li>
50   * </ul>
51   * 
52   * 
53   */
54  public abstract class AbstractConnector extends HttpBuffers implements Connector, Dumpable
55  {
56      private static final Logger LOG = Log.getLogger(AbstractConnector.class);
57  
58      private String _name;
59  
60      private Server _server;
61      private ThreadPool _threadPool;
62      private String _host;
63      private int _port = 0;
64      private String _integralScheme = HttpSchemes.HTTPS;
65      private int _integralPort = 0;
66      private String _confidentialScheme = HttpSchemes.HTTPS;
67      private int _confidentialPort = 0;
68      private int _acceptQueueSize = 0;
69      private int _acceptors = 1;
70      private int _acceptorPriorityOffset = 0;
71      private boolean _useDNS;
72      private boolean _forwarded;
73      private String _hostHeader;
74  
75      private String _forwardedHostHeader = HttpHeaders.X_FORWARDED_HOST;
76      private String _forwardedServerHeader = HttpHeaders.X_FORWARDED_SERVER;
77      private String _forwardedForHeader = HttpHeaders.X_FORWARDED_FOR;
78      private String _forwardedProtoHeader = HttpHeaders.X_FORWARDED_PROTO;
79      private String _forwardedCipherSuiteHeader;
80      private String _forwardedSslSessionIdHeader;
81      private boolean _reuseAddress = true;
82  
83      protected int _maxIdleTime = 200000;
84      protected int _lowResourceMaxIdleTime = -1;
85      protected int _soLingerTime = -1;
86  
87      private transient Thread[] _acceptorThread;
88  
89      private final AtomicLong _statsStartedAt = new AtomicLong(-1L);
90  
91      /** connections to server */
92      private final CounterStatistic _connectionStats = new CounterStatistic();
93      /** requests per connection */
94      private final SampleStatistic _requestStats = new SampleStatistic();
95      /** duration of a connection */
96      private final SampleStatistic _connectionDurationStats = new SampleStatistic();
97  
98      /* ------------------------------------------------------------ */
99      /**
100      */
101     public AbstractConnector()
102     {
103     }
104 
105     /* ------------------------------------------------------------ */
106     /*
107      */
108     public Server getServer()
109     {
110         return _server;
111     }
112 
113     /* ------------------------------------------------------------ */
114     public void setServer(Server server)
115     {
116         _server = server;
117     }
118 
119     /* ------------------------------------------------------------ */
120     public ThreadPool getThreadPool()
121     {
122         return _threadPool;
123     }
124 
125     /* ------------------------------------------------------------ */
126     public void setThreadPool(ThreadPool pool)
127     {
128         _threadPool = pool;
129     }
130 
131     /* ------------------------------------------------------------ */
132     /**
133      */
134     public void setHost(String host)
135     {
136         _host = host;
137     }
138 
139     /* ------------------------------------------------------------ */
140     /*
141      */
142     public String getHost()
143     {
144         return _host;
145     }
146 
147     /* ------------------------------------------------------------ */
148     public void setPort(int port)
149     {
150         _port = port;
151     }
152 
153     /* ------------------------------------------------------------ */
154     public int getPort()
155     {
156         return _port;
157     }
158 
159     /* ------------------------------------------------------------ */
160     /**
161      * @return Returns the maxIdleTime.
162      */
163     public int getMaxIdleTime()
164     {
165         return _maxIdleTime;
166     }
167 
168     /* ------------------------------------------------------------ */
169     /**
170      * Set the maximum Idle time for a connection, which roughly translates to the {@link Socket#setSoTimeout(int)} call, although with NIO implementations
171      * other mechanisms may be used to implement the timeout. The max idle time is applied:
172      * <ul>
173      * <li>When waiting for a new request to be received on a connection</li>
174      * <li>When reading the headers and content of a request</li>
175      * <li>When writing the headers and content of a response</li>
176      * </ul>
177      * Jetty interprets this value as the maximum time between some progress being made on the connection. So if a single byte is read or written, then the
178      * timeout (if implemented by jetty) is reset. However, in many instances, the reading/writing is delegated to the JVM, and the semantic is more strictly
179      * enforced as the maximum time a single read/write operation can take. Note, that as Jetty supports writes of memory mapped file buffers, then a write may
180      * take many 10s of seconds for large content written to a slow device.
181      * <p>
182      * Previously, Jetty supported separate idle timeouts and IO operation timeouts, however the expense of changing the value of soTimeout was significant, so
183      * these timeouts were merged. With the advent of NIO, it may be possible to again differentiate these values (if there is demand).
184      * 
185      * @param maxIdleTime
186      *            The maxIdleTime to set.
187      */
188     public void setMaxIdleTime(int maxIdleTime)
189     {
190         _maxIdleTime = maxIdleTime;
191     }
192 
193     /* ------------------------------------------------------------ */
194     /**
195      * @return Returns the maxIdleTime when resources are low.
196      */
197     public int getLowResourcesMaxIdleTime()
198     {
199         return _lowResourceMaxIdleTime;
200     }
201 
202     /* ------------------------------------------------------------ */
203     /**
204      * @param maxIdleTime
205      *            The maxIdleTime to set when resources are low.
206      */
207     public void setLowResourcesMaxIdleTime(int maxIdleTime)
208     {
209         _lowResourceMaxIdleTime = maxIdleTime;
210     }
211 
212     /* ------------------------------------------------------------ */
213     /**
214      * @return Returns the maxIdleTime when resources are low.
215      * @deprecated
216      */
217     public final int getLowResourceMaxIdleTime()
218     {
219         return getLowResourcesMaxIdleTime();
220     }
221 
222     /* ------------------------------------------------------------ */
223     /**
224      * @param maxIdleTime
225      *            The maxIdleTime to set when resources are low.
226      * @deprecated
227      */
228     public final void setLowResourceMaxIdleTime(int maxIdleTime)
229     {
230         setLowResourcesMaxIdleTime(maxIdleTime);
231     }
232 
233     /* ------------------------------------------------------------ */
234     /**
235      * @return Returns the soLingerTime.
236      */
237     public int getSoLingerTime()
238     {
239         return _soLingerTime;
240     }
241 
242     /* ------------------------------------------------------------ */
243     /**
244      * @return Returns the acceptQueueSize.
245      */
246     public int getAcceptQueueSize()
247     {
248         return _acceptQueueSize;
249     }
250 
251     /* ------------------------------------------------------------ */
252     /**
253      * @param acceptQueueSize
254      *            The acceptQueueSize to set.
255      */
256     public void setAcceptQueueSize(int acceptQueueSize)
257     {
258         _acceptQueueSize = acceptQueueSize;
259     }
260 
261     /* ------------------------------------------------------------ */
262     /**
263      * @return Returns the number of acceptor threads.
264      */
265     public int getAcceptors()
266     {
267         return _acceptors;
268     }
269 
270     /* ------------------------------------------------------------ */
271     /**
272      * @param acceptors
273      *            The number of acceptor threads to set.
274      */
275     public void setAcceptors(int acceptors)
276     {
277         if (acceptors > 2 * Runtime.getRuntime().availableProcessors())
278             LOG.warn("Acceptors should be <=2*availableProcessors: " + this);
279         _acceptors = acceptors;
280     }
281 
282     /* ------------------------------------------------------------ */
283     /**
284      * @param soLingerTime
285      *            The soLingerTime to set or -1 to disable.
286      */
287     public void setSoLingerTime(int soLingerTime)
288     {
289         _soLingerTime = soLingerTime;
290     }
291 
292     /* ------------------------------------------------------------ */
293     @Override
294     protected void doStart() throws Exception
295     {
296         if (_server == null)
297             throw new IllegalStateException("No server");
298 
299         // open listener port
300         open();
301 
302         super.doStart();
303 
304         if (_threadPool == null)
305             _threadPool = _server.getThreadPool();
306         if (_threadPool != _server.getThreadPool() && (_threadPool instanceof LifeCycle))
307             ((LifeCycle)_threadPool).start();
308 
309         // Start selector thread
310         synchronized (this)
311         {
312             _acceptorThread = new Thread[getAcceptors()];
313 
314             for (int i = 0; i < _acceptorThread.length; i++)
315                 _threadPool.dispatch(new Acceptor(i));
316             if (_threadPool.isLowOnThreads())
317                 LOG.warn("insufficient threads configured for {}",this);
318         }
319 
320         LOG.info("Started {}",this);
321     }
322 
323     /* ------------------------------------------------------------ */
324     @Override
325     protected void doStop() throws Exception
326     {
327         try
328         {
329             close();
330         }
331         catch (IOException e)
332         {
333             LOG.warn(e);
334         }
335 
336         if (_threadPool != _server.getThreadPool() && _threadPool instanceof LifeCycle)
337             ((LifeCycle)_threadPool).stop();
338 
339         super.doStop();
340 
341         Thread[] acceptors = null;
342         synchronized (this)
343         {
344             acceptors = _acceptorThread;
345             _acceptorThread = null;
346         }
347         if (acceptors != null)
348         {
349             for (int i = 0; i < acceptors.length; i++)
350             {
351                 Thread thread = acceptors[i];
352                 if (thread != null)
353                     thread.interrupt();
354             }
355         }
356     }
357 
358     /* ------------------------------------------------------------ */
359     public void join() throws InterruptedException
360     {
361         Thread[] threads = _acceptorThread;
362         if (threads != null)
363             for (int i = 0; i < threads.length; i++)
364                 if (threads[i] != null)
365                     threads[i].join();
366     }
367 
368     /* ------------------------------------------------------------ */
369     protected void configure(Socket socket) throws IOException
370     {
371         try
372         {
373             socket.setTcpNoDelay(true);
374             if (_soLingerTime >= 0)
375                 socket.setSoLinger(true,_soLingerTime / 1000);
376             else
377                 socket.setSoLinger(false,0);
378         }
379         catch (Exception e)
380         {
381             LOG.ignore(e);
382         }
383     }
384 
385     /* ------------------------------------------------------------ */
386     public void customize(EndPoint endpoint, Request request) throws IOException
387     {
388         if (isForwarded())
389             checkForwardedHeaders(endpoint,request);
390     }
391 
392     /* ------------------------------------------------------------ */
393     protected void checkForwardedHeaders(EndPoint endpoint, Request request) throws IOException
394     {
395         HttpFields httpFields = request.getConnection().getRequestFields();
396 
397         // Do SSL first
398         if (getForwardedCipherSuiteHeader()!=null)
399         {
400             String cipher_suite=httpFields.getStringField(getForwardedCipherSuiteHeader());
401             if (cipher_suite!=null)
402                 request.setAttribute("javax.servlet.request.cipher_suite",cipher_suite);
403         }
404         if (getForwardedSslSessionIdHeader()!=null)
405         {
406             String ssl_session_id=httpFields.getStringField(getForwardedSslSessionIdHeader());
407             if(ssl_session_id!=null)
408             {
409                 request.setAttribute("javax.servlet.request.ssl_session_id", ssl_session_id);
410                 request.setScheme(HttpSchemes.HTTPS);
411             }
412         }
413         
414         // Retrieving headers from the request
415         String forwardedHost = getLeftMostFieldValue(httpFields,getForwardedHostHeader());
416         String forwardedServer = getLeftMostFieldValue(httpFields,getForwardedServerHeader());
417         String forwardedFor = getLeftMostFieldValue(httpFields,getForwardedForHeader());
418         String forwardedProto = getLeftMostFieldValue(httpFields,getForwardedProtoHeader());
419         
420         if (_hostHeader != null)
421         {
422             // Update host header
423             httpFields.put(HttpHeaders.HOST_BUFFER,_hostHeader);
424             request.setServerName(null);
425             request.setServerPort(-1);
426             request.getServerName();
427         }
428         else if (forwardedHost != null)
429         {
430             // Update host header
431             httpFields.put(HttpHeaders.HOST_BUFFER,forwardedHost);
432             request.setServerName(null);
433             request.setServerPort(-1);
434             request.getServerName();
435         }
436         else if (forwardedServer != null)
437         {
438             // Use provided server name
439             request.setServerName(forwardedServer);
440         }
441 
442         if (forwardedFor != null)
443         {
444             request.setRemoteAddr(forwardedFor);
445             InetAddress inetAddress = null;
446 
447             if (_useDNS)
448             {
449                 try
450                 {
451                     inetAddress = InetAddress.getByName(forwardedFor);
452                 }
453                 catch (UnknownHostException e)
454                 {
455                     LOG.ignore(e);
456                 }
457             }
458 
459             request.setRemoteHost(inetAddress == null?forwardedFor:inetAddress.getHostName());
460         }
461         
462         if (forwardedProto != null)
463         {
464             request.setScheme(forwardedProto);
465         }
466     }
467 
468     /* ------------------------------------------------------------ */
469     protected String getLeftMostFieldValue(HttpFields fields, String header)
470     {
471         if (header == null)
472             return null;
473 
474         String headerValue = fields.getStringField(header);
475 
476         if (headerValue == null)
477             return null;
478 
479         int commaIndex = headerValue.indexOf(',');
480 
481         if (commaIndex == -1)
482         {
483             // Single value
484             return headerValue;
485         }
486 
487         // The left-most value is the farthest downstream client
488         return headerValue.substring(0,commaIndex);
489     }
490 
491     /* ------------------------------------------------------------ */
492     public void persist(EndPoint endpoint) throws IOException
493     {
494     }
495 
496     /* ------------------------------------------------------------ */
497     /*
498      * @see org.eclipse.jetty.server.Connector#getConfidentialPort()
499      */
500     public int getConfidentialPort()
501     {
502         return _confidentialPort;
503     }
504 
505     /* ------------------------------------------------------------ */
506     /* ------------------------------------------------------------ */
507     /*
508      * @see org.eclipse.jetty.server.Connector#getConfidentialScheme()
509      */
510     public String getConfidentialScheme()
511     {
512         return _confidentialScheme;
513     }
514 
515     /* ------------------------------------------------------------ */
516     /*
517      * @see org.eclipse.jetty.server.Connector#isConfidential(org.eclipse.jetty.server .Request)
518      */
519     public boolean isIntegral(Request request)
520     {
521         return false;
522     }
523 
524     /* ------------------------------------------------------------ */
525     /*
526      * @see org.eclipse.jetty.server.Connector#getConfidentialPort()
527      */
528     public int getIntegralPort()
529     {
530         return _integralPort;
531     }
532 
533     /* ------------------------------------------------------------ */
534     /*
535      * @see org.eclipse.jetty.server.Connector#getIntegralScheme()
536      */
537     public String getIntegralScheme()
538     {
539         return _integralScheme;
540     }
541 
542     /* ------------------------------------------------------------ */
543     /*
544      * @see org.eclipse.jetty.server.Connector#isConfidential(org.eclipse.jetty.server.Request)
545      */
546     public boolean isConfidential(Request request)
547     {
548         return _forwarded && request.getScheme().equalsIgnoreCase(HttpSchemes.HTTPS);
549     }
550 
551     /* ------------------------------------------------------------ */
552     /**
553      * @param confidentialPort
554      *            The confidentialPort to set.
555      */
556     public void setConfidentialPort(int confidentialPort)
557     {
558         _confidentialPort = confidentialPort;
559     }
560 
561     /* ------------------------------------------------------------ */
562     /**
563      * @param confidentialScheme
564      *            The confidentialScheme to set.
565      */
566     public void setConfidentialScheme(String confidentialScheme)
567     {
568         _confidentialScheme = confidentialScheme;
569     }
570 
571     /* ------------------------------------------------------------ */
572     /**
573      * @param integralPort
574      *            The integralPort to set.
575      */
576     public void setIntegralPort(int integralPort)
577     {
578         _integralPort = integralPort;
579     }
580 
581     /* ------------------------------------------------------------ */
582     /**
583      * @param integralScheme
584      *            The integralScheme to set.
585      */
586     public void setIntegralScheme(String integralScheme)
587     {
588         _integralScheme = integralScheme;
589     }
590 
591     /* ------------------------------------------------------------ */
592     protected abstract void accept(int acceptorID) throws IOException, InterruptedException;
593 
594     /* ------------------------------------------------------------ */
595     public void stopAccept(int acceptorID) throws Exception
596     {
597     }
598 
599     /* ------------------------------------------------------------ */
600     public boolean getResolveNames()
601     {
602         return _useDNS;
603     }
604 
605     /* ------------------------------------------------------------ */
606     public void setResolveNames(boolean resolve)
607     {
608         _useDNS = resolve;
609     }
610 
611     /* ------------------------------------------------------------ */
612     /**
613      * Is reverse proxy handling on?
614      * 
615      * @return true if this connector is checking the x-forwarded-for/host/server headers
616      */
617     public boolean isForwarded()
618     {
619         return _forwarded;
620     }
621 
622     /* ------------------------------------------------------------ */
623     /**
624      * Set reverse proxy handling. If set to true, then the X-Forwarded headers (or the headers set in their place) are looked for to set the request protocol,
625      * host, server and client ip.
626      * 
627      * @param check
628      *            true if this connector is checking the x-forwarded-for/host/server headers
629      * @set {@link #setForwardedForHeader(String)}
630      * @set {@link #setForwardedHostHeader(String)}
631      * @set {@link #setForwardedProtoHeader(String)}
632      * @set {@link #setForwardedServerHeader(String)}
633      */
634     public void setForwarded(boolean check)
635     {
636         if (check)
637             LOG.debug("{} is forwarded",this);
638         _forwarded = check;
639     }
640 
641     /* ------------------------------------------------------------ */
642     public String getHostHeader()
643     {
644         return _hostHeader;
645     }
646 
647     /* ------------------------------------------------------------ */
648     /**
649      * Set a forced valued for the host header to control what is returned by {@link ServletRequest#getServerName()} and {@link ServletRequest#getServerPort()}.
650      * This value is only used if {@link #isForwarded()} is true.
651      * 
652      * @param hostHeader
653      *            The value of the host header to force.
654      */
655     public void setHostHeader(String hostHeader)
656     {
657         _hostHeader = hostHeader;
658     }
659 
660     /* ------------------------------------------------------------ */
661     /*
662      * 
663      * @see #setForwarded(boolean)
664      */
665     public String getForwardedHostHeader()
666     {
667         return _forwardedHostHeader;
668     }
669 
670     /* ------------------------------------------------------------ */
671     /**
672      * @param forwardedHostHeader
673      *            The header name for forwarded hosts (default x-forwarded-host)
674      * @see #setForwarded(boolean)
675      */
676     public void setForwardedHostHeader(String forwardedHostHeader)
677     {
678         _forwardedHostHeader = forwardedHostHeader;
679     }
680 
681     /* ------------------------------------------------------------ */
682     /**
683      * @return the header name for forwarded server.
684      * @see #setForwarded(boolean)
685      */
686     public String getForwardedServerHeader()
687     {
688         return _forwardedServerHeader;
689     }
690 
691     /* ------------------------------------------------------------ */
692     /**
693      * @param forwardedServerHeader
694      *            The header name for forwarded server (default x-forwarded-server)
695      * @see #setForwarded(boolean)
696      */
697     public void setForwardedServerHeader(String forwardedServerHeader)
698     {
699         _forwardedServerHeader = forwardedServerHeader;
700     }
701 
702     /* ------------------------------------------------------------ */
703     /**
704      * @see #setForwarded(boolean)
705      */
706     public String getForwardedForHeader()
707     {
708         return _forwardedForHeader;
709     }
710 
711     /* ------------------------------------------------------------ */
712     /**
713      * @param forwardedRemoteAddressHeader
714      *            The header name for forwarded for (default x-forwarded-for)
715      * @see #setForwarded(boolean)
716      */
717     public void setForwardedForHeader(String forwardedRemoteAddressHeader)
718     {
719         _forwardedForHeader = forwardedRemoteAddressHeader;
720     }
721 
722     /* ------------------------------------------------------------ */
723     /**
724      * Get the forwardedProtoHeader.
725      * 
726      * @return the forwardedProtoHeader (default X-Forwarded-For)
727      * @see #setForwarded(boolean)
728      */
729     public String getForwardedProtoHeader()
730     {
731         return _forwardedProtoHeader;
732     }
733 
734     /* ------------------------------------------------------------ */
735     /**
736      * Set the forwardedProtoHeader.
737      * 
738      * @param forwardedProtoHeader
739      *            the forwardedProtoHeader to set (default X-Forwarded-For)
740      * @see #setForwarded(boolean)
741      */
742     public void setForwardedProtoHeader(String forwardedProtoHeader)
743     {
744         _forwardedProtoHeader = forwardedProtoHeader;
745     }
746 
747     /* ------------------------------------------------------------ */
748     /**
749      * @return The header name holding a forwarded cipher suite (default null)
750      */
751     public String getForwardedCipherSuiteHeader()
752     {
753         return _forwardedCipherSuiteHeader;
754     }
755 
756     /* ------------------------------------------------------------ */
757     /**
758      * @param forwardedCipherSuite
759      *            The header name holding a forwarded cipher suite (default null)
760      */
761     public void setForwardedCipherSuiteHeader(String forwardedCipherSuite)
762     {
763         _forwardedCipherSuiteHeader = forwardedCipherSuite;
764     }
765 
766     /* ------------------------------------------------------------ */
767     /**
768      * @return The header name holding a forwarded SSL Session ID (default null)
769      */
770     public String getForwardedSslSessionIdHeader()
771     {
772         return _forwardedSslSessionIdHeader;
773     }
774 
775     /* ------------------------------------------------------------ */
776     /**
777      * @param forwardedSslSessionId
778      *            The header name holding a forwarded SSL Session ID (default null)
779      */
780     public void setForwardedSslSessionIdHeader(String forwardedSslSessionId)
781     {
782         _forwardedSslSessionIdHeader = forwardedSslSessionId;
783     }
784 
785     /* ------------------------------------------------------------ */
786     @Override
787     public String toString()
788     {
789         String name = this.getClass().getName();
790         int dot = name.lastIndexOf('.');
791         if (dot > 0)
792             name = name.substring(dot + 1);
793 
794         return name + "@" + (getHost() == null?"0.0.0.0":getHost()) + ":" + (getLocalPort() <= 0?getPort():getLocalPort()) + " "
795                 + AbstractLifeCycle.getState(this);
796     }
797 
798     /* ------------------------------------------------------------ */
799     /* ------------------------------------------------------------ */
800     /* ------------------------------------------------------------ */
801     private class Acceptor implements Runnable
802     {
803         int _acceptor = 0;
804 
805         Acceptor(int id)
806         {
807             _acceptor = id;
808         }
809 
810         /* ------------------------------------------------------------ */
811         public void run()
812         {
813             Thread current = Thread.currentThread();
814             String name;
815             synchronized (AbstractConnector.this)
816             {
817                 if (_acceptorThread == null)
818                     return;
819 
820                 _acceptorThread[_acceptor] = current;
821                 name = _acceptorThread[_acceptor].getName();
822                 current.setName(name + " Acceptor" + _acceptor + " " + AbstractConnector.this);
823             }
824             int old_priority = current.getPriority();
825 
826             try
827             {
828                 current.setPriority(old_priority - _acceptorPriorityOffset);
829                 while (isRunning() && getConnection() != null)
830                 {
831                     try
832                     {
833                         accept(_acceptor);
834                     }
835                     catch (EofException e)
836                     {
837                         LOG.ignore(e);
838                     }
839                     catch (IOException e)
840                     {
841                         LOG.ignore(e);
842                     }
843                     catch (InterruptedException x)
844                     {
845                         // Connector has been stopped
846                         LOG.ignore(x);
847                     }
848                     catch (ThreadDeath e)
849                     {
850                         throw e;
851                     }
852                     catch (Throwable e)
853                     {
854                         LOG.warn(e);
855                     }
856                 }
857             }
858             finally
859             {
860                 current.setPriority(old_priority);
861                 current.setName(name);
862 
863                 synchronized (AbstractConnector.this)
864                 {
865                     if (_acceptorThread != null)
866                         _acceptorThread[_acceptor] = null;
867                 }
868             }
869         }
870     }
871 
872     /* ------------------------------------------------------------ */
873     public String getName()
874     {
875         if (_name == null)
876             _name = (getHost() == null?"0.0.0.0":getHost()) + ":" + (getLocalPort() <= 0?getPort():getLocalPort());
877         return _name;
878     }
879 
880     /* ------------------------------------------------------------ */
881     public void setName(String name)
882     {
883         _name = name;
884     }
885 
886     /* ------------------------------------------------------------ */
887     /**
888      * @return Get the number of requests handled by this connector since last call of statsReset(). If setStatsOn(false) then this is undefined.
889      */
890     public int getRequests()
891     {
892         return (int)_requestStats.getTotal();
893     }
894 
895     /* ------------------------------------------------------------ */
896     /**
897      * @return Returns the connectionsDurationTotal.
898      */
899     public long getConnectionsDurationTotal()
900     {
901         return _connectionDurationStats.getTotal();
902     }
903 
904     /* ------------------------------------------------------------ */
905     /**
906      * @return Number of connections accepted by the server since statsReset() called. Undefined if setStatsOn(false).
907      */
908     public int getConnections()
909     {
910         return (int)_connectionStats.getTotal();
911     }
912 
913     /* ------------------------------------------------------------ */
914     /**
915      * @return Number of connections currently open that were opened since statsReset() called. Undefined if setStatsOn(false).
916      */
917     public int getConnectionsOpen()
918     {
919         return (int)_connectionStats.getCurrent();
920     }
921 
922     /* ------------------------------------------------------------ */
923     /**
924      * @return Maximum number of connections opened simultaneously since statsReset() called. Undefined if setStatsOn(false).
925      */
926     public int getConnectionsOpenMax()
927     {
928         return (int)_connectionStats.getMax();
929     }
930 
931     /* ------------------------------------------------------------ */
932     /**
933      * @return Mean duration in milliseconds of open connections since statsReset() called. Undefined if setStatsOn(false).
934      */
935     public double getConnectionsDurationMean()
936     {
937         return _connectionDurationStats.getMean();
938     }
939 
940     /* ------------------------------------------------------------ */
941     /**
942      * @return Maximum duration in milliseconds of an open connection since statsReset() called. Undefined if setStatsOn(false).
943      */
944     public long getConnectionsDurationMax()
945     {
946         return _connectionDurationStats.getMax();
947     }
948 
949     /* ------------------------------------------------------------ */
950     /**
951      * @return Standard deviation of duration in milliseconds of open connections since statsReset() called. Undefined if setStatsOn(false).
952      */
953     public double getConnectionsDurationStdDev()
954     {
955         return _connectionDurationStats.getStdDev();
956     }
957 
958     /* ------------------------------------------------------------ */
959     /**
960      * @return Mean number of requests per connection since statsReset() called. Undefined if setStatsOn(false).
961      */
962     public double getConnectionsRequestsMean()
963     {
964         return _requestStats.getMean();
965     }
966 
967     /* ------------------------------------------------------------ */
968     /**
969      * @return Maximum number of requests per connection since statsReset() called. Undefined if setStatsOn(false).
970      */
971     public int getConnectionsRequestsMax()
972     {
973         return (int)_requestStats.getMax();
974     }
975 
976     /* ------------------------------------------------------------ */
977     /**
978      * @return Standard deviation of number of requests per connection since statsReset() called. Undefined if setStatsOn(false).
979      */
980     public double getConnectionsRequestsStdDev()
981     {
982         return _requestStats.getStdDev();
983     }
984 
985     /* ------------------------------------------------------------ */
986     /**
987      * Reset statistics.
988      */
989     public void statsReset()
990     {
991         updateNotEqual(_statsStartedAt,-1,System.currentTimeMillis());
992 
993         _requestStats.reset();
994         _connectionStats.reset();
995         _connectionDurationStats.reset();
996     }
997 
998     /* ------------------------------------------------------------ */
999     public void setStatsOn(boolean on)
1000     {
1001         if (on && _statsStartedAt.get() != -1)
1002             return;
1003 
1004         if (LOG.isDebugEnabled())
1005             LOG.debug("Statistics on = " + on + " for " + this);
1006 
1007         statsReset();
1008         _statsStartedAt.set(on?System.currentTimeMillis():-1);
1009     }
1010 
1011     /* ------------------------------------------------------------ */
1012     /**
1013      * @return True if statistics collection is turned on.
1014      */
1015     public boolean getStatsOn()
1016     {
1017         return _statsStartedAt.get() != -1;
1018     }
1019 
1020     /* ------------------------------------------------------------ */
1021     /**
1022      * @return Timestamp stats were started at.
1023      */
1024     public long getStatsOnMs()
1025     {
1026         long start = _statsStartedAt.get();
1027 
1028         return (start != -1)?(System.currentTimeMillis() - start):0;
1029     }
1030 
1031     /* ------------------------------------------------------------ */
1032     protected void connectionOpened(Connection connection)
1033     {
1034         if (_statsStartedAt.get() == -1)
1035             return;
1036 
1037         _connectionStats.increment();
1038     }
1039 
1040     /* ------------------------------------------------------------ */
1041     protected void connectionUpgraded(Connection oldConnection, Connection newConnection)
1042     {
1043         _requestStats.set((oldConnection instanceof HttpConnection)?((HttpConnection)oldConnection).getRequests():0);
1044     }
1045 
1046     /* ------------------------------------------------------------ */
1047     protected void connectionClosed(Connection connection)
1048     {
1049         connection.closed();
1050 
1051         if (_statsStartedAt.get() == -1)
1052             return;
1053 
1054         long duration = System.currentTimeMillis() - connection.getTimeStamp();
1055         int requests = (connection instanceof HttpConnection)?((HttpConnection)connection).getRequests():0;
1056         _requestStats.set(requests);
1057         _connectionStats.decrement();
1058         _connectionDurationStats.set(duration);
1059     }
1060 
1061     /* ------------------------------------------------------------ */
1062     /**
1063      * @return the acceptorPriority
1064      */
1065     public int getAcceptorPriorityOffset()
1066     {
1067         return _acceptorPriorityOffset;
1068     }
1069 
1070     /* ------------------------------------------------------------ */
1071     /**
1072      * Set the priority offset of the acceptor threads. The priority is adjusted by this amount (default 0) to either favour the acceptance of new threads and
1073      * newly active connections or to favour the handling of already dispatched connections.
1074      * 
1075      * @param offset
1076      *            the amount to alter the priority of the acceptor threads.
1077      */
1078     public void setAcceptorPriorityOffset(int offset)
1079     {
1080         _acceptorPriorityOffset = offset;
1081     }
1082 
1083     /* ------------------------------------------------------------ */
1084     /**
1085      * @return True if the the server socket will be opened in SO_REUSEADDR mode.
1086      */
1087     public boolean getReuseAddress()
1088     {
1089         return _reuseAddress;
1090     }
1091 
1092     /* ------------------------------------------------------------ */
1093     /**
1094      * @param reuseAddress
1095      *            True if the the server socket will be opened in SO_REUSEADDR mode.
1096      */
1097     public void setReuseAddress(boolean reuseAddress)
1098     {
1099         _reuseAddress = reuseAddress;
1100     }
1101 
1102     /* ------------------------------------------------------------ */
1103     public boolean isLowResources()
1104     {
1105         if (_threadPool != null)
1106             return _threadPool.isLowOnThreads();
1107         return _server.getThreadPool().isLowOnThreads();
1108     }
1109 
1110     /* ------------------------------------------------------------ */
1111     private void updateNotEqual(AtomicLong valueHolder, long compare, long value)
1112     {
1113         long oldValue = valueHolder.get();
1114         while (compare != oldValue)
1115         {
1116             if (valueHolder.compareAndSet(oldValue,value))
1117                 break;
1118             oldValue = valueHolder.get();
1119         }
1120     }
1121 
1122     /* ------------------------------------------------------------ */
1123     public String dump()
1124     {
1125         return AggregateLifeCycle.dump(this);
1126     }
1127 
1128     /* ------------------------------------------------------------ */
1129     public void dump(Appendable out, String indent) throws IOException
1130     {
1131         out.append(String.valueOf(this)).append("\n");
1132     }
1133 
1134 }