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 (_soLingerTime >= 0)
494                 socket.setSoLinger(true,_soLingerTime / 1000);
495             else
496                 socket.setSoLinger(false,0);
497         }
498         catch (Exception e)
499         {
500             Log.ignore(e);
501         }
502     }
503 
504     /* ------------------------------------------------------------ */
505     public void customize(EndPoint endpoint, Request request) throws IOException
506     {
507         if (isForwarded())
508             checkForwardedHeaders(endpoint,request);
509     }
510 
511     /* ------------------------------------------------------------ */
512     protected void checkForwardedHeaders(EndPoint endpoint, Request request) throws IOException
513     {
514         HttpFields httpFields = request.getConnection().getRequestFields();
515 
516         // Retrieving headers from the request
517         String forwardedHost = getLeftMostValue(httpFields.getStringField(getForwardedHostHeader()));
518         String forwardedServer = getLeftMostValue(httpFields.getStringField(getForwardedServerHeader()));
519         String forwardedFor = getLeftMostValue(httpFields.getStringField(getForwardedForHeader()));
520 
521         if (_hostHeader != null)
522         {
523             // Update host header
524             httpFields.put(HttpHeaders.HOST_BUFFER,_hostHeader);
525             request.setServerName(null);
526             request.setServerPort(-1);
527             request.getServerName();
528         }
529         else if (forwardedHost != null)
530         {
531             // Update host header
532             httpFields.put(HttpHeaders.HOST_BUFFER,forwardedHost);
533             request.setServerName(null);
534             request.setServerPort(-1);
535             request.getServerName();
536         }
537         else if (forwardedServer != null)
538         {
539             // Use provided server name
540             request.setServerName(forwardedServer);
541         }
542 
543         if (forwardedFor != null)
544         {
545             request.setRemoteAddr(forwardedFor);
546             InetAddress inetAddress = null;
547 
548             if (_useDNS)
549             {
550                 try
551                 {
552                     inetAddress = InetAddress.getByName(forwardedFor);
553                 }
554                 catch (UnknownHostException e)
555                 {
556                     Log.ignore(e);
557                 }
558             }
559 
560             request.setRemoteHost(inetAddress == null?forwardedFor:inetAddress.getHostName());
561         }
562     }
563 
564     /* ------------------------------------------------------------ */
565     protected String getLeftMostValue(String headerValue)
566     {
567         if (headerValue == null)
568             return null;
569 
570         int commaIndex = headerValue.indexOf(',');
571 
572         if (commaIndex == -1)
573         {
574             // Single value
575             return headerValue;
576         }
577 
578         // The left-most value is the farthest downstream client
579         return headerValue.substring(0,commaIndex);
580     }
581 
582     /* ------------------------------------------------------------ */
583     public void persist(EndPoint endpoint) throws IOException
584     {
585     }
586 
587     /* ------------------------------------------------------------ */
588     /*
589      * @see org.eclipse.jetty.server.Connector#getConfidentialPort()
590      */
591     public int getConfidentialPort()
592     {
593         return _confidentialPort;
594     }
595 
596     /* ------------------------------------------------------------ */
597     /* ------------------------------------------------------------ */
598     /*
599      * @see org.eclipse.jetty.server.Connector#getConfidentialScheme()
600      */
601     public String getConfidentialScheme()
602     {
603         return _confidentialScheme;
604     }
605 
606     /* ------------------------------------------------------------ */
607     /*
608      * @see
609      * org.eclipse.jetty.server.Connector#isConfidential(org.eclipse.jetty.server
610      * .Request)
611      */
612     public boolean isIntegral(Request request)
613     {
614         return false;
615     }
616 
617     /* ------------------------------------------------------------ */
618     /*
619      * @see org.eclipse.jetty.server.Connector#getConfidentialPort()
620      */
621     public int getIntegralPort()
622     {
623         return _integralPort;
624     }
625 
626     /* ------------------------------------------------------------ */
627     /*
628      * @see org.eclipse.jetty.server.Connector#getIntegralScheme()
629      */
630     public String getIntegralScheme()
631     {
632         return _integralScheme;
633     }
634 
635     /* ------------------------------------------------------------ */
636     /*
637      * @see
638      * org.eclipse.jetty.server.Connector#isConfidential(org.eclipse.jetty.server
639      * .Request)
640      */
641     public boolean isConfidential(Request request)
642     {
643         return false;
644     }
645 
646     /* ------------------------------------------------------------ */
647     /**
648      * @param confidentialPort
649      *            The confidentialPort to set.
650      */
651     public void setConfidentialPort(int confidentialPort)
652     {
653         _confidentialPort = confidentialPort;
654     }
655 
656     /* ------------------------------------------------------------ */
657     /**
658      * @param confidentialScheme
659      *            The confidentialScheme to set.
660      */
661     public void setConfidentialScheme(String confidentialScheme)
662     {
663         _confidentialScheme = confidentialScheme;
664     }
665 
666     /* ------------------------------------------------------------ */
667     /**
668      * @param integralPort
669      *            The integralPort to set.
670      */
671     public void setIntegralPort(int integralPort)
672     {
673         _integralPort = integralPort;
674     }
675 
676     /* ------------------------------------------------------------ */
677     /**
678      * @param integralScheme
679      *            The integralScheme to set.
680      */
681     public void setIntegralScheme(String integralScheme)
682     {
683         _integralScheme = integralScheme;
684     }
685 
686     /* ------------------------------------------------------------ */
687     protected abstract void accept(int acceptorID) throws IOException, InterruptedException;
688 
689     /* ------------------------------------------------------------ */
690     public void stopAccept(int acceptorID) throws Exception
691     {
692     }
693 
694     /* ------------------------------------------------------------ */
695     public boolean getResolveNames()
696     {
697         return _useDNS;
698     }
699 
700     /* ------------------------------------------------------------ */
701     public void setResolveNames(boolean resolve)
702     {
703         _useDNS = resolve;
704     }
705 
706     /* ------------------------------------------------------------ */
707     /**
708      * Is reverse proxy handling on?
709      * 
710      * @return true if this connector is checking the
711      *         x-forwarded-for/host/server headers
712      */
713     public boolean isForwarded()
714     {
715         return _forwarded;
716     }
717 
718     /* ------------------------------------------------------------ */
719     /**
720      * Set reverse proxy handling
721      * 
722      * @param check
723      *            true if this connector is checking the
724      *            x-forwarded-for/host/server headers
725      */
726     public void setForwarded(boolean check)
727     {
728         if (check)
729             Log.debug(this + " is forwarded");
730         _forwarded = check;
731     }
732 
733     /* ------------------------------------------------------------ */
734     public String getHostHeader()
735     {
736         return _hostHeader;
737     }
738 
739     /* ------------------------------------------------------------ */
740     /**
741      * Set a forced valued for the host header to control what is returned by
742      * {@link ServletRequest#getServerName()} and
743      * {@link ServletRequest#getServerPort()}. This value is only used if
744      * {@link #isForwarded()} is true.
745      * 
746      * @param hostHeader
747      *            The value of the host header to force.
748      */
749     public void setHostHeader(String hostHeader)
750     {
751         _hostHeader = hostHeader;
752     }
753 
754     /* ------------------------------------------------------------ */
755     public String getForwardedHostHeader()
756     {
757         return _forwardedHostHeader;
758     }
759 
760     /* ------------------------------------------------------------ */
761     /**
762      * @param forwardedHostHeader
763      *            The header name for forwarded hosts (default x-forwarded-host)
764      */
765     public void setForwardedHostHeader(String forwardedHostHeader)
766     {
767         _forwardedHostHeader = forwardedHostHeader;
768     }
769 
770     /* ------------------------------------------------------------ */
771     public String getForwardedServerHeader()
772     {
773         return _forwardedServerHeader;
774     }
775 
776     /* ------------------------------------------------------------ */
777     /**
778      * @param forwardedServerHeader
779      *            The header name for forwarded server (default
780      *            x-forwarded-server)
781      */
782     public void setForwardedServerHeader(String forwardedServerHeader)
783     {
784         _forwardedServerHeader = forwardedServerHeader;
785     }
786 
787     /* ------------------------------------------------------------ */
788     public String getForwardedForHeader()
789     {
790         return _forwardedForHeader;
791     }
792 
793     /* ------------------------------------------------------------ */
794     /**
795      * @param forwardedRemoteAddressHeader
796      *            The header name for forwarded for (default x-forwarded-for)
797      */
798     public void setForwardedForHeader(String forwardedRemoteAddressHeader)
799     {
800         _forwardedForHeader = forwardedRemoteAddressHeader;
801     }
802 
803     /* ------------------------------------------------------------ */
804     @Override
805     public String toString()
806     {
807         String name = this.getClass().getName();
808         int dot = name.lastIndexOf('.');
809         if (dot > 0)
810             name = name.substring(dot + 1);
811 
812         return name + "@" + (getHost() == null?"0.0.0.0":getHost()) + ":" + (getLocalPort() <= 0?getPort():getLocalPort());
813     }
814 
815     /* ------------------------------------------------------------ */
816     /* ------------------------------------------------------------ */
817     /* ------------------------------------------------------------ */
818     private class Acceptor implements Runnable
819     {
820         int _acceptor = 0;
821 
822         Acceptor(int id)
823         {
824             _acceptor = id;
825         }
826 
827         /* ------------------------------------------------------------ */
828         public void run()
829         {
830             Thread current = Thread.currentThread();
831             String name;
832             synchronized (AbstractConnector.this)
833             {
834                 if (_acceptorThread == null)
835                     return;
836 
837                 _acceptorThread[_acceptor] = current;
838                 name = _acceptorThread[_acceptor].getName();
839                 current.setName(name + " - Acceptor" + _acceptor + " " + AbstractConnector.this);
840             }
841             int old_priority = current.getPriority();
842 
843             try
844             {
845                 current.setPriority(old_priority - _acceptorPriorityOffset);
846                 while (isRunning() && getConnection() != null)
847                 {
848                     try
849                     {
850                         accept(_acceptor);
851                     }
852                     catch (EofException e)
853                     {
854                         Log.ignore(e);
855                     }
856                     catch (IOException e)
857                     {
858                         Log.ignore(e);
859                     }
860                     catch (InterruptedException x)
861                     {
862                         // Connector has been stopped
863                         Log.ignore(x);
864                     }
865                     catch (ThreadDeath e)
866                     {
867                         throw e;
868                     }
869                     catch (Throwable e)
870                     {
871                         Log.warn(e);
872                     }
873                 }
874             }
875             finally
876             {
877                 current.setPriority(old_priority);
878                 current.setName(name);
879                 try
880                 {
881                     if (_acceptor == 0)
882                         close();
883                 }
884                 catch (IOException e)
885                 {
886                     Log.warn(e);
887                 }
888 
889                 synchronized (AbstractConnector.this)
890                 {
891                     if (_acceptorThread != null)
892                         _acceptorThread[_acceptor] = null;
893                 }
894             }
895         }
896     }
897 
898     /* ------------------------------------------------------------ */
899     public String getName()
900     {
901         if (_name == null)
902             _name = (getHost() == null?"0.0.0.0":getHost()) + ":" + (getLocalPort() <= 0?getPort():getLocalPort());
903         return _name;
904     }
905 
906     /* ------------------------------------------------------------ */
907     public void setName(String name)
908     {
909         _name = name;
910     }
911 
912     /* ------------------------------------------------------------ */
913     /**
914      * @return Get the number of requests handled by this connector since last
915      *         call of statsReset(). If setStatsOn(false) then this is
916      *         undefined.
917      */
918     public int getRequests()
919     {
920         return (int)_requestStats.getTotal();
921     }
922 
923     /* ------------------------------------------------------------ */
924     /**
925      * @return Returns the connectionsDurationTotal.
926      */
927     public long getConnectionsDurationTotal()
928     {
929         return _connectionDurationStats.getTotal();
930     }
931 
932     /* ------------------------------------------------------------ */
933     /**
934      * @return Number of connections accepted by the server since statsReset()
935      *         called. Undefined if setStatsOn(false).
936      */
937     public int getConnections()
938     {
939         return (int)_connectionStats.getTotal();
940     }
941 
942     /* ------------------------------------------------------------ */
943     /**
944      * @return Number of connections currently open that were opened since
945      *         statsReset() called. Undefined if setStatsOn(false).
946      */
947     public int getConnectionsOpen()
948     {
949         return (int)_connectionStats.getCurrent();
950     }
951 
952     /* ------------------------------------------------------------ */
953     /**
954      * @return Maximum number of connections opened simultaneously since
955      *         statsReset() called. Undefined if setStatsOn(false).
956      */
957     public int getConnectionsOpenMax()
958     {
959         return (int)_connectionStats.getMax();
960     }
961 
962     /* ------------------------------------------------------------ */
963     /**
964      * @return Mean duration in milliseconds of open connections since
965      *         statsReset() called. Undefined if setStatsOn(false).
966      */
967     public double getConnectionsDurationMean()
968     {
969         return _connectionDurationStats.getMean();
970     }
971 
972     /* ------------------------------------------------------------ */
973     /**
974      * @return Maximum duration in milliseconds of an open connection since
975      *         statsReset() called. Undefined if setStatsOn(false).
976      */
977     public long getConnectionsDurationMax()
978     {
979         return _connectionDurationStats.getMax();
980     }
981 
982     /* ------------------------------------------------------------ */
983     /**
984      * @return Standard deviation of duration in milliseconds of open
985      *         connections since statsReset() called. Undefined if
986      *         setStatsOn(false).
987      */
988     public double getConnectionsDurationStdDev()
989     {
990         return _connectionDurationStats.getStdDev();
991     }
992 
993     /* ------------------------------------------------------------ */
994     /**
995      * @return Mean number of requests per connection since statsReset() called.
996      *         Undefined if setStatsOn(false).
997      */
998     public double getConnectionsRequestsMean()
999     {
1000         return _requestStats.getMean();
1001     }
1002 
1003     /* ------------------------------------------------------------ */
1004     /**
1005      * @return Maximum number of requests per connection since statsReset()
1006      *         called. Undefined if setStatsOn(false).
1007      */
1008     public int getConnectionsRequestsMax()
1009     {
1010         return (int)_requestStats.getMax();
1011     }
1012 
1013     /* ------------------------------------------------------------ */
1014     /**
1015      * @return Standard deviation of number of requests per connection since
1016      *         statsReset() called. Undefined if setStatsOn(false).
1017      */
1018     public double getConnectionsRequestsStdDev()
1019     {
1020         return _requestStats.getStdDev();
1021     }
1022 
1023     /* ------------------------------------------------------------ */
1024     /**
1025      * Reset statistics.
1026      */
1027     public void statsReset()
1028     {
1029         updateNotEqual(_statsStartedAt,-1,System.currentTimeMillis());
1030 
1031         _requestStats.reset();
1032         _connectionStats.reset();
1033         _connectionDurationStats.reset();
1034     }
1035 
1036     /* ------------------------------------------------------------ */
1037     public void setStatsOn(boolean on)
1038     {
1039         if (on && _statsStartedAt.get() != -1)
1040             return;
1041 
1042         Log.debug("Statistics on = " + on + " for " + this);
1043 
1044         statsReset();
1045         _statsStartedAt.set(on?System.currentTimeMillis():-1);
1046     }
1047 
1048     /* ------------------------------------------------------------ */
1049     /**
1050      * @return True if statistics collection is turned on.
1051      */
1052     public boolean getStatsOn()
1053     {
1054         return _statsStartedAt.get() != -1;
1055     }
1056 
1057     /* ------------------------------------------------------------ */
1058     /**
1059      * @return Timestamp stats were started at.
1060      */
1061     public long getStatsOnMs()
1062     {
1063         long start = _statsStartedAt.get();
1064 
1065         return (start != -1)?(System.currentTimeMillis() - start):0;
1066     }
1067 
1068     /* ------------------------------------------------------------ */
1069     protected void connectionOpened(Connection connection)
1070     {
1071         if (_statsStartedAt.get() == -1)
1072             return;
1073 
1074         _connectionStats.increment();
1075     }
1076 
1077     /* ------------------------------------------------------------ */
1078     protected void connectionUpgraded(Connection oldConnection, Connection newConnection)
1079     {
1080         _requestStats.set((oldConnection instanceof HttpConnection)?((HttpConnection)oldConnection).getRequests():0);
1081     }
1082 
1083     /* ------------------------------------------------------------ */
1084     protected void connectionClosed(Connection connection)
1085     {
1086         if (_statsStartedAt.get() == -1)
1087             return;
1088 
1089         long duration = System.currentTimeMillis() - connection.getTimeStamp();
1090         int requests = (connection instanceof HttpConnection)?((HttpConnection)connection).getRequests():0;
1091 
1092         _requestStats.set(requests);
1093         _connectionStats.decrement();
1094         _connectionDurationStats.set(duration);
1095     }
1096 
1097     /* ------------------------------------------------------------ */
1098     /**
1099      * @return the acceptorPriority
1100      */
1101     public int getAcceptorPriorityOffset()
1102     {
1103         return _acceptorPriorityOffset;
1104     }
1105 
1106     /* ------------------------------------------------------------ */
1107     /**
1108      * Set the priority offset of the acceptor threads. The priority is adjusted
1109      * by this amount (default 0) to either favour the acceptance of new threads
1110      * and newly active connections or to favour the handling of already
1111      * dispatched connections.
1112      * 
1113      * @param offset
1114      *            the amount to alter the priority of the acceptor threads.
1115      */
1116     public void setAcceptorPriorityOffset(int offset)
1117     {
1118         _acceptorPriorityOffset = offset;
1119     }
1120 
1121     /* ------------------------------------------------------------ */
1122     /**
1123      * @return True if the the server socket will be opened in SO_REUSEADDR
1124      *         mode.
1125      */
1126     public boolean getReuseAddress()
1127     {
1128         return _reuseAddress;
1129     }
1130 
1131     /* ------------------------------------------------------------ */
1132     /**
1133      * @param reuseAddress
1134      *            True if the the server socket will be opened in SO_REUSEADDR
1135      *            mode.
1136      */
1137     public void setReuseAddress(boolean reuseAddress)
1138     {
1139         _reuseAddress = reuseAddress;
1140     }
1141 
1142     /* ------------------------------------------------------------ */
1143     public boolean isLowResources()
1144     {
1145         if (_threadPool != null)
1146             return _threadPool.isLowOnThreads();
1147         return _server.getThreadPool().isLowOnThreads();
1148     }
1149 
1150     /* ------------------------------------------------------------ */
1151     private void updateNotEqual(AtomicLong valueHolder, long compare, long value)
1152     {
1153         long oldValue = valueHolder.get();
1154         while (compare != oldValue)
1155         {
1156             if (valueHolder.compareAndSet(oldValue,value))
1157                 break;
1158             oldValue = valueHolder.get();
1159         }
1160     }
1161 }