View Javadoc

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