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;
362         synchronized(this)
363         {
364             threads= _acceptorThread;
365         }
366         if (threads != null)
367             for (int i = 0; i < threads.length; i++)
368                 if (threads[i] != null)
369                     threads[i].join();
370     }
371 
372     /* ------------------------------------------------------------ */
373     protected void configure(Socket socket) throws IOException
374     {
375         try
376         {
377             socket.setTcpNoDelay(true);
378             if (_soLingerTime >= 0)
379                 socket.setSoLinger(true,_soLingerTime / 1000);
380             else
381                 socket.setSoLinger(false,0);
382         }
383         catch (Exception e)
384         {
385             LOG.ignore(e);
386         }
387     }
388 
389     /* ------------------------------------------------------------ */
390     public void customize(EndPoint endpoint, Request request) throws IOException
391     {
392         if (isForwarded())
393             checkForwardedHeaders(endpoint,request);
394     }
395 
396     /* ------------------------------------------------------------ */
397     protected void checkForwardedHeaders(EndPoint endpoint, Request request) throws IOException
398     {
399         HttpFields httpFields = request.getConnection().getRequestFields();
400 
401         // Do SSL first
402         if (getForwardedCipherSuiteHeader()!=null)
403         {
404             String cipher_suite=httpFields.getStringField(getForwardedCipherSuiteHeader());
405             if (cipher_suite!=null)
406                 request.setAttribute("javax.servlet.request.cipher_suite",cipher_suite);
407         }
408         if (getForwardedSslSessionIdHeader()!=null)
409         {
410             String ssl_session_id=httpFields.getStringField(getForwardedSslSessionIdHeader());
411             if(ssl_session_id!=null)
412             {
413                 request.setAttribute("javax.servlet.request.ssl_session_id", ssl_session_id);
414                 request.setScheme(HttpSchemes.HTTPS);
415             }
416         }
417 
418         // Retrieving headers from the request
419         String forwardedHost = getLeftMostFieldValue(httpFields,getForwardedHostHeader());
420         String forwardedServer = getLeftMostFieldValue(httpFields,getForwardedServerHeader());
421         String forwardedFor = getLeftMostFieldValue(httpFields,getForwardedForHeader());
422         String forwardedProto = getLeftMostFieldValue(httpFields,getForwardedProtoHeader());
423 
424         if (_hostHeader != null)
425         {
426             // Update host header
427             httpFields.put(HttpHeaders.HOST_BUFFER,_hostHeader);
428             request.setServerName(null);
429             request.setServerPort(-1);
430             request.getServerName();
431         }
432         else if (forwardedHost != null)
433         {
434             // Update host header
435             httpFields.put(HttpHeaders.HOST_BUFFER,forwardedHost);
436             request.setServerName(null);
437             request.setServerPort(-1);
438             request.getServerName();
439         }
440         else if (forwardedServer != null)
441         {
442             // Use provided server name
443             request.setServerName(forwardedServer);
444         }
445 
446         if (forwardedFor != null)
447         {
448             request.setRemoteAddr(forwardedFor);
449             InetAddress inetAddress = null;
450 
451             if (_useDNS)
452             {
453                 try
454                 {
455                     inetAddress = InetAddress.getByName(forwardedFor);
456                 }
457                 catch (UnknownHostException e)
458                 {
459                     LOG.ignore(e);
460                 }
461             }
462 
463             request.setRemoteHost(inetAddress == null?forwardedFor:inetAddress.getHostName());
464         }
465 
466         if (forwardedProto != null)
467         {
468             request.setScheme(forwardedProto);
469         }
470     }
471 
472     /* ------------------------------------------------------------ */
473     protected String getLeftMostFieldValue(HttpFields fields, String header)
474     {
475         if (header == null)
476             return null;
477 
478         String headerValue = fields.getStringField(header);
479 
480         if (headerValue == null)
481             return null;
482 
483         int commaIndex = headerValue.indexOf(',');
484 
485         if (commaIndex == -1)
486         {
487             // Single value
488             return headerValue;
489         }
490 
491         // The left-most value is the farthest downstream client
492         return headerValue.substring(0,commaIndex);
493     }
494 
495     /* ------------------------------------------------------------ */
496     public void persist(EndPoint endpoint) throws IOException
497     {
498     }
499 
500     /* ------------------------------------------------------------ */
501     /*
502      * @see org.eclipse.jetty.server.Connector#getConfidentialPort()
503      */
504     public int getConfidentialPort()
505     {
506         return _confidentialPort;
507     }
508 
509     /* ------------------------------------------------------------ */
510     /* ------------------------------------------------------------ */
511     /*
512      * @see org.eclipse.jetty.server.Connector#getConfidentialScheme()
513      */
514     public String getConfidentialScheme()
515     {
516         return _confidentialScheme;
517     }
518 
519     /* ------------------------------------------------------------ */
520     /*
521      * @see org.eclipse.jetty.server.Connector#isConfidential(org.eclipse.jetty.server .Request)
522      */
523     public boolean isIntegral(Request request)
524     {
525         return false;
526     }
527 
528     /* ------------------------------------------------------------ */
529     /*
530      * @see org.eclipse.jetty.server.Connector#getConfidentialPort()
531      */
532     public int getIntegralPort()
533     {
534         return _integralPort;
535     }
536 
537     /* ------------------------------------------------------------ */
538     /*
539      * @see org.eclipse.jetty.server.Connector#getIntegralScheme()
540      */
541     public String getIntegralScheme()
542     {
543         return _integralScheme;
544     }
545 
546     /* ------------------------------------------------------------ */
547     /*
548      * @see org.eclipse.jetty.server.Connector#isConfidential(org.eclipse.jetty.server.Request)
549      */
550     public boolean isConfidential(Request request)
551     {
552         return _forwarded && request.getScheme().equalsIgnoreCase(HttpSchemes.HTTPS);
553     }
554 
555     /* ------------------------------------------------------------ */
556     /**
557      * @param confidentialPort
558      *            The confidentialPort to set.
559      */
560     public void setConfidentialPort(int confidentialPort)
561     {
562         _confidentialPort = confidentialPort;
563     }
564 
565     /* ------------------------------------------------------------ */
566     /**
567      * @param confidentialScheme
568      *            The confidentialScheme to set.
569      */
570     public void setConfidentialScheme(String confidentialScheme)
571     {
572         _confidentialScheme = confidentialScheme;
573     }
574 
575     /* ------------------------------------------------------------ */
576     /**
577      * @param integralPort
578      *            The integralPort to set.
579      */
580     public void setIntegralPort(int integralPort)
581     {
582         _integralPort = integralPort;
583     }
584 
585     /* ------------------------------------------------------------ */
586     /**
587      * @param integralScheme
588      *            The integralScheme to set.
589      */
590     public void setIntegralScheme(String integralScheme)
591     {
592         _integralScheme = integralScheme;
593     }
594 
595     /* ------------------------------------------------------------ */
596     protected abstract void accept(int acceptorID) throws IOException, InterruptedException;
597 
598     /* ------------------------------------------------------------ */
599     public void stopAccept(int acceptorID) throws Exception
600     {
601     }
602 
603     /* ------------------------------------------------------------ */
604     public boolean getResolveNames()
605     {
606         return _useDNS;
607     }
608 
609     /* ------------------------------------------------------------ */
610     public void setResolveNames(boolean resolve)
611     {
612         _useDNS = resolve;
613     }
614 
615     /* ------------------------------------------------------------ */
616     /**
617      * Is reverse proxy handling on?
618      *
619      * @return true if this connector is checking the x-forwarded-for/host/server headers
620      */
621     public boolean isForwarded()
622     {
623         return _forwarded;
624     }
625 
626     /* ------------------------------------------------------------ */
627     /**
628      * 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,
629      * host, server and client ip.
630      *
631      * @param check
632      *            true if this connector is checking the x-forwarded-for/host/server headers
633      * @set {@link #setForwardedForHeader(String)}
634      * @set {@link #setForwardedHostHeader(String)}
635      * @set {@link #setForwardedProtoHeader(String)}
636      * @set {@link #setForwardedServerHeader(String)}
637      */
638     public void setForwarded(boolean check)
639     {
640         if (check)
641             LOG.debug("{} is forwarded",this);
642         _forwarded = check;
643     }
644 
645     /* ------------------------------------------------------------ */
646     public String getHostHeader()
647     {
648         return _hostHeader;
649     }
650 
651     /* ------------------------------------------------------------ */
652     /**
653      * Set a forced valued for the host header to control what is returned by {@link ServletRequest#getServerName()} and {@link ServletRequest#getServerPort()}.
654      * This value is only used if {@link #isForwarded()} is true.
655      *
656      * @param hostHeader
657      *            The value of the host header to force.
658      */
659     public void setHostHeader(String hostHeader)
660     {
661         _hostHeader = hostHeader;
662     }
663 
664     /* ------------------------------------------------------------ */
665     /*
666      *
667      * @see #setForwarded(boolean)
668      */
669     public String getForwardedHostHeader()
670     {
671         return _forwardedHostHeader;
672     }
673 
674     /* ------------------------------------------------------------ */
675     /**
676      * @param forwardedHostHeader
677      *            The header name for forwarded hosts (default x-forwarded-host)
678      * @see #setForwarded(boolean)
679      */
680     public void setForwardedHostHeader(String forwardedHostHeader)
681     {
682         _forwardedHostHeader = forwardedHostHeader;
683     }
684 
685     /* ------------------------------------------------------------ */
686     /**
687      * @return the header name for forwarded server.
688      * @see #setForwarded(boolean)
689      */
690     public String getForwardedServerHeader()
691     {
692         return _forwardedServerHeader;
693     }
694 
695     /* ------------------------------------------------------------ */
696     /**
697      * @param forwardedServerHeader
698      *            The header name for forwarded server (default x-forwarded-server)
699      * @see #setForwarded(boolean)
700      */
701     public void setForwardedServerHeader(String forwardedServerHeader)
702     {
703         _forwardedServerHeader = forwardedServerHeader;
704     }
705 
706     /* ------------------------------------------------------------ */
707     /**
708      * @see #setForwarded(boolean)
709      */
710     public String getForwardedForHeader()
711     {
712         return _forwardedForHeader;
713     }
714 
715     /* ------------------------------------------------------------ */
716     /**
717      * @param forwardedRemoteAddressHeader
718      *            The header name for forwarded for (default x-forwarded-for)
719      * @see #setForwarded(boolean)
720      */
721     public void setForwardedForHeader(String forwardedRemoteAddressHeader)
722     {
723         _forwardedForHeader = forwardedRemoteAddressHeader;
724     }
725 
726     /* ------------------------------------------------------------ */
727     /**
728      * Get the forwardedProtoHeader.
729      *
730      * @return the forwardedProtoHeader (default X-Forwarded-For)
731      * @see #setForwarded(boolean)
732      */
733     public String getForwardedProtoHeader()
734     {
735         return _forwardedProtoHeader;
736     }
737 
738     /* ------------------------------------------------------------ */
739     /**
740      * Set the forwardedProtoHeader.
741      *
742      * @param forwardedProtoHeader
743      *            the forwardedProtoHeader to set (default X-Forwarded-For)
744      * @see #setForwarded(boolean)
745      */
746     public void setForwardedProtoHeader(String forwardedProtoHeader)
747     {
748         _forwardedProtoHeader = forwardedProtoHeader;
749     }
750 
751     /* ------------------------------------------------------------ */
752     /**
753      * @return The header name holding a forwarded cipher suite (default null)
754      */
755     public String getForwardedCipherSuiteHeader()
756     {
757         return _forwardedCipherSuiteHeader;
758     }
759 
760     /* ------------------------------------------------------------ */
761     /**
762      * @param forwardedCipherSuite
763      *            The header name holding a forwarded cipher suite (default null)
764      */
765     public void setForwardedCipherSuiteHeader(String forwardedCipherSuite)
766     {
767         _forwardedCipherSuiteHeader = forwardedCipherSuite;
768     }
769 
770     /* ------------------------------------------------------------ */
771     /**
772      * @return The header name holding a forwarded SSL Session ID (default null)
773      */
774     public String getForwardedSslSessionIdHeader()
775     {
776         return _forwardedSslSessionIdHeader;
777     }
778 
779     /* ------------------------------------------------------------ */
780     /**
781      * @param forwardedSslSessionId
782      *            The header name holding a forwarded SSL Session ID (default null)
783      */
784     public void setForwardedSslSessionIdHeader(String forwardedSslSessionId)
785     {
786         _forwardedSslSessionIdHeader = forwardedSslSessionId;
787     }
788 
789     /* ------------------------------------------------------------ */
790     @Override
791     public String toString()
792     {
793         String name = this.getClass().getName();
794         int dot = name.lastIndexOf('.');
795         if (dot > 0)
796             name = name.substring(dot + 1);
797 
798         return name + "@" + (getHost() == null?"0.0.0.0":getHost()) + ":" + (getLocalPort() <= 0?getPort():getLocalPort()) + " "
799                 + AbstractLifeCycle.getState(this);
800     }
801 
802     /* ------------------------------------------------------------ */
803     /* ------------------------------------------------------------ */
804     /* ------------------------------------------------------------ */
805     private class Acceptor implements Runnable
806     {
807         int _acceptor = 0;
808 
809         Acceptor(int id)
810         {
811             _acceptor = id;
812         }
813 
814         /* ------------------------------------------------------------ */
815         public void run()
816         {
817             Thread current = Thread.currentThread();
818             String name;
819             synchronized (AbstractConnector.this)
820             {
821                 if (_acceptorThread == null)
822                     return;
823 
824                 _acceptorThread[_acceptor] = current;
825                 name = _acceptorThread[_acceptor].getName();
826                 current.setName(name + " Acceptor" + _acceptor + " " + AbstractConnector.this);
827             }
828             int old_priority = current.getPriority();
829 
830             try
831             {
832                 current.setPriority(old_priority - _acceptorPriorityOffset);
833                 while (isRunning() && getConnection() != null)
834                 {
835                     try
836                     {
837                         accept(_acceptor);
838                     }
839                     catch (EofException e)
840                     {
841                         LOG.ignore(e);
842                     }
843                     catch (IOException e)
844                     {
845                         LOG.ignore(e);
846                     }
847                     catch (InterruptedException x)
848                     {
849                         // Connector has been stopped
850                         LOG.ignore(x);
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 AbstractHttpConnection)?((AbstractHttpConnection)oldConnection).getRequests():0);
1044     }
1045 
1046     /* ------------------------------------------------------------ */
1047     protected void connectionClosed(Connection connection)
1048     {
1049         connection.onClose();
1050 
1051         if (_statsStartedAt.get() == -1)
1052             return;
1053 
1054         long duration = System.currentTimeMillis() - connection.getTimeStamp();
1055         int requests = (connection instanceof AbstractHttpConnection)?((AbstractHttpConnection)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 }