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  
18  /**
19   * Decoder State that reads chunk size
20   * 
21   * @author Michael Mimo Moratti (mimo@mimo.ch)
22   *
23   * @see IcapMessageDecoder
24   * @see StateEnum
25   */
26  public class ReadChunkSizeState extends State<ReadChunkSizeState.DecisionState> {
27  
28  	/**
29  	 * Used to decide what state has to be processed next.
30  	 * 
31  	 * @author Michael Mimo Moratti (mimo@mimo.ch)
32  	 *
33  	 */
34  	public static enum DecisionState {
35  		READ_CHUNK(StateEnum.READ_CHUNK_STATE),
36  		READ_HUGE_CHUNK_IN_SMALER_CHUNKS(StateEnum.READ_CHUNKED_CONTENT_AS_CHUNKS_STATE),
37  		READ_TRAILING_HEADERS(StateEnum.READ_TRAILING_HEADERS_STATE),
38  		IS_LAST_PREVIEW_CHUNK(StateEnum.READ_CHUNK_SIZE_STATE),
39  		IS_LAST_CHUNK(StateEnum.SKIP_CONTROL_CHARS),
40  		RESET(null);
41  		
42  		private StateEnum state;
43  		
44  		DecisionState(StateEnum nextState) {
45  			state = nextState;
46  		}
47  		
48  		public StateEnum getNextState() {
49  			return state;
50  		}
51  	}
52  	
53  	public ReadChunkSizeState(String name) {
54  		super(name);
55  	}
56  	
57  	@Override
58  	public void onEntry(ChannelBuffer buffer, IcapMessageDecoder icapMessageDecoder) throws DecodingException {
59  	}
60  
61  	@Override
62  	/**
63  	 * 1. chunk size is not 0 --> normal chunk reading is required. (go to chunk reading)
64  	 * 2. chunk size is 0 --> end of body (end of processing)
65  	 * 3. chunk size is 0 and we are in preview mode / preview is therefore sent and possible more is to come. (stay in state)
66  	 * 4. chunk size is 0; ieof early end of preview (stay in state)
67  	 * 5. next message arrives and we are still in preview mode. (step out and start over)
68  	 *
69  	 * Strategy:
70  	 * 
71  	 * 1. preview read next line
72  	 * 2. attempt to parse chunk length
73  	 * 2a. chunk length could not be parsed step out (END STATE/reset).
74  	 * 2b. chunk length could be parsed, adjust readerIndex by reading the line.
75  	 * 3. chunk size > 0, read the next chunk. (END STATE).
76  	 * 4. chunk size == -1, early termination of preview reading process. Stay in state and wait for more data.
77  	 * 5. chunk size == 0 and message is preview message. Stay in state and wait for more data.
78  	 * 6. chunk size == 0 step out (END STATE/reset).
79  	 */
80  	public StateReturnValue execute(ChannelBuffer buffer, IcapMessageDecoder icapMessageDecoder) throws DecodingException {
81  		int chunkSize = 0;
82  		String previewLine = IcapDecoderUtil.previewLine(buffer,icapMessageDecoder.maxInitialLineLength);
83  		try {
84  			chunkSize = IcapDecoderUtil.getChunkSize(previewLine);
85  			IcapDecoderUtil.readLine(buffer,icapMessageDecoder.maxInitialLineLength);
86  		} catch(DecodingException de) {
87  			return StateReturnValue.createIrrelevantResultWithDecisionInformation(DecisionState.RESET);
88  		}
89  		icapMessageDecoder.currentChunkSize = chunkSize;
90  		if(chunkSize > 0) {
91  			if(chunkSize >= icapMessageDecoder.maxChunkSize) {
92  				return StateReturnValue.createIrrelevantResultWithDecisionInformation(DecisionState.READ_HUGE_CHUNK_IN_SMALER_CHUNKS);
93  			} else {
94  				return StateReturnValue.createIrrelevantResultWithDecisionInformation(DecisionState.READ_CHUNK);
95  			}
96  		} else if(chunkSize == -1) {
97  			icapMessageDecoder.currentChunkSize = 0;
98  			IcapDecoderUtil.readLine(buffer,Integer.MAX_VALUE);
99  			return StateReturnValue.createRelevantResultWithDecisionInformation(new DefaultIcapChunkTrailer(true,true),DecisionState.IS_LAST_PREVIEW_CHUNK);
100 		} else if(chunkSize == 0) {
101 			if(!checkForLineBreak(buffer)) {
102 				return StateReturnValue.createIrrelevantResultWithDecisionInformation(DecisionState.READ_TRAILING_HEADERS);
103 			}
104 			IcapDecoderUtil.readLine(buffer,10);
105 			if(icapMessageDecoder.message.isPreviewMessage()) {
106 				return StateReturnValue.createRelevantResultWithDecisionInformation(new DefaultIcapChunkTrailer(true,false),DecisionState.IS_LAST_PREVIEW_CHUNK);
107 			} else {
108 				return StateReturnValue.createRelevantResultWithDecisionInformation(new DefaultIcapChunkTrailer(icapMessageDecoder.message.isPreviewMessage(),false),DecisionState.IS_LAST_CHUNK);
109 			}		
110 		} else {
111 			return StateReturnValue.createIrrelevantResultWithDecisionInformation(DecisionState.RESET);
112 		}
113 	}
114 	
115 	@Override
116 	public StateEnum onExit(ChannelBuffer buffer, IcapMessageDecoder icapMessageDecoder, DecisionState decisionInformation) throws DecodingException {
117 		return decisionInformation.getNextState();
118 	}
119 	
120 	private boolean checkForLineBreak(ChannelBuffer buffer) {
121 		byte previewByte = buffer.getByte(buffer.readerIndex() + 1);
122 		return previewByte == IcapCodecUtil.CR | previewByte == IcapCodecUtil.LF;
123 	}
124 }