View Javadoc

    1   /*******************************************************************************
    2    * Copyright (c) 2011 Michael Mimo Moratti.
    3    *
    4    * Michael Mimo Moratti licenses this file to you under the Apache License, version 2.0
    5    * (the "License"); you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at:
    7    *     http://www.apache.org/licenses/LICENSE-2.0
    8    * Unless required by applicable law or agreed to in writing, software
    9    * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    10   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
    11   * License for the specific language governing permissions and limitations
    12   * under the License.
    13   *******************************************************************************/
    14  package ch.mimo.netty.handler.codec.icap;
    15
    16  import org.jboss.netty.buffer.ChannelBuffer;
    17  import org.jboss.netty.buffer.ChannelBuffers;
    18  import org.jboss.netty.channel.ChannelHandlerContext;
    19  import org.jboss.netty.channel.Channels;
    20  import org.jboss.netty.channel.MessageEvent;
    21  import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
    22  import org.jboss.netty.handler.codec.frame.TooLongFrameException;
    23  import org.jboss.netty.handler.codec.http.HttpMessage;
    24  import org.jboss.netty.logging.InternalLogger;
    25  import org.jboss.netty.logging.InternalLoggerFactory;
    26
    27  /**
    28   * This ICAP chunk aggregator will combine an received ICAP message with all body chunks.
    29   * the body is the to be found attached to the correct HTTP request or response instance
    30   * within the ICAP message.
    31   *
    32   * @author Michael Mimo Moratti (mimo@mimo.ch)
    33   *
    34   * @see IcapChunkSeparator
    35   *
    36   */
    37  public class IcapChunkAggregator extends SimpleChannelUpstreamHandler {
    38
    39  	private static final InternalLogger LOG = InternalLoggerFactory.getInstance(IcapChunkAggregator.class);
    40
    41  	private long maxContentLength;
    42  	private IcapMessageWrapper message;
    43
    44  	/**
    45  	 * Convenience method to retrieve a HTTP request,response or
    46  	 * an ICAP options response body from an aggregated IcapMessage.
    47  	 * @param message
    48  	 * @return null or @see {@link ChannelBuffer} if a body exists.
    49  	 */
    50  	public static ChannelBuffer extractHttpBodyContentFromIcapMessage(IcapMessage message) {
    51  		ChannelBuffer buffer = null;
    52  		if(message.getBody().equals(IcapMessageElementEnum.REQBODY) && message.getHttpRequest() != null) {
    53  			buffer = message.getHttpRequest().getContent();
    54  		} else if(message.getBody().equals(IcapMessageElementEnum.RESBODY) && message.getHttpResponse() != null) {
    55  			buffer = message.getHttpResponse().getContent();
    56  		} else if(message instanceof IcapResponse && message.getBody().equals(IcapMessageElementEnum.OPTBODY)) {
    57  			IcapResponse response = (IcapResponse)message;
    58  			buffer = response.getContent();
    59  		}
    60  		return buffer;
    61  	}
    62
    63  	/**
    64  	 * @param maxContentLength defines the maximum length of the body content that is allowed.
    65  	 * If the length is exceeded an exception is thrown.
    66  	 */
    67  	public IcapChunkAggregator(long maxContentLength) {
    68  		this.maxContentLength = maxContentLength;
    69  	}
    70
    71      @Override
    72      public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
    73      	Object msg = e.getMessage();
    74      	if(msg instanceof IcapMessage) {
    75      		LOG.debug("Aggregation of message [" + msg.getClass().getName() + "] ");
    76      		IcapMessage currentMessage = (IcapMessage)msg;
    77      		message = new IcapMessageWrapper(currentMessage);
    78      		if(!message.hasBody()) {
    79      			Channels.fireMessageReceived(ctx,message.getIcapMessage(),e.getRemoteAddress());
    80      			message = null;
    81      			return;
    82      		}
    83      	} else if(msg instanceof IcapChunkTrailer) {
    84      		LOG.debug("Aggregation of chunk trailer [" + msg.getClass().getName() + "] ");
    85      		if(message == null) {
    86      			ctx.sendUpstream(e);
    87      		} else {
    88      			IcapChunkTrailer trailer = (IcapChunkTrailer)msg;
    89      			if(trailer.getHeaderNames().size() > 0) {
    90      				for(String name : trailer.getHeaderNames()) {
    91      					message.addHeader(name,trailer.getHeader(name));
    92      				}
    93      			}
    94      			Channels.fireMessageReceived(ctx,message.getIcapMessage(),e.getRemoteAddress());
    95      		}
    96      	} else if(msg instanceof IcapChunk) {
    97      		LOG.debug("Aggregation of chunk [" + msg.getClass().getName() + "] ");
    98      		IcapChunk chunk = (IcapChunk)msg;
    99      		if(message == null) {
    100     			ctx.sendUpstream(e);
    101     		} else if(chunk.isLast()) {
    102     			Channels.fireMessageReceived(ctx,message.getIcapMessage(),e.getRemoteAddress());
    103     			message = null;
    104     		} else {
    105 	    		ChannelBuffer chunkBuffer = chunk.getContent();
    106 	    		ChannelBuffer content = message.getContent();
    107     			if(content.readableBytes() > maxContentLength - chunkBuffer.readableBytes()) {
    108     				throw new TooLongFrameException("ICAP content length exceeded [" + maxContentLength + "] bytes");
    109     			} else {
    110     				content.writeBytes(chunkBuffer);
    111     			}
    112     		}
    113     	} else {
    114     		ctx.sendUpstream(e);
    115     	}
    116     }
    117
    118     private final class IcapMessageWrapper {
    119
    120     	private IcapMessage message;
    121     	private HttpMessage relevantHttpMessage;
    122     	private IcapResponse icapResponse;
    123     	private boolean messageWithBody;
    124
    125     	public IcapMessageWrapper(IcapMessage message) {
    126     		this.message = message;
    127     		if(message.getBody() != null) {
    128 	    		if(message.getBody().equals(IcapMessageElementEnum.REQBODY)) {
    129 	    			relevantHttpMessage = message.getHttpRequest();
    130 	    			messageWithBody = true;
    131 	    		} else if(message.getBody().equals(IcapMessageElementEnum.RESBODY)) {
    132 	    			relevantHttpMessage = message.getHttpResponse();
    133 	    			messageWithBody = true;
    134 	    		} else if(message instanceof IcapResponse && message.getBody().equals(IcapMessageElementEnum.OPTBODY)) {
    135 	    			icapResponse = (IcapResponse)message;
    136 	    			messageWithBody = true;
    137 	    		}
    138     		}
    139     		if(messageWithBody) {
    140     			if(relevantHttpMessage != null) {
    141 	    			if(relevantHttpMessage.getContent() == null || relevantHttpMessage.getContent().readableBytes() <= 0) {
    142 	    				relevantHttpMessage.setContent(ChannelBuffers.dynamicBuffer());
    143 	    			}
    144     			} else if(icapResponse != null) {
    145     				if(icapResponse.getContent() == null || icapResponse.getContent().readableBytes() <= 0) {
    146     					icapResponse.setContent(ChannelBuffers.dynamicBuffer());
    147     				}
    148     			}
    149     		}
    150     	}
    151
    152     	public boolean hasBody() {
    153     		return messageWithBody;
    154     	}
    155
    156     	public IcapMessage getIcapMessage() {
    157     		return message;
    158     	}
    159
    160     	public void addHeader(String name, String value) {
    161     		if(messageWithBody) {
    162     			relevantHttpMessage.addHeader(name,value);
    163     		} else {
    164     			throw new IcapDecodingError("A message without body cannot carry trailing headers.");
    165     		}
    166     	}
    167
    168     	public ChannelBuffer getContent() {
    169     		if(messageWithBody) {
    170     			if(relevantHttpMessage != null) {
    171     				return relevantHttpMessage.getContent();
    172     			} else if(icapResponse != null) {
    173     				return icapResponse.getContent();
    174     			}
    175     		}
    176     		throw new IcapDecodingError("Message stated that there is a body but nothing found in message.");
    177     	}
    178     }
    179 }