|
What this is
Other links
The source code
/*
* Copyright 1999-2004 The Apache Sofware Foundation.
*
* 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.apache.tomcat.modules.server;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Locale;
import org.apache.tomcat.core.Context;
import org.apache.tomcat.core.ContextManager;
import org.apache.tomcat.core.Request;
import org.apache.tomcat.core.Response;
import org.apache.tomcat.util.buf.DateTool;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.http.HttpMessages;
import org.apache.tomcat.util.log.Log;
import org.apache.tomcat.util.net.SSLSupport;
import org.apache.tomcat.util.net.TcpConnection;
import org.apache.tomcat.util.net.TcpConnectionHandler;
/** Standalone http.
*
* Connector properties:
* - secure - will load a SSL socket factory and act as https server
*
* Properties passed to the net layer:
* - timeout
* - backlog
* - address
* - port
* Thread pool properties:
* - minSpareThreads
* - maxSpareThreads
* - maxThreads
* - poolOn
* Properties for HTTPS:
* - keystore - certificates - default to ~/.keystore
* - keypass - password
* - clientauth - true if the server should authenticate the client using certs
* Properties for HTTP:
* - reportedname - name of server sent back to browser (security purposes)
*/
public class Http10Interceptor extends PoolTcpConnector
implements TcpConnectionHandler
{
private int timeout = 300000; // 5 minutes as in Apache HTTPD server
private String reportedname;
private int socketCloseDelay=-1;
public Http10Interceptor() {
super();
super.setSoLinger( 100 );
// defaults:
this.setPort( 8080 );
}
// -------------------- PoolTcpConnector --------------------
protected void localInit() throws Exception {
ep.setConnectionHandler( this );
}
// -------------------- Attributes --------------------
public void setTimeout( int timeouts ) {
timeout = timeouts * 1000;
}
public void setReportedname( String reportedName) {
reportedname = reportedName;
}
public void setSocketCloseDelay( int d ) {
socketCloseDelay=d;
}
public void setProperty( String prop, String value ) {
setAttribute( prop, value );
}
// -------------------- Handler implementation --------------------
public void setServer( Object o ) {
this.cm=(ContextManager)o;
}
public Object[] init() {
Object thData[]=new Object[3];
HttpRequest reqA=new HttpRequest();
HttpResponse resA=new HttpResponse();
if (reportedname != null)
resA.setReported(reportedname);
cm.initRequest( reqA, resA );
thData[0]=reqA;
thData[1]=resA;
thData[2]=null;
return thData;
}
public void processConnection(TcpConnection connection, Object thData[]) {
Socket socket=null;
HttpRequest reqA=null;
HttpResponse resA=null;
try {
reqA=(HttpRequest)thData[0];
resA=(HttpResponse)thData[1];
socket=connection.getSocket();
socket.setSoTimeout(timeout);
reqA.setSocket( socket );
resA.setSocket( socket );
reqA.readNextRequest(resA);
if( secure ) {
reqA.scheme().setString( "https" );
// Load up the SSLSupport class
if(sslImplementation != null)
reqA.setSSLSupport(sslImplementation.getSSLSupport(socket));
}
cm.service( reqA, resA );
// If unread input arrives after the shutdownInput() call
// below and before or during the socket.close(), an error
// may be reported to the client. To help troubleshoot this
// type of error, provide a configurable delay to give the
// unread input time to arrive so it can be successfully read
// and discarded by shutdownInput().
if( socketCloseDelay >= 0 ) {
try {
Thread.sleep(socketCloseDelay);
} catch (InterruptedException ie) { /* ignore */ }
}
// XXX didn't honor HTTP/1.0 KeepAlive, should be fixed
TcpConnection.shutdownInput( socket );
}
catch(java.net.SocketException e) {
// SocketExceptions are normal
log( "SocketException reading request, ignored", null,
Log.INFORMATION);
log( "SocketException reading request:", e, Log.DEBUG);
}
catch (java.io.InterruptedIOException ioe) {
// We have armed a timeout on read as does apache httpd server.
// Just to avoid staying with inactive connection
// BUG#1006
ioe.printStackTrace();
log( "Timeout reading request, aborting", ioe, Log.ERROR);
}
catch (java.io.IOException e) {
// IOExceptions are normal
log( "IOException reading request, ignored", null,
Log.INFORMATION);
log( "IOException reading request:", e, Log.DEBUG);
}
// Future developers: if you discover any other
// rare-but-nonfatal exceptions, catch them here, and log as
// above.
catch (Throwable e) {
// any other exception or error is odd. Here we log it
// with "ERROR" level, so it will show up even on
// less-than-verbose logs.
e.printStackTrace();
log( "Error reading request, ignored", e, Log.ERROR);
}
finally {
// recycle kernel sockets ASAP
// XXX didn't honor HTTP/1.0 KeepAlive, should be fixed
try { if (socket != null) socket.close (); }
catch (IOException e) { /* ignore */ }
}
}
/** Internal constants for getInfo */
private static final int GET_OTHER = 0;
private static final int GET_CIPHER_SUITE = 1;
private static final int GET_PEER_CERTIFICATE_CHAIN = 2;
/**
getInfo calls for SSL data
@return the requested data
*/
public Object getInfo( Context ctx, Request request,
int id, String key ) {
// The following code explicitly assumes that the only
// attributes hand;ed here are HTTP. If you change that
// you MUST change the test for sslSupport==null --EKR
if (key != null) {
int infoRequested = GET_OTHER;
if(key.equals("javax.servlet.request.cipher_suite"))
infoRequested = GET_CIPHER_SUITE;
else if(key.equals("javax.servlet.request.X509Certificate"))
infoRequested = GET_PEER_CERTIFICATE_CHAIN;
if(infoRequested != GET_OTHER) {
HttpRequest httpReq;
try {
httpReq=(HttpRequest)request;
} catch (ClassCastException e){
return null;
}
if (httpReq!=null && httpReq.sslSupport!=null){
try {
switch (infoRequested) {
case GET_CIPHER_SUITE:
return httpReq.sslSupport.getCipherSuite();
case GET_PEER_CERTIFICATE_CHAIN:
return httpReq.sslSupport.getPeerCertificateChain();
}
} catch (Exception e){
log("Exception getting SSL attribute " + key,e,Log.WARNING);
return null;
}
} // if req != null
} // if asking for ssl attribute
} // if key != null
return super.getInfo(ctx,request,id,key);
} // getInfo
}
class HttpRequest extends Request {
Http10 http=new Http10();
private boolean moreRequests = false;
Socket socket;
SSLSupport sslSupport=null;
public HttpRequest() {
super();
// recycle these to remove the defaults
remoteAddrMB.recycle();
remoteHostMB.recycle();
}
public void recycle() {
super.recycle();
if( http!=null) http.recycle();
// recycle these to remove the defaults
remoteAddrMB.recycle();
remoteHostMB.recycle();
sslSupport=null;
}
public void setSocket(Socket socket) throws IOException {
http.setSocket( socket );
this.socket=socket;
}
public Socket getSocket() {
return socket;
}
public int doRead() throws IOException {
if( available == 0 )
return -1;
// #3745
// if available == -1: unknown length, we'll read until end of stream.
if( available!= -1 )
available--;
return http.doRead();
}
public int doRead(byte[] b, int off, int len) throws IOException {
if( available == 0 )
return -1;
// if available == -1: unknown length, we'll read until end of stream.
int rd=http.doRead( b, off, len );
if( rd==-1) {
available=0;
return -1;
}
if( available!= -1 )
available -= rd;
return rd;
}
public void readNextRequest(Response response) throws IOException {
int status=http.processRequestLine( methodMB, uriMB,queryMB, protoMB );
// XXX remove this after we swich to MB
// method=methodMB.toString();
// requestURI=uriMB.toString();
// queryString=queryMB.toString();
// protocol=protoMB.toString();
if( status > 200 ) {
response.setStatus( status );
return;
}
// for 0.9, we don't have headers!
if (! protoMB.equals("")) {
// all HTTP versions with protocol also have headers
// ( 0.9 has no HTTP/0.9 !)
status=http.readHeaders( headers );
if( status >200 ) {
response.setStatus( status );
return;
}
}
// XXX detect for real whether or not we have more requests
// coming
moreRequests = false;
}
// -------------------- override special methods
public MessageBytes remoteAddr() {
// WARNING: On some linux configurations, this call may get you in
// trubles... Big trubles...
if( remoteAddrMB.isNull() ) {
remoteAddrMB.setString(socket.getInetAddress().getHostAddress());
}
return remoteAddrMB;
}
public MessageBytes remoteHost() {
if( remoteHostMB.isNull() ) {
remoteHostMB.setString( socket.getInetAddress().getHostName() );
}
return remoteHostMB;
}
public String getLocalHost() {
InetAddress localAddress = socket.getLocalAddress();
localHost = localAddress.getHostName();
return localHost;
}
public MessageBytes serverName(){
if(! serverNameMB.isNull()) return serverNameMB;
parseHostHeader();
return serverNameMB;
}
public int getServerPort(){
if(serverPort!=-1) return serverPort;
parseHostHeader();
return serverPort;
}
protected void parseHostHeader() {
MessageBytes hH=getMimeHeaders().getValue("host");
if (sslSupport != null){
serverPort = 443;
} else {
serverPort = 80;
}
if (hH != null) {
// XXX use MessageBytes
String hostHeader = hH.toString();
int i = hostHeader.indexOf(':');
if (i > -1) {
serverNameMB.setString( hostHeader.substring(0,i));
hostHeader = hostHeader.substring(i+1);
try{
serverPort=Integer.parseInt(hostHeader);
}catch(NumberFormatException nfe){
}
}else serverNameMB.setString( hostHeader);
return;
}
serverPort = socket.getLocalPort();
if( localHost != null ) {
serverNameMB.setString( localHost );
}
// default to localhost - and warn
// log("No server name, defaulting to localhost");
serverNameMB.setString( getLocalHost() );
}
void setSSLSupport(SSLSupport s){
sslSupport=s;
}
}
class HttpResponse extends Response {
Http10 http;
String reportedname;
DateFormat dateFormat;
public HttpResponse() {
super();
}
public void init() {
super.init();
dateFormat=new SimpleDateFormat(DateTool.RFC1123_PATTERN,
Locale.US);
dateFormat.setTimeZone(DateTool.GMT_ZONE);
}
public void setSocket( Socket s ) {
http=((HttpRequest)request).http;
}
public void recycle() {
super.recycle();
}
public void setReported(String reported) {
reportedname = reported;
}
public void endHeaders() throws IOException {
super.endHeaders();
if(request.protocol().isNull() ||
request.protocol().equals("") ) // HTTP/0.9
return;
http.sendStatus( status, HttpMessages.getMessage( status ));
// Check if a Date is to be added
MessageBytes dateH=getMimeHeaders().getValue("Date");
if( dateH == null ) {
// no date header set by user
MessageBytes dateHeader=getMimeHeaders().setValue( "Date" );
dateHeader.setTime( System.currentTimeMillis(), dateFormat);
}
// return server name (or the reported one)
if (reportedname == null) {
Context ctx = request.getContext();
String server = ctx != null ? ctx.getEngineHeader() :
ContextManager.TOMCAT_NAME + "/" + ContextManager.TOMCAT_VERSION;
getMimeHeaders().setValue( "Server" ).setString(server);
} else {
if (reportedname.length() != 0)
getMimeHeaders().setValue( "Server" ).setString(reportedname);
}
http.sendHeaders( getMimeHeaders() );
}
public void doWrite( byte buffer[], int pos, int count)
throws IOException
{
http.doWrite( buffer, pos, count);
}
}
|
| ... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.