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.Channel;
18  import org.jboss.netty.channel.ChannelHandlerContext;
19  import org.jboss.netty.handler.codec.replay.ReplayingDecoder;
20  import org.jboss.netty.logging.InternalLogger;
21  import org.jboss.netty.logging.InternalLoggerFactory;
22  
23  /**
24   * Main ICAP message decoder implementation. this decoder is bases on a @see {@link ReplayingDecoder}
25   * 
26   * Due to the complexity of an ICAP message the decoder was implement with individual states that reside
27   * in their own classes.
28   * 
29   * For a full list of states that are used within this decoder: @see {@link StateEnum}  
30   * 
31   * @author Michael Mimo Moratti (mimo@mimo.ch)
32   *
33   * @see IcapRequestDecoder
34   * @see IcapResponseDecoder
35   */
36  
37  public abstract class IcapMessageDecoder extends ReplayingDecoder<StateEnum> {
38  
39  	private final InternalLogger LOG;
40  	
41      protected final int maxInitialLineLength;
42      protected final int maxIcapHeaderSize;
43      protected final int maxHttpHeaderSize;
44      protected final int maxChunkSize;
45      
46  	protected IcapMessage message;
47  	
48  	protected int currentChunkSize;
49  	
50  	
51  	
52      /**
53       * Creates a new instance with the default
54       * {@code maxInitialLineLength (4096}}, {@code maxIcapHeaderSize (8192)}, {@code maxHttpHeaderSize (8192)}, and
55       * {@code maxChunkSize (8192)}.
56       */
57      protected IcapMessageDecoder() {
58          this(4096,8192,8192,8192);
59      }
60      
61      /**
62       * Creates a new instance with the specified parameters.
63       * @param maxInitialLineLength
64       * @param maxIcapHeaderSize
65       * @param maxHttpHeaderSize
66       * @param maxChunkSize
67       */
68      protected IcapMessageDecoder(int maxInitialLineLength, int maxIcapHeaderSize, int maxHttpHeaderSize, int maxChunkSize) {
69          super(StateEnum.SKIP_CONTROL_CHARS,true);
70          LOG = InternalLoggerFactory.getInstance(getClass());
71          if (maxInitialLineLength <= 0) {
72              throw new IllegalArgumentException("maxInitialLineLength must be a positive integer: " + maxInitialLineLength);
73          }
74          if (maxIcapHeaderSize <= 0) {
75              throw new IllegalArgumentException("maxIcapHeaderSize must be a positive integer: " + maxIcapHeaderSize);
76          }
77          if(maxHttpHeaderSize <= 0) {
78          	throw new IllegalArgumentException("maxHttpHeaderSize must be a positive integer: " + maxIcapHeaderSize);
79          }
80          if (maxChunkSize <= 0) {
81              throw new IllegalArgumentException("maxChunkSize must be a positive integer: " + maxChunkSize);
82          }
83          this.maxInitialLineLength = maxInitialLineLength;
84          this.maxIcapHeaderSize = maxIcapHeaderSize;
85          this.maxHttpHeaderSize = maxHttpHeaderSize;
86          this.maxChunkSize = maxChunkSize;
87      }
88  
89  	@SuppressWarnings({ "rawtypes", "unchecked" })
90  	@Override
91  	protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, StateEnum stateEnumValue) throws Exception {
92  		if(stateEnumValue != null) {
93  			try {
94  				State state = stateEnumValue.getState();
95  				LOG.debug("Executing state [" + state + ']');
96  				state.onEntry(buffer,this);
97  				StateReturnValue returnValue = state.execute(buffer,this);
98  				LOG.debug("Return value from state [" + state + "] = [" + returnValue + "]");
99  				StateEnum nextState = state.onExit(buffer,this,returnValue.getDecisionInformation());
100 				LOG.debug("Next State [" + nextState + "]");
101 				if(nextState != null) {
102 					checkpoint(nextState);
103 				} else {
104 					reset();
105 				}
106 				if(returnValue.isRelevant()) {
107 					return returnValue.getValue();
108 				}
109 			} catch(DecodingException e) {
110 				reset();
111 				throw e;
112 			}
113 		}
114 		return null;
115 	}
116 	
117 	/**
118 	 * set the decoders message to NULL and the next checkpoint to @see {@link StateEnum#SKIP_CONTROL_CHARS}
119 	 */
120     private void reset() {
121         this.message = null;
122         checkpoint(StateEnum.SKIP_CONTROL_CHARS);
123     }
124 	
125 	public abstract boolean isDecodingResponse();
126 	
127 	protected abstract IcapMessage createMessage(String[] initialLine);
128 }