// ========================================================================
// $Id: AJP13Listener.java,v 1.20 2006/10/08 14:13:05 gregwilkins Exp $
// Copyright 2002-2004 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 net.lightbody.bmp.proxy.jetty.http.ajp;
import net.lightbody.bmp.proxy.jetty.http.*;
import net.lightbody.bmp.proxy.jetty.log.LogFactory;
import net.lightbody.bmp.proxy.jetty.util.InetAddrPort;
import net.lightbody.bmp.proxy.jetty.util.ThreadedServer;
import org.apache.commons.logging.Log;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
/* ------------------------------------------------------------ */
/**
* AJP 1.3 Protocol Listener. This listener takes requests from the mod_jk or
* mod_jk2 modules used by web servers such as apache and IIS to forward
* requests to a servlet container.
* <p>
* This code uses the AJP13 code from tomcat3.3 as the protocol specification,
* but is new implementation.
*
* @version $Id: AJP13Listener.java,v 1.20 2006/10/08 14:13:05 gregwilkins Exp $
* @author Greg Wilkins (gregw)
*/
public class AJP13Listener extends ThreadedServer implements HttpListener
{
private static Log log=LogFactory.getLog(AJP13Listener.class);
/* ------------------------------------------------------------------- */
private HttpServer _server;
private boolean _lastOut=false;
private boolean _lastLow=false;
private String _integralScheme=HttpMessage.__SSL_SCHEME;
private String _confidentialScheme=HttpMessage.__SSL_SCHEME;
private int _integralPort=0;
private int _confidentialPort=0;
private boolean _identifyListener=false;
private int _bufferSize=8192;
private int _bufferReserve=512;
private String[] _remoteServers;
private HttpHandler _handler;
/* ------------------------------------------------------------------- */
public AJP13Listener()
{
}
/* ------------------------------------------------------------------- */
public AJP13Listener(InetAddrPort address)
{
super(address);
}
/* ------------------------------------------------------------ */
public void setHttpServer(HttpServer server)
{
_server=server;
}
/* ------------------------------------------------------------ */
public HttpServer getHttpServer()
{
return _server;
}
/* ------------------------------------------------------------ */
public int getBufferSize()
{
return _bufferSize;
}
/* ------------------------------------------------------------ */
public void setBufferSize(int size)
{
_bufferSize=size;
if (_bufferSize>8192)
log.warn("AJP Data buffer > 8192: "+size);
}
/* ------------------------------------------------------------ */
public int getBufferReserve()
{
return _bufferReserve;
}
/* ------------------------------------------------------------ */
public void setBufferReserve(int size)
{
_bufferReserve=size;
}
/* ------------------------------------------------------------ */
public boolean getIdentifyListener()
{
return _identifyListener;
}
/* ------------------------------------------------------------ */
/**
* @param identifyListener
* If true, the listener name is added to all requests as the
* org.mortbay.http.HttListener attribute
*/
public void setIdentifyListener(boolean identifyListener)
{
_identifyListener=identifyListener;
}
/* --------------------------------------------------------------- */
public String getDefaultScheme()
{
return HttpMessage.__SCHEME;
}
/* --------------------------------------------------------------- */
public void start() throws Exception
{
super.start();
log.info("Started AJP13Listener on "+getInetAddrPort());
log.info("NOTICE: AJP13 is not a secure protocol. Please protect the port "+getInetAddrPort());
}
/* --------------------------------------------------------------- */
public void stop() throws InterruptedException
{
super.stop();
log.info("Stopped AJP13Listener on "+getInetAddrPort());
}
/* ------------------------------------------------------------ */
/**
* @return Array of accepted remote server hostnames or IPs.
*/
public String[] getRemoteServers()
{
return _remoteServers;
}
/* ------------------------------------------------------------ */
/**
* Set accepted remote servers. The AJP13 protocol is not secure and
* contains no authentication. If remote servers are set, then this listener
* will only accept connections from hosts with matching addresses or
* hostnames.
*
* @param servers
* Array of accepted remote server hostnames or IPs
*/
public void setRemoteServers(String[] servers)
{
_remoteServers=servers;
}
/* ------------------------------------------------------------ */
/**
* Handle Job. Implementation of ThreadPool.handle(), calls
* handleConnection.
*
* @param socket
* A Connection.
*/
public void handleConnection(Socket socket) throws IOException
{
// Check acceptable remote servers
if (_remoteServers!=null&&_remoteServers.length>0)
{
boolean match=false;
InetAddress inetAddress=socket.getInetAddress();
String hostAddr=inetAddress.getHostAddress();
String hostName=inetAddress.getHostName();
for (int i=0; i<_remoteServers.length; i++)
{
if (hostName.equals(_remoteServers[i])||hostAddr.equals(_remoteServers[i]))
{
match=true;
break;
}
}
if (!match)
{
log.warn("AJP13 Connection from un-approved host: "+inetAddress);
return;
}
}
// Handle the connection
socket.setTcpNoDelay(true);
socket.setSoTimeout(getMaxIdleTimeMs());
AJP13Connection connection=createConnection(socket);
try
{
connection.handle();
}
finally
{
connection.destroy();
}
}
/* ------------------------------------------------------------ */
/**
* Create an AJP13Connection instance. This method can be used to override
* the connection instance.
*
* @param socket
* The underlying socket.
*/
protected AJP13Connection createConnection(Socket socket) throws IOException
{
return new AJP13Connection(this,socket.getInputStream(),socket.getOutputStream(),socket,getBufferSize());
}
/* ------------------------------------------------------------ */
/**
* Customize the request from connection. This method extracts the socket
* from the connection and calls the customizeRequest(Socket,HttpRequest)
* method.
*
* @param request
*/
public void customizeRequest(HttpConnection connection, HttpRequest request)
{
if (_identifyListener)
request.setAttribute(HttpListener.ATTRIBUTE,getName());
Socket socket=(Socket)(connection.getConnection());
customizeRequest(socket,request);
}
/* ------------------------------------------------------------ */
/**
* Customize request from socket. Derived versions of SocketListener may
* specialize this method to customize the request with attributes of the
* socket used (eg SSL session ids).
*
* @param request
*/
protected void customizeRequest(Socket socket, HttpRequest request)
{
}
/* ------------------------------------------------------------ */
/**
* Persist the connection.
*
* @param connection
*/
public void persistConnection(HttpConnection connection)
{
}
/* ------------------------------------------------------------ */
/**
* @return True if low on idle threads.
*/
public boolean isLowOnResources()
{
boolean low=getThreads()==getMaxThreads()&&getIdleThreads()<getMinThreads();
if (low&&!_lastLow)
log.info("LOW ON THREADS: "+this);
else if (!low&&_lastLow)
{
log.info("OK on threads: "+this);
_lastOut=false;
}
_lastLow=low;
return low;
}
/* ------------------------------------------------------------ */
/**
* @return True if out of resources.
*/
public boolean isOutOfResources()
{
boolean out=getThreads()==getMaxThreads()&&getIdleThreads()==0;
if (out&&!_lastOut)
log.warn("OUT OF THREADS: "+this);
_lastOut=out;
return out;
}
/* ------------------------------------------------------------ */
public boolean isIntegral(HttpConnection connection)
{
return ((AJP13Connection)connection).isSSL();
}
/* ------------------------------------------------------------ */
public boolean isConfidential(HttpConnection connection)
{
return ((AJP13Connection)connection).isSSL();
}
/* ------------------------------------------------------------ */
public String getIntegralScheme()
{
return _integralScheme;
}
/* ------------------------------------------------------------ */
public void setIntegralScheme(String integralScheme)
{
_integralScheme=integralScheme;
}
/* ------------------------------------------------------------ */
public int getIntegralPort()
{
return _integralPort;
}
/* ------------------------------------------------------------ */
public void setIntegralPort(int integralPort)
{
_integralPort=integralPort;
}
/* ------------------------------------------------------------ */
public String getConfidentialScheme()
{
return _confidentialScheme;
}
/* ------------------------------------------------------------ */
public void setConfidentialScheme(String confidentialScheme)
{
_confidentialScheme=confidentialScheme;
}
/* ------------------------------------------------------------ */
public int getConfidentialPort()
{
return _confidentialPort;
}
/* ------------------------------------------------------------ */
public void setConfidentialPort(int confidentialPort)
{
_confidentialPort=confidentialPort;
}
/* ------------------------------------------------------------ */
public HttpHandler getHttpHandler()
{
return _handler;
}
/* ------------------------------------------------------------ */
public void setHttpHandler(HttpHandler handler)
{
_handler=handler;
}
}