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