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