Package org.jvnet.glassfish.comms.clb.proxy

Source Code of org.jvnet.glassfish.comms.clb.proxy.ProxyRequestHandler

/*
*  Copyright 1999-2004 The Apache Software 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.
*/
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) Ericsson AB, 2004-2008. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code.  If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.jvnet.glassfish.comms.clb.proxy;

import com.sun.grizzly.ConnectorHandler;
import com.sun.grizzly.ssl.SSLOutputBuffer;
import com.sun.enterprise.web.connector.grizzly.ssl.SSLByteBufferInputStream;
import com.sun.grizzly.http.Constants;
import com.sun.grizzly.http.SocketChannelOutputBuffer;
import com.sun.grizzly.tcp.Request;
import com.sun.grizzly.tcp.Response;
import com.sun.grizzly.util.ByteBufferInputStream;
import com.sun.grizzly.util.buf.Ascii;
import com.sun.grizzly.util.buf.ByteChunk;
import com.sun.grizzly.util.buf.MessageBytes;
import com.sun.grizzly.util.http.MimeHeaders;
import com.sun.grizzly.util.net.SSLSupport;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.logging.Level;

import java.util.logging.Logger;
import javax.net.ssl.SSLEngine;
import org.jvnet.glassfish.comms.clb.proxy.api.Endpoint;
import org.jvnet.glassfish.comms.clb.proxy.config.LoadBalancerProxyConstants;
import org.jvnet.glassfish.comms.clb.proxy.config.ProxyConfig;
import org.jvnet.glassfish.comms.clb.proxy.http.util.HttpInputBuffer;
import org.jvnet.glassfish.comms.clb.proxy.http.util.HttpRequest;

import java.security.cert.Certificate;
import javax.net.ssl.SSLSession;
import org.apache.catalina.util.Base64;

/**
* This class encapsulates the pluggable framework invocation.
* Implementation has some code that has been used from Apache.
* The inputs to the task are the channel and the byte buffer that has
* been read. The task invokes all the HttpLayers that have been configured
* in the dispatcher.xml, any functionality that has to be plugged in
* has to be done through dispatcher.xml
*
* @author
*/
public class ProxyRequestHandler {
   
    /**
     * Byte buffer that has been read from the client channel
     */
    protected ByteBuffer buffer;
    /**
     * Client socket channel
     */
    protected SocketChannel clientChannel;
    /**
     * Http request interface, extended from grizzly tcp request interface.
     */
    protected HttpRequest request;
   
    /**
     * Http response interface, this is a grizzly tcp response interface.
     */
    protected Response response;
   
    /**
     * Extended from the grizzly inter
     */
    protected HttpInputBuffer inputBuffer;
   
    protected SocketChannelOutputBuffer outputBuffer;
   
    protected SelectionKey clientKey;
   
    protected SelectionKey serverKey;
   
    protected Logger _logger;
   
    protected InputStream inputStream;
    /**
     * Holds value of property bytesWritten.
     */
    protected long bytesWritten =0;
   
    protected long payloadlength = 0;
   
    protected boolean recycle = true;
   
    protected boolean initialized = false;
   
    protected boolean keepAlive = false;
   
    protected SSLEngine sslEngine;
   
    protected ByteBuffer sslInputBB;
   
    protected ByteBuffer sslOutputBB;
   
    protected ConnectorHandler connectorHandler;
   
    protected boolean transferencoding = false;
   
    protected boolean hasRemaining = false;
   
    protected boolean error = false;      
    /**
     * Creates a new instance of ProxyRequestHandler
     */
    public ProxyRequestHandler(){
        this(false);
    }
   
    public ProxyRequestHandler(boolean secure) {
        _logger = ProxyConfig.getInstance().getLogger();
        initialize(secure);
    }

    public void initialize(boolean secure){
        request  = new HttpRequest();
        inputBuffer = new HttpInputBuffer(request);
        inputStream = secure ? new SSLByteBufferInputStream()
        : new ByteBufferInputStream();
       
        inputBuffer.setInputStream(inputStream);
        request.setInputBuffer(inputBuffer);
        response = new Response();
        outputBuffer = secure ? new SSLOutputBuffer(response,
                LoadBalancerProxyConstants.DEFAULT_HTTP_HEADER_BUFFER_SIZE,
                false):
            new SocketChannelOutputBuffer
                (response,
                LoadBalancerProxyConstants.DEFAULT_HTTP_HEADER_BUFFER_SIZE,
                false);
        response.setOutputBuffer(outputBuffer);
        request.setResponse(response);
        initialized = true;
    }
   
    public void setByteBuffer(ByteBuffer buffer){
        this.buffer = buffer;
    }
   
    public void setChannel(SocketChannel channel){
        clientChannel = channel;
    }
   
    public SelectionKey getSelectionKey(){
        return clientKey;
    }
   
    public void setSelectionKey(SelectionKey key){
        clientKey = key;
        if (key != null){
            clientChannel = (SocketChannel)key.channel();
            request.setSelectionKey(key);
            outputBuffer.setChannel(clientChannel);
        }
    }
   
    public SelectionKey getServerSelectionKey(){
        return serverKey;
    }
   
    public void setServerSelectionKey(SelectionKey key){
        serverKey = key;
    }
   
   
    public void setConnectorHandler(ConnectorHandler handler){
        connectorHandler = handler;
    }
   
    public ConnectorHandler getConnectorHandler(){
        return connectorHandler;
    }
   
    public void recycle() {
        inputBuffer.nextRequest();
        outputBuffer.nextRequest();
        request.recycle();
        response.recycle();
        payloadlength = 0;
        bytesWritten = 0;
        clientChannel = null;
        buffer = null;
        clientKey = null;
        serverKey = null;
        transferencoding = false;
        connectorHandler = null;
        error = false;
    }
   
   
    public void doTask(){
        if ((buffer == null) || (clientChannel == null)){
            _logger.log(Level.SEVERE,"clb.proxy.task_null_buffer_channel");
            return;
        }
        if (_logger.isLoggable(Level.FINEST)){
            _logger.log(Level.FINEST,"clb.proxy.requesthandler.start");
        }
        process();
        if (_logger.isLoggable(Level.FINEST)){
            _logger.log(Level.FINEST,"clb.proxy.requesthandler.finished_process");
        }
        Endpoint endpoint = getEndpoint();
        if( endpoint != null && !endpoint.isLocal())
            handleRemoteTermination();
        postProcess();
        if (_logger.isLoggable(Level.FINEST)){
            _logger.log(Level.FINEST,"clb.proxy.requesthandler.finished_postprocess");
        }
    }
   
    public void process(){
        buffer.flip();
        if (isSecure()) {
            ((SSLByteBufferInputStream)inputStream).setByteBuffer(buffer);
            ((SSLByteBufferInputStream)inputStream).setSelectionKey(clientKey);
            ((SSLByteBufferInputStream)inputStream).setReadTimeout(
                    ProxyConfig.getInstance().getReadTimeOutInt());
            if (_logger.isLoggable(Level.FINEST)){
                _logger.log(Level.FINEST,"clb.proxy.requesthandler.ssl_stream");
            }
        } else {
            ((ByteBufferInputStream)inputStream).setByteBuffer(buffer);
            ((ByteBufferInputStream)inputStream).setSelectionKey(clientKey);
            ((ByteBufferInputStream)inputStream).setReadTimeout(
                    ProxyConfig.getInstance().getReadTimeOutInt());
        }
       
        /** Try catch exception and check for failures
         */
        HttpProxy.getInstance().getLayerHandler().doInvoke(request, response);
        /* The above API can return under 2 conditions.
         * 1. With status 200, which means all layers went through
         * and we have to query the Endpoint to remote this request
         * 2. With status NON 200, means that this is an error response
         * and needs to be returned to the HTTP client.
         */
    }
   
    public boolean getKeepAlive(){
        return keepAlive;
    }
   
    public void setKeepAlive(boolean keepalive){
        keepAlive = keepalive;
    }
   
    public Endpoint getEndpoint(){
        return request.getConvergedLoadBalancerEndpoint();
    }
   
    private void reallocateBuffer() {
        buffer = ByteBuffer.allocate(inputBuffer.lastValidPos());
    }
   
    //Method to add required proxy headers when request is being
    //proxied to remote instance
    private void addProxyHeaders() {
        /**
         *  Do we need a re-handshake here ?
         *  TODO !
         */
        if (sslEngine != null) {
           
            SSLSession sslSession = sslEngine.getSession();
           
            //Adding cipher key size as proxy header
            String keySize = getKeySize(sslSession);
            if(keySize != null){
                request.addHeader(LoadBalancerProxyConstants.
                        PROXY_KEYSIZE, keySize);
            }
           
            //Adding client certificate chain as proxy headers          
            Certificate[] certs = null;
            try {
                certs = sslSession.getPeerCertificates();
                if (certs != null){
                    for (Certificate cert: certs){
                        String clientCert =
                                new String(Base64.encode(cert.getEncoded()));
                        request.addHeader(LoadBalancerProxyConstants.CERT_HEADER,
                                clientCert);
                    }
                }
            } catch( Throwable t ) {
                if ( _logger.isLoggable(Level.FINEST))
                    _logger.log(Level.FINEST,"Error getting client certs");
            }
        }
       
        //Adding proxy header for client ip address
        request.addHeader(LoadBalancerProxyConstants.PROXY_CLIENT_IP,
                "" + request.remoteAddr().toString());
       
        //Adding proxy header for client port
        request.addHeader(LoadBalancerProxyConstants.PROXY_CLIENT_PORT,
                "" + request.getRemotePort());
       
        //Adding proxy header for proxy local ip address
        request.addHeader(LoadBalancerProxyConstants.PROXY_LOCAL_IP,
                request.localAddr().toString());
       
        //Adding proxy header for proxy local port
        request.addHeader(LoadBalancerProxyConstants.PROXY_LOCAL_PORT,
                "" + request.getLocalPort());
    }
    //Method to get key size from cipher suite
    private String getKeySize(SSLSession sslSession) {
   
        //Code to get key size picked from
        //glassfish/appserv-webtier/src/java/org/apache/tomcat/util/net/jsse/JSSESupport.java
        Integer keySize = (Integer) sslSession
                .getValue(SSLSupport.KEY_SIZE_KEY);
       
        if(keySize != null)
            return keySize.toString();
       
        SSLSupport.CipherData c_aux[] = SSLSupport.ciphers;
        String cipherSuite = sslSession.getCipherSuite();

        for (int i = 0; i < c_aux.length; i++) {
            if (cipherSuite.indexOf(c_aux[i].phrase) > -1) {
                return "" + c_aux[i].keySize;
            }
        }
       
        return null;
    }
   
    public void postProcess(){
       
        buffer.clear();
        if (inputBuffer.lastValidPos() > buffer.capacity()){
            reallocateBuffer();
        }
        buffer.put(inputBuffer.getBytes(), buffer.position(),
                inputBuffer.lastValidPos());
    }
   
    private void handleRemoteTermination(){
       
        addProxyHeaders();
       
        String transferencodingname  =
                request.getHeader(Constants.TRANSFERENCODING);
        if ((transferencodingname != null) &&
                (transferencodingname.trim().equals(Constants.CHUNKED))) {
            transferencoding = true;
            int ret = findBytes(inputBuffer.getBytes(),
                    LoadBalancerProxyConstants.END_BYTES, 0, inputBuffer.getBytes().length);
            hasRemaining = (ret < 0);
        }
       
        long contentlength = (request.getContentLengthLong() < 0) ? 0 :
            request.getContentLengthLong();
        payloadlength = contentlength +
                inputBuffer.getHeaderLength();
        if (_logger.isLoggable(Level.FINE)){
            _logger.log(Level.FINE, "clb.proxy.requesthandler.content_length",
                    contentlength);
            _logger.log(Level.FINE, "clb.proxy.requesthandler.payload_length",
                    payloadlength);
        }
        MessageBytes protocolMB = request.protocol();
        if (protocolMB.equals(Constants.HTTP_11)) {
            keepAlive = true;
        } else if (protocolMB.equals(Constants.HTTP_10)) {
            keepAlive = false;
        } else if (protocolMB.equals("")) {
            // HTTP/0.9
            keepAlive = false;
        } else {
            if (_logger.isLoggable(Level.FINE)){
                _logger.log(Level.FINE, "clb.proxy.requesthandler.set_error_response");
            }
            response.setStatus(505);
        }
       
        MimeHeaders headers = request.getMimeHeaders();
        // Check connection header
        MessageBytes connectionValueMB = headers.getValue(
                LoadBalancerProxyConstants.HTTP_CONNECTION_HEADER);
        if (connectionValueMB != null) {
            ByteChunk connectionValueBC = connectionValueMB.getByteChunk();
            if (findBytes(connectionValueBC, Constants.CLOSE_BYTES) != -1) {
                keepAlive = false;
            } else if (findBytes(connectionValueBC,
                    Constants.KEEPALIVE_BYTES) != -1) {
                keepAlive = true;
            }
        }
       
    }
   
    public void setSecure(boolean secure){
        request.setSecure(secure);
    }
   
    public boolean isSecure(){
        return request.isSecure();
    }
    /**
     * Specialized utility method: find a sequence of lower case bytes inside
     * a ByteChunk.
     */
    protected int findBytes(ByteChunk bc, byte[] b) {
       
        byte[] buff = bc.getBuffer();
        int start = bc.getStart();
        int end = bc.getEnd();
        return findBytes(buff, b, start, end);
    }
   
    /**
     * This method has been taken from the grizzly DefaultProcessorTask.
     * @param buff
     * @param b
     * @param start
     * @param end
     * @return
     */
    public static int findBytes(byte [] buff, byte [] b, int start, int end){
        int srcEnd = b.length;
        byte first = b[0];
        for (int i = start; i <= (end - srcEnd); i++) {
            if (Ascii.toLower(buff[i]) != first) continue;
            // found first char, now look for a match
            int myPos = i+1;
            for (int srcPos = 1; srcPos < srcEnd; ) {
                if (Ascii.toLower(buff[myPos++]) != b[srcPos++])
                    break;
                if (srcPos == srcEnd) return i - start; // found it
            }
        }
        return -1;
    }
   
    public ByteBuffer getBuffer() {
        return buffer;
    }
   
    public long getPayloadLength(){
        return payloadlength;
    }
   
    public boolean isTransferEncoding(){
        return transferencoding;
    }
   
    public void updateEnd(ByteBuffer buff, boolean moredata){
        int pos = moredata ? buff.position() : request.getHeaderLength();
        int ret = findBytes(buff.array(),
                LoadBalancerProxyConstants.END_BYTES, pos, buff.limit());
       
        hasRemaining = (ret < 0);
    }
   
    public Request getRequest(){
        return request;
    }
   
    public Response getResponse(){
        return response;
    }
    /**
     * Getter for property bytesWritten.
     * @return Value of property bytesWritten.
     */
    public long getBytesWritten() {
        return bytesWritten;
    }
   
    /**
     * Setter for property bytesWritten.
     * @param bytesWritten New value of property bytesWritten.
     */
    public void setBytesWritten(long bytesWritten) {
        this.bytesWritten = bytesWritten;
    }
   
    /**
     * Util method to check if there are more
     * bytes in this request
     */
    public boolean hasRemaining() {
        return (transferencoding ? hasRemaining : (bytesWritten < payloadlength));
    }
   
    public void setSSLEngine(SSLEngine engine){
        sslEngine = engine;
    }
   
    public SSLEngine getSSLEngine(){
        return sslEngine;
    }
   
    public void setInputBB(ByteBuffer bb){
        sslInputBB = bb;
    }
   
    public ByteBuffer getInputBB() {
        return sslInputBB;
    }
   
   
    public void setOutputBB(ByteBuffer bb){
        sslOutputBB = bb;
    }
   
    public ByteBuffer getOutputBB() {
        return sslOutputBB;
    }
   
    public void setError(boolean er){
        error = er;
    }
   
    public boolean getError(){
        return error;
    }
}
TOP

Related Classes of org.jvnet.glassfish.comms.clb.proxy.ProxyRequestHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.