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