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