All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.mortbay.jetty.AbstractConnector Maven / Gradle / Ivy

The newest version!
//========================================================================
//$Id: AbstractConnector.java,v 1.9 2005/11/14 11:00:31 gregwilkins Exp $
//Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at 
//http://www.apache.org/licenses/LICENSE-2.0
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//========================================================================

package org.mortbay.jetty;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.servlet.ServletRequest;

import org.mortbay.component.LifeCycle;
import org.mortbay.io.EndPoint;
import org.mortbay.log.Log;
import org.mortbay.thread.ThreadPool;


/** Abstract Connector implementation.
 * This abstract implemenation of the Connector interface provides:
    *
  • AbstractLifeCycle implementation
  • *
  • Implementations for connector getters and setters
  • *
  • Buffer management
  • *
  • Socket configuration
  • *
  • Base acceptor thread
  • *
  • Optional reverse proxy headers checking
  • *
* * @author gregw * * TODO - allow multiple Acceptor threads */ public abstract class AbstractConnector extends AbstractBuffers implements Connector { private String _name; private Server _server; private ThreadPool _threadPool; private String _host; private int _port=0; private String _integralScheme=HttpSchemes.HTTPS; private int _integralPort=0; private String _confidentialScheme=HttpSchemes.HTTPS; private int _confidentialPort=0; private int _acceptQueueSize=0; private int _acceptors=1; private int _acceptorPriorityOffset=0; private boolean _useDNS; private boolean _forwarded; private String _hostHeader; private String _forwardedHostHeader = "X-Forwarded-Host"; // default to mod_proxy_http header private String _forwardedServerHeader = "X-Forwarded-Server"; // default to mod_proxy_http header private String _forwardedForHeader = "X-Forwarded-For"; // default to mod_proxy_http header private boolean _reuseAddress=true; protected int _maxIdleTime=200000; protected int _lowResourceMaxIdleTime=-1; protected int _soLingerTime=-1; private transient Thread[] _acceptorThread; Object _statsLock = new Object(); transient long _statsStartedAt=-1; transient int _requests; transient int _connections; // total number of connections made to server transient int _connectionsOpen; // number of connections currently open transient int _connectionsOpenMin; // min number of connections open simultaneously transient int _connectionsOpenMax; // max number of connections open simultaneously transient long _connectionsDurationMin; // min duration of a connection transient long _connectionsDurationMax; // max duration of a connection transient long _connectionsDurationTotal; // total duration of all coneection transient int _connectionsRequestsMin; // min requests per connection transient int _connectionsRequestsMax; // max requests per connection /* ------------------------------------------------------------------------------- */ /** */ public AbstractConnector() { } /* ------------------------------------------------------------------------------- */ /* */ public Server getServer() { return _server; } /* ------------------------------------------------------------------------------- */ public void setServer(Server server) { _server=server; } /* ------------------------------------------------------------------------------- */ /* * @see org.mortbay.jetty.HttpListener#getHttpServer() */ public ThreadPool getThreadPool() { return _threadPool; } /* ------------------------------------------------------------------------------- */ public void setThreadPool(ThreadPool pool) { _threadPool=pool; } /* ------------------------------------------------------------------------------- */ /** */ public void setHost(String host) { _host=host; } /* ------------------------------------------------------------------------------- */ /* */ public String getHost() { return _host; } /* ------------------------------------------------------------------------------- */ /* * @see org.mortbay.jetty.HttpListener#setPort(int) */ public void setPort(int port) { _port=port; } /* ------------------------------------------------------------------------------- */ /* * @see org.mortbay.jetty.HttpListener#getPort() */ public int getPort() { return _port; } /* ------------------------------------------------------------ */ /** * @return Returns the maxIdleTime. */ public int getMaxIdleTime() { return _maxIdleTime; } /* ------------------------------------------------------------ */ /** * Set the maximum Idle time for a connection, which roughly translates * to the {@link Socket#setSoTimeout(int)} call, although with NIO * implementations other mechanisms may be used to implement the timeout. * The max idle time is applied:
    *
  • When waiting for a new request to be received on a connection
  • *
  • When reading the headers and content of a request
  • *
  • When writing the headers and content of a response
  • *
* Jetty interprets this value as the maximum time between some progress being * made on the connection. So if a single byte is read or written, then the * timeout (if implemented by jetty) is reset. However, in many instances, * the reading/writing is delegated to the JVM, and the semantic is more * strictly enforced as the maximum time a single read/write operation can * take. Note, that as Jetty supports writes of memory mapped file buffers, * then a write may take many 10s of seconds for large content written to a * slow device. *

* Previously, Jetty supported separate idle timeouts and IO operation timeouts, * however the expense of changing the value of soTimeout was significant, so * these timeouts were merged. With the advent of NIO, it may be possible to * again differentiate these values (if there is demand). * * @param maxIdleTime The maxIdleTime to set. */ public void setMaxIdleTime(int maxIdleTime) { _maxIdleTime = maxIdleTime; } /* ------------------------------------------------------------ */ /** * @return Returns the maxIdleTime. */ public int getLowResourceMaxIdleTime() { return _lowResourceMaxIdleTime; } /* ------------------------------------------------------------ */ /** * @param maxIdleTime The maxIdleTime to set. */ public void setLowResourceMaxIdleTime(int maxIdleTime) { _lowResourceMaxIdleTime = maxIdleTime; } /* ------------------------------------------------------------ */ /** * @return Returns the soLingerTime. */ public long getSoLingerTime() { return _soLingerTime; } /* ------------------------------------------------------------ */ /** * @return Returns the acceptQueueSize. */ public int getAcceptQueueSize() { return _acceptQueueSize; } /* ------------------------------------------------------------ */ /** * @param acceptQueueSize The acceptQueueSize to set. */ public void setAcceptQueueSize(int acceptQueueSize) { _acceptQueueSize = acceptQueueSize; } /* ------------------------------------------------------------ */ /** * @return Returns the number of acceptor threads. */ public int getAcceptors() { return _acceptors; } /* ------------------------------------------------------------ */ /** * @param acceptors The number of acceptor threads to set. */ public void setAcceptors(int acceptors) { _acceptors = acceptors; } /* ------------------------------------------------------------ */ /** * @param soLingerTime The soLingerTime to set or -1 to disable. */ public void setSoLingerTime(int soLingerTime) { _soLingerTime = soLingerTime; } /* ------------------------------------------------------------ */ protected void doStart() throws Exception { if (_server==null) throw new IllegalStateException("No server"); // open listener port open(); super.doStart(); if (_threadPool==null) _threadPool=_server.getThreadPool(); if (_threadPool!=_server.getThreadPool() && (_threadPool instanceof LifeCycle)) ((LifeCycle)_threadPool).start(); // Start selector thread synchronized(this) { _acceptorThread=new Thread[getAcceptors()]; for (int i=0;i<_acceptorThread.length;i++) { if (!_threadPool.dispatch(new Acceptor(i))) { Log.warn("insufficient maxThreads configured for {}",this); break; } } } Log.info("Started {}",this); } /* ------------------------------------------------------------ */ protected void doStop() throws Exception { try{close();} catch(IOException e) {Log.warn(e);} if (_threadPool==_server.getThreadPool()) _threadPool=null; else if (_threadPool instanceof LifeCycle) ((LifeCycle)_threadPool).stop(); super.doStop(); Thread[] acceptors=null; synchronized(this) { acceptors=_acceptorThread; _acceptorThread=null; } if (acceptors != null) { for (int i=0;i= 0) socket.setSoTimeout(_maxIdleTime); if (_soLingerTime >= 0) socket.setSoLinger(true, _soLingerTime/1000); else socket.setSoLinger(false, 0); } catch (Exception e) { Log.ignore(e); } } /* ------------------------------------------------------------ */ public void customize(EndPoint endpoint, Request request) throws IOException { if (isForwarded()) checkForwardedHeaders(endpoint, request); } /* ------------------------------------------------------------ */ protected void checkForwardedHeaders(EndPoint endpoint, Request request) throws IOException { HttpFields httpFields = request.getConnection().getRequestFields(); // Retrieving headers from the request String forwardedHost = getLeftMostValue(httpFields.getStringField(getForwardedHostHeader())); String forwardedServer = getLeftMostValue(httpFields.getStringField(getForwardedServerHeader())); String forwardedFor = getLeftMostValue(httpFields.getStringField(getForwardedForHeader())); if (_hostHeader!=null) { // Update host header httpFields.put(HttpHeaders.HOST_BUFFER, _hostHeader); request.setServerName(null); request.setServerPort(-1); request.getServerName(); } else if (forwardedHost != null) { // Update host header httpFields.put(HttpHeaders.HOST_BUFFER, forwardedHost); request.setServerName(null); request.setServerPort(-1); request.getServerName(); } if (forwardedServer != null) { // Use provided server name request.setServerName(forwardedServer); } if (forwardedFor != null) { request.setRemoteAddr(forwardedFor); InetAddress inetAddress = null; if (_useDNS) { try { inetAddress = InetAddress.getByName(forwardedFor); } catch (UnknownHostException e) { Log.ignore(e); } } request.setRemoteHost(inetAddress==null?forwardedFor:inetAddress.getHostName()); } } /* ------------------------------------------------------------ */ protected String getLeftMostValue(String headerValue) { if (headerValue == null) return null; int commaIndex = headerValue.indexOf(','); if (commaIndex == -1) { // Single value return headerValue; } // The left-most value is the farthest downstream client return headerValue.substring(0, commaIndex); } /* ------------------------------------------------------------ */ public void persist(EndPoint endpoint) throws IOException { } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* * @see org.mortbay.jetty.Connector#getConfidentialPort() */ public int getConfidentialPort() { return _confidentialPort; } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* * @see org.mortbay.jetty.Connector#getConfidentialScheme() */ public String getConfidentialScheme() { return _confidentialScheme; } /* ------------------------------------------------------------ */ /* * @see org.mortbay.jetty.Connector#isConfidential(org.mortbay.jetty.Request) */ public boolean isIntegral(Request request) { return false; } /* ------------------------------------------------------------ */ /* * @see org.mortbay.jetty.Connector#getConfidentialPort() */ public int getIntegralPort() { return _integralPort; } /* ------------------------------------------------------------ */ /* * @see org.mortbay.jetty.Connector#getIntegralScheme() */ public String getIntegralScheme() { return _integralScheme; } /* ------------------------------------------------------------ */ /* * @see org.mortbay.jetty.Connector#isConfidential(org.mortbay.jetty.Request) */ public boolean isConfidential(Request request) { return false; } /* ------------------------------------------------------------ */ /** * @param confidentialPort The confidentialPort to set. */ public void setConfidentialPort(int confidentialPort) { _confidentialPort = confidentialPort; } /* ------------------------------------------------------------ */ /** * @param confidentialScheme The confidentialScheme to set. */ public void setConfidentialScheme(String confidentialScheme) { _confidentialScheme = confidentialScheme; } /* ------------------------------------------------------------ */ /** * @param integralPort The integralPort to set. */ public void setIntegralPort(int integralPort) { _integralPort = integralPort; } /* ------------------------------------------------------------ */ /** * @param integralScheme The integralScheme to set. */ public void setIntegralScheme(String integralScheme) { _integralScheme = integralScheme; } /* ------------------------------------------------------------ */ protected abstract void accept(int acceptorID) throws IOException, InterruptedException; /* ------------------------------------------------------------ */ public void stopAccept(int acceptorID) throws Exception { } /* ------------------------------------------------------------ */ public boolean getResolveNames() { return _useDNS; } /* ------------------------------------------------------------ */ public void setResolveNames(boolean resolve) { _useDNS=resolve; } /* ------------------------------------------------------------ */ /** * Is reverse proxy handling on? * @return true if this connector is checking the x-forwarded-for/host/server headers */ public boolean isForwarded() { return _forwarded; } /* ------------------------------------------------------------ */ /** * Set reverse proxy handling * @param check true if this connector is checking the x-forwarded-for/host/server headers */ public void setForwarded(boolean check) { if (check) Log.debug(this+" is forwarded"); _forwarded=check; } /* ------------------------------------------------------------ */ public String getHostHeader() { return _hostHeader; } /* ------------------------------------------------------------ */ /** * Set a forced valued for the host header to control what is returned * by {@link ServletRequest#getServerName()} and {@link ServletRequest#getServerPort()}. * This value is only used if {@link #isForwarded()} is true. * @param hostHeader The value of the host header to force. */ public void setHostHeader(String hostHeader) { _hostHeader=hostHeader; } /* ------------------------------------------------------------ */ public String getForwardedHostHeader() { return _forwardedHostHeader; } /* ------------------------------------------------------------ */ /** * @param forwardedHostHeader The header name for forwarded hosts (default x-forwarded-host) */ public void setForwardedHostHeader(String forwardedHostHeader) { _forwardedHostHeader=forwardedHostHeader; } /* ------------------------------------------------------------ */ public String getForwardedServerHeader() { return _forwardedServerHeader; } /* ------------------------------------------------------------ */ /** * @param forwardedHostHeader The header name for forwarded server (default x-forwarded-server) */ public void setForwardedServerHeader(String forwardedServerHeader) { _forwardedServerHeader=forwardedServerHeader; } /* ------------------------------------------------------------ */ public String getForwardedForHeader() { return _forwardedForHeader; } /* ------------------------------------------------------------ */ /** * @param forwardedHostHeader The header name for forwarded for (default x-forwarded-for) */ public void setForwardedForHeader(String forwardedRemoteAddressHeade) { _forwardedForHeader=forwardedRemoteAddressHeade; } /* ------------------------------------------------------------ */ public String toString() { String name = this.getClass().getName(); int dot = name.lastIndexOf('.'); if (dot>0) name=name.substring(dot+1); return name+"@"+(getHost()==null?"0.0.0.0":getHost())+":"+(getLocalPort()<=0?getPort():getLocalPort()); } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ private class Acceptor implements Runnable { int _acceptor=0; Acceptor(int id) { _acceptor=id; } /* ------------------------------------------------------------ */ public void run() { Thread current = Thread.currentThread(); synchronized(AbstractConnector.this) { if (_acceptorThread==null) return; _acceptorThread[_acceptor]=current; } String name =_acceptorThread[_acceptor].getName(); current.setName(name+" - Acceptor"+_acceptor+" "+AbstractConnector.this); int old_priority=current.getPriority(); try { current.setPriority(old_priority-_acceptorPriorityOffset); while (isRunning() && getConnection()!=null) { try { accept(_acceptor); } catch(EofException e) { Log.ignore(e); } catch(IOException e) { Log.ignore(e); } catch(ThreadDeath e) { Log.warn(e); throw e; } catch(Throwable e) { Log.warn(e); } } } finally { current.setPriority(old_priority); current.setName(name); try { if (_acceptor==0) close(); } catch (IOException e) { Log.warn(e); } synchronized(AbstractConnector.this) { if (_acceptorThread!=null) _acceptorThread[_acceptor]=null; } } } } /* ------------------------------------------------------------ */ public String getName() { if (_name==null) _name= (getHost()==null?"0.0.0.0":getHost())+":"+(getLocalPort()<=0?getPort():getLocalPort()); return _name; } /* ------------------------------------------------------------ */ public void setName(String name) { _name = name; } /* ------------------------------------------------------------ */ /** * @return Get the number of requests handled by this context * since last call of statsReset(). If setStatsOn(false) then this * is undefined. */ public int getRequests() {return _requests;} /* ------------------------------------------------------------ */ /** * @return Returns the connectionsDurationMin. */ public long getConnectionsDurationMin() { return _connectionsDurationMin; } /* ------------------------------------------------------------ */ /** * @return Returns the connectionsDurationTotal. */ public long getConnectionsDurationTotal() { return _connectionsDurationTotal; } /* ------------------------------------------------------------ */ /** * @return Returns the connectionsOpenMin. */ public int getConnectionsOpenMin() { return _connectionsOpenMin; } /* ------------------------------------------------------------ */ /** * @return Returns the connectionsRequestsMin. */ public int getConnectionsRequestsMin() { return _connectionsRequestsMin; } /* ------------------------------------------------------------ */ /** * @return Number of connections accepted by the server since * statsReset() called. Undefined if setStatsOn(false). */ public int getConnections() {return _connections;} /* ------------------------------------------------------------ */ /** * @return Number of connections currently open that were opened * since statsReset() called. Undefined if setStatsOn(false). */ public int getConnectionsOpen() {return _connectionsOpen;} /* ------------------------------------------------------------ */ /** * @return Maximum number of connections opened simultaneously * since statsReset() called. Undefined if setStatsOn(false). */ public int getConnectionsOpenMax() {return _connectionsOpenMax;} /* ------------------------------------------------------------ */ /** * @return Average duration in milliseconds of open connections * since statsReset() called. Undefined if setStatsOn(false). */ public long getConnectionsDurationAve() {return _connections==0?0:(_connectionsDurationTotal/_connections);} /* ------------------------------------------------------------ */ /** * @return Maximum duration in milliseconds of an open connection * since statsReset() called. Undefined if setStatsOn(false). */ public long getConnectionsDurationMax() {return _connectionsDurationMax;} /* ------------------------------------------------------------ */ /** * @return Average number of requests per connection * since statsReset() called. Undefined if setStatsOn(false). */ public int getConnectionsRequestsAve() {return _connections==0?0:(_requests/_connections);} /* ------------------------------------------------------------ */ /** * @return Maximum number of requests per connection * since statsReset() called. Undefined if setStatsOn(false). */ public int getConnectionsRequestsMax() {return _connectionsRequestsMax;} /* ------------------------------------------------------------ */ /** Reset statistics. */ public void statsReset() { _statsStartedAt=_statsStartedAt==-1?-1:System.currentTimeMillis(); _connections=0; _connectionsOpenMin=_connectionsOpen; _connectionsOpenMax=_connectionsOpen; _connectionsOpen=0; _connectionsDurationMin=0; _connectionsDurationMax=0; _connectionsDurationTotal=0; _requests=0; _connectionsRequestsMin=0; _connectionsRequestsMax=0; } /* ------------------------------------------------------------ */ public void setStatsOn(boolean on) { if (on && _statsStartedAt!=-1) return; Log.debug("Statistics on = "+on+" for "+this); statsReset(); _statsStartedAt=on?System.currentTimeMillis():-1; } /* ------------------------------------------------------------ */ /** * @return True if statistics collection is turned on. */ public boolean getStatsOn() { return _statsStartedAt!=-1; } /* ------------------------------------------------------------ */ /** * @return Timestamp stats were started at. */ public long getStatsOnMs() { return (_statsStartedAt!=-1)?(System.currentTimeMillis()-_statsStartedAt):0; } /* ------------------------------------------------------------ */ protected void connectionOpened(HttpConnection connection) { if (_statsStartedAt==-1) return; synchronized(_statsLock) { _connectionsOpen++; if (_connectionsOpen > _connectionsOpenMax) _connectionsOpenMax=_connectionsOpen; } } /* ------------------------------------------------------------ */ protected void connectionClosed(HttpConnection connection) { if (_statsStartedAt>=0) { long duration=System.currentTimeMillis()-connection.getTimeStamp(); int requests=connection.getRequests(); synchronized(_statsLock) { _requests+=requests; _connections++; _connectionsOpen--; _connectionsDurationTotal+=duration; if (_connectionsOpen<0) _connectionsOpen=0; if (_connectionsOpen<_connectionsOpenMin) _connectionsOpenMin=_connectionsOpen; if (_connectionsDurationMin==0 || duration<_connectionsDurationMin) _connectionsDurationMin=duration; if (duration>_connectionsDurationMax) _connectionsDurationMax=duration; if (_connectionsRequestsMin==0 || requests<_connectionsRequestsMin) _connectionsRequestsMin=requests; if (requests>_connectionsRequestsMax) _connectionsRequestsMax=requests; } } connection.destroy(); } /* ------------------------------------------------------------ */ /** * @return the acceptorPriority */ public int getAcceptorPriorityOffset() { return _acceptorPriorityOffset; } /* ------------------------------------------------------------ */ /** * Set the priority offset of the acceptor threads. The priority is adjusted by * this amount (default 0) to either favour the acceptance of new threads and newly active * connections or to favour the handling of already dispatched connections. * @param offset the amount to alter the priority of the acceptor threads. */ public void setAcceptorPriorityOffset(int offset) { _acceptorPriorityOffset=offset; } /* ------------------------------------------------------------ */ /** * @return True if the the server socket will be opened in SO_REUSEADDR mode. */ public boolean getReuseAddress() { return _reuseAddress; } /* ------------------------------------------------------------ */ /** * @param reuseAddress True if the the server socket will be opened in SO_REUSEADDR mode. */ public void setReuseAddress(boolean reuseAddress) { _reuseAddress=reuseAddress; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy