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