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