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