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 }