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 implemenation 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 long 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         try{close();} catch(IOException e) {Log.warn(e);}
    314
    315         if (_threadPool==_server.getThreadPool())
    316             _threadPool=null;
    317         else if (_threadPool instanceof LifeCycle)
    318             ((LifeCycle)_threadPool).stop();
    319
    320         super.doStop();
    321
    322         Thread[] acceptors=null;
    323         synchronized(this)
    324         {
    325             acceptors=_acceptorThread;
    326             _acceptorThread=null;
    327         }
    328         if (acceptors != null)
    329         {
    330             for (int i=0;i<acceptors.length;i++)
    331             {
    332                 Thread thread=acceptors[i];
    333                 if (thread!=null)
    334                     thread.interrupt();
    335             }
    336         }
    337
    338     }
    339
    340     /* ------------------------------------------------------------ */
    341     public void join() throws InterruptedException
    342     {
    343         Thread[] threads=_acceptorThread;
    344         if (threads!=null)
    345             for (int i=0;i<threads.length;i++)
    346                 if (threads[i]!=null)
    347                     threads[i].join();
    348     }
    349
    350     /* ------------------------------------------------------------ */
    351     protected void configure(Socket socket)
    352         throws IOException
    353     {
    354         try
    355         {
    356             socket.setTcpNoDelay(true);
    357             if (_maxIdleTime >= 0)
    358                 socket.setSoTimeout(_maxIdleTime);
    359             if (_soLingerTime >= 0)
    360                 socket.setSoLinger(true, _soLingerTime/1000);
    361             else
    362                 socket.setSoLinger(false, 0);
    363         }
    364         catch (Exception e)
    365         {
    366             Log.ignore(e);
    367         }
    368     }
    369
    370
    371     /* ------------------------------------------------------------ */
    372     public void customize(EndPoint endpoint, Request request)
    373         throws IOException
    374     {
    375         if (isForwarded())
    376             checkForwardedHeaders(endpoint, request);
    377     }
    378
    379     /* ------------------------------------------------------------ */
    380     protected void checkForwardedHeaders(EndPoint endpoint, Request request)
    381         throws IOException
    382     {
    383         HttpFields httpFields = request.getConnection().getRequestFields();
    384
    385         // Retrieving headers from the request
    386         String forwardedHost = getLeftMostValue(httpFields.getStringField(getForwardedHostHeader()));
    387         String forwardedServer = getLeftMostValue(httpFields.getStringField(getForwardedServerHeader()));
    388         String forwardedFor = getLeftMostValue(httpFields.getStringField(getForwardedForHeader()));
    389
    390         if (_hostHeader!=null)
    391         {
    392             // Update host header
    393             httpFields.put(HttpHeaders.HOST_BUFFER, _hostHeader);
    394             request.setServerName(null);
    395             request.setServerPort(-1);
    396             request.getServerName();
    397         }
    398         else if (forwardedHost != null)
    399         {
    400             // Update host header
    401             httpFields.put(HttpHeaders.HOST_BUFFER, forwardedHost);
    402             request.setServerName(null);
    403             request.setServerPort(-1);
    404             request.getServerName();
    405         }
    406
    407         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             synchronized(AbstractConnector.this)
    690             {
    691                 if (_acceptorThread==null)
    692                     return;
    693
    694                 _acceptorThread[_acceptor]=current;
    695             }
    696             String name =_acceptorThread[_acceptor].getName();
    697             current.setName(name+" - Acceptor"+_acceptor+" "+AbstractConnector.this);
    698             int old_priority=current.getPriority();
    699
    700             try
    701             {
    702                 current.setPriority(old_priority-_acceptorPriorityOffset);
    703                 while (isRunning() && getConnection()!=null)
    704                 {
    705                     try
    706                     {
    707                         accept(_acceptor);
    708                     }
    709                     catch(EofException e)
    710                     {
    711                         Log.ignore(e);
    712                     }
    713                     catch(IOException e)
    714                     {
    715                         Log.ignore(e);
    716                     }
    717                     catch(ThreadDeath e)
    718                     {
    719                         Log.warn(e);
    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                 try
    733                 {
    734                     if (_acceptor==0)
    735                         close();
    736                 }
    737                 catch (IOException e)
    738                 {
    739                     Log.warn(e);
    740                 }
    741
    742                 synchronized(AbstractConnector.this)
    743                 {
    744                     if (_acceptorThread!=null)
    745                         _acceptorThread[_acceptor]=null;
    746                 }
    747             }
    748         }
    749     }
    750
    751     /* ------------------------------------------------------------ */
    752     public String getName()
    753     {
    754         if (_name==null)
    755             _name= (getHost()==null?"0.0.0.0":getHost())+":"+(getLocalPort()<=0?getPort():getLocalPort());
    756         return _name;
    757     }
    758
    759     /* ------------------------------------------------------------ */
    760     public void setName(String name)
    761     {
    762         _name = name;
    763     }
    764
    765
    766
    767     /* ------------------------------------------------------------ */
    768     /**
    769      * @return Get the number of requests handled by this context
    770      * since last call of statsReset(). If setStatsOn(false) then this
    771      * is undefined.
    772      */
    773     public int getRequests() {return _requests;}
    774
    775     /* ------------------------------------------------------------ */
    776     /**
    777      * @return Returns the connectionsDurationMin.
    778      */
    779     public long getConnectionsDurationMin()
    780     {
    781         return _connectionsDurationMin;
    782     }
    783
    784     /* ------------------------------------------------------------ */
    785     /**
    786      * @return Returns the connectionsDurationTotal.
    787      */
    788     public long getConnectionsDurationTotal()
    789     {
    790         return _connectionsDurationTotal;
    791     }
    792
    793     /* ------------------------------------------------------------ */
    794     /**
    795      * @return Returns the connectionsOpenMin.
    796      */
    797     public int getConnectionsOpenMin()
    798     {
    799         return _connectionsOpenMin;
    800     }
    801
    802     /* ------------------------------------------------------------ */
    803     /**
    804      * @return Returns the connectionsRequestsMin.
    805      */
    806     public int getConnectionsRequestsMin()
    807     {
    808         return _connectionsRequestsMin;
    809     }
    810
    811
    812     /* ------------------------------------------------------------ */
    813     /**
    814      * @return Number of connections accepted by the server since
    815      * statsReset() called. Undefined if setStatsOn(false).
    816      */
    817     public int getConnections() {return _connections;}
    818
    819     /* ------------------------------------------------------------ */
    820     /**
    821      * @return Number of connections currently open that were opened
    822      * since statsReset() called. Undefined if setStatsOn(false).
    823      */
    824     public int getConnectionsOpen() {return _connectionsOpen;}
    825
    826     /* ------------------------------------------------------------ */
    827     /**
    828      * @return Maximum number of connections opened simultaneously
    829      * since statsReset() called. Undefined if setStatsOn(false).
    830      */
    831     public int getConnectionsOpenMax() {return _connectionsOpenMax;}
    832
    833     /* ------------------------------------------------------------ */
    834     /**
    835      * @return Average duration in milliseconds of open connections
    836      * since statsReset() called. Undefined if setStatsOn(false).
    837      */
    838     public long getConnectionsDurationAve() {return _connections==0?0:(_connectionsDurationTotal/_connections);}
    839
    840     /* ------------------------------------------------------------ */
    841     /**
    842      * @return Maximum duration in milliseconds of an open connection
    843      * since statsReset() called. Undefined if setStatsOn(false).
    844      */
    845     public long getConnectionsDurationMax() {return _connectionsDurationMax;}
    846
    847     /* ------------------------------------------------------------ */
    848     /**
    849      * @return Average number of requests per connection
    850      * since statsReset() called. Undefined if setStatsOn(false).
    851      */
    852     public int getConnectionsRequestsAve() {return _connections==0?0:(_requests/_connections);}
    853
    854     /* ------------------------------------------------------------ */
    855     /**
    856      * @return Maximum number of requests per connection
    857      * since statsReset() called. Undefined if setStatsOn(false).
    858      */
    859     public int getConnectionsRequestsMax() {return _connectionsRequestsMax;}
    860
    861
    862
    863     /* ------------------------------------------------------------ */
    864     /** Reset statistics.
    865      */
    866     public void statsReset()
    867     {
    868         _statsStartedAt=_statsStartedAt==-1?-1:System.currentTimeMillis();
    869
    870         _connections=0;
    871
    872         _connectionsOpenMin=_connectionsOpen;
    873         _connectionsOpenMax=_connectionsOpen;
    874         _connectionsOpen=0;
    875
    876         _connectionsDurationMin=0;
    877         _connectionsDurationMax=0;
    878         _connectionsDurationTotal=0;
    879
    880         _requests=0;
    881
    882         _connectionsRequestsMin=0;
    883         _connectionsRequestsMax=0;
    884     }
    885
    886     /* ------------------------------------------------------------ */
    887     public void setStatsOn(boolean on)
    888     {
    889         if (on && _statsStartedAt!=-1)
    890             return;
    891         Log.debug("Statistics on = "+on+" for "+this);
    892         statsReset();
    893         _statsStartedAt=on?System.currentTimeMillis():-1;
    894     }
    895
    896     /* ------------------------------------------------------------ */
    897     /**
    898      * @return True if statistics collection is turned on.
    899      */
    900     public boolean getStatsOn()
    901     {
    902         return _statsStartedAt!=-1;
    903     }
    904
    905     /* ------------------------------------------------------------ */
    906     /**
    907      * @return Timestamp stats were started at.
    908      */
    909     public long getStatsOnMs()
    910     {
    911         return (_statsStartedAt!=-1)?(System.currentTimeMillis()-_statsStartedAt):0;
    912     }
    913
    914     /* ------------------------------------------------------------ */
    915     protected void connectionOpened(HttpConnection connection)
    916     {
    917         if (_statsStartedAt==-1)
    918             return;
    919         synchronized(_statsLock)
    920         {
    921             _connectionsOpen++;
    922             if (_connectionsOpen > _connectionsOpenMax)
    923                 _connectionsOpenMax=_connectionsOpen;
    924         }
    925     }
    926
    927     /* ------------------------------------------------------------ */
    928     protected void connectionClosed(HttpConnection connection)
    929     {
    930         if (_statsStartedAt>=0)
    931         {
    932             long duration=System.currentTimeMillis()-connection.getTimeStamp();
    933             int requests=connection.getRequests();
    934             synchronized(_statsLock)
    935             {
    936                 _requests+=requests;
    937                 _connections++;
    938                 _connectionsOpen--;
    939                 _connectionsDurationTotal+=duration;
    940                 if (_connectionsOpen<0)
    941                     _connectionsOpen=0;
    942                 if (_connectionsOpen<_connectionsOpenMin)
    943                     _connectionsOpenMin=_connectionsOpen;
    944                 if (_connectionsDurationMin==0 || duration<_connectionsDurationMin)
    945                     _connectionsDurationMin=duration;
    946                 if (duration>_connectionsDurationMax)
    947                     _connectionsDurationMax=duration;
    948                 if (_connectionsRequestsMin==0 || requests<_connectionsRequestsMin)
    949                     _connectionsRequestsMin=requests;
    950                 if (requests>_connectionsRequestsMax)
    951                     _connectionsRequestsMax=requests;
    952             }
    953         }
    954
    955         connection.destroy();
    956     }
    957
    958     /* ------------------------------------------------------------ */
    959     /**
    960      * @return the acceptorPriority
    961      */
    962     public int getAcceptorPriorityOffset()
    963     {
    964         return _acceptorPriorityOffset;
    965     }
    966
    967     /* ------------------------------------------------------------ */
    968     /**
    969      * Set the priority offset of the acceptor threads. The priority is adjusted by
    970      * this amount (default 0) to either favour the acceptance of new threads and newly active
    971      * connections or to favour the handling of already dispatched connections.
    972      * @param offset the amount to alter the priority of the acceptor threads.
    973      */
    974     public void setAcceptorPriorityOffset(int offset)
    975     {
    976         _acceptorPriorityOffset=offset;
    977     }
    978
    979     /* ------------------------------------------------------------ */
    980     /**
    981      * @return True if the the server socket will be opened in SO_REUSEADDR mode.
    982      */
    983     public boolean getReuseAddress()
    984     {
    985         return _reuseAddress;
    986     }
    987
    988     /* ------------------------------------------------------------ */
    989     /**
    990      * @param reuseAddress True if the the server socket will be opened in SO_REUSEADDR mode.
    991      */
    992     public void setReuseAddress(boolean reuseAddress)
    993     {
    994         _reuseAddress=reuseAddress;
    995     }
    996
    997 }