View Javadoc

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