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.channel.ChannelDownstreamHandler;
    18  import org.jboss.netty.channel.ChannelEvent;
    19  import org.jboss.netty.channel.ChannelHandlerContext;
    20  import org.jboss.netty.channel.DownstreamMessageEvent;
    21  import org.jboss.netty.channel.MessageEvent;
    22  import org.jboss.netty.logging.InternalLogger;
    23  import org.jboss.netty.logging.InternalLoggerFactory;
    24
    25  /**
    26   * Separates a received ICAP message and body that is attached to either the HTTP request or response.
    27   *
    28   * In other words. This handler allows to create a combined ICAP message containing HTTP request/response and
    29   * the corresponding body as ChannelBuffer include in one of the HTTP relevant instances.
    30   *
    31   * This separator cannot handle trailing headers at HTTP request or response bodies. If you have to
    32   * send trailing headers then consider not using this separator but handling the message body by yourself.
    33   *
    34   * @author Michael Mimo Moratti (mimo@mimo.ch)
    35   *
    36   */
    37  public class IcapChunkSeparator implements ChannelDownstreamHandler {
    38
    39  	private static final InternalLogger LOG = InternalLoggerFactory.getInstance(IcapChunkSeparator.class);
    40
    41  	private int chunkSize;
    42
    43  	/**
    44  	 * @param chunkSize defines the normal chunk size that is to be produced while separating.
    45  	 */
    46  	public IcapChunkSeparator(int chunkSize) {
    47  		this.chunkSize = chunkSize;
    48  	}
    49
    50  	@Override
    51  	public void handleDownstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception {
    52  		if (e instanceof MessageEvent) {
    53  			MessageEvent msgEvent = (MessageEvent)e;
    54  			Object msg = msgEvent.getMessage();
    55  	    	if(msg instanceof IcapMessage) {
    56  	    		LOG.debug("Separation of message [" + msg.getClass().getName() + "] ");
    57  	    		IcapMessage message = (IcapMessage)msg;
    58  	    		ChannelBuffer content = extractContentFromMessage(message);
    59  	    		fireDownstreamEvent(ctx,message,msgEvent);
    60  	    		if(content != null) {
    61  	    			boolean isPreview = message.isPreviewMessage();
    62  	    			boolean isEarlyTerminated = false;
    63  	    			if(isPreview) {
    64  	    				isEarlyTerminated = content.readableBytes() < message.getPreviewAmount();
    65  	    			}
    66  					while(content.readableBytes() > 0) {
    67  						IcapChunk chunk = null;
    68  						if(content.readableBytes() > chunkSize) {
    69  							chunk = new DefaultIcapChunk(content.readBytes(chunkSize));
    70  						} else {
    71  							chunk = new DefaultIcapChunk(content.readBytes(content.readableBytes()));
    72  						}
    73  						chunk.setPreviewChunk(isPreview);
    74  						chunk.setEarlyTermination(isEarlyTerminated);
    75  						fireDownstreamEvent(ctx,chunk,msgEvent);
    76  						if(chunk.isLast() | content.readableBytes() <= 0) {
    77  							IcapChunkTrailer trailer = new DefaultIcapChunkTrailer();
    78  							trailer.setPreviewChunk(isPreview);
    79  							trailer.setEarlyTermination(isEarlyTerminated);
    80  							fireDownstreamEvent(ctx,trailer,msgEvent);
    81  						}
    82  					}
    83  	    		}
    84  	    	} else {
    85  	    		ctx.sendDownstream(e);
    86  	    	}
    87  		} else {
    88  			ctx.sendDownstream(e);
    89  		}
    90  	}
    91
    92  	private ChannelBuffer extractContentFromMessage(IcapMessage message) {
    93  		ChannelBuffer content = null;
    94  		if(message instanceof IcapResponse && ((IcapResponse)message).getContent() != null) {
    95  			IcapResponse response = (IcapResponse)message;
    96  			content = response.getContent();
    97  			if(content != null) {
    98  				message.setBody(IcapMessageElementEnum.OPTBODY);
    99  			}
    100 		} else if(message.getHttpRequest() != null && message.getHttpRequest().getContent() != null && message.getHttpRequest().getContent().readableBytes() > 0) {
    101 			content = message.getHttpRequest().getContent();
    102 			message.setBody(IcapMessageElementEnum.REQBODY);
    103 		} else if(message.getHttpResponse() != null && message.getHttpResponse().getContent() != null && message.getHttpResponse().getContent().readableBytes() > 0) {
    104 			content = message.getHttpResponse().getContent();
    105 			message.setBody(IcapMessageElementEnum.RESBODY);
    106 		}
    107 		return content;
    108 	}
    109
    110     private void fireDownstreamEvent(ChannelHandlerContext ctx, Object message, MessageEvent messageEvent) {
    111     	DownstreamMessageEvent downstreamMessageEvent =
    112     		new DownstreamMessageEvent(ctx.getChannel(),messageEvent.getFuture(),message,messageEvent.getRemoteAddress());
    113     	ctx.sendDownstream(downstreamMessageEvent);
    114     }
    115 }