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