View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.proxy.utils;
21  
22  import org.apache.mina.core.buffer.IoBuffer;
23  import org.apache.mina.core.session.IoSession;
24  import org.apache.mina.filter.codec.textline.LineDelimiter;
25  
26  /**
27   * IoBufferDecoder.java - Handles an {@link IoBuffer} decoder which supports two methods : 
28   * - dynamic delimiter decoding
29   * - fixed length content reading
30   * 
31   * @author The Apache MINA Project (dev@mina.apache.org)
32   * @version $Rev: 685703 $, $Date: 2008-08-14 00:14:47 +0200 (Thu, 14 Aug 2008) $
33   * @since MINA 2.0.0-M3
34   */
35  public class IoBufferDecoder {
36  
37      public class DecodingContext {
38  
39          private IoBuffer decodedBuffer;
40  
41          private IoBuffer delimiter;
42  
43          private int contentLength = -1;
44  
45          private int matchCount = 0;
46  
47          public void clean() {
48              contentLength = -1;
49              matchCount = 0;
50              decodedBuffer = null;
51          }
52  
53          public int getContentLength() {
54              return contentLength;
55          }
56  
57          public void setContentLength(int contentLength) {
58              this.contentLength = contentLength;
59          }
60  
61          public int getMatchCount() {
62              return matchCount;
63          }
64  
65          public void setMatchCount(int matchCount) {
66              this.matchCount = matchCount;
67          }
68  
69          public IoBuffer getDecodedBuffer() {
70              return decodedBuffer;
71          }
72  
73          public void setDecodedBuffer(IoBuffer decodedBuffer) {
74              this.decodedBuffer = decodedBuffer;
75          }
76  
77          public IoBuffer getDelimiter() {
78              return delimiter;
79          }
80  
81          public void setDelimiter(IoBuffer delimiter) {
82              this.delimiter = delimiter;
83          }
84      }
85  
86      private DecodingContext ctx = new DecodingContext();
87  
88      /**
89       * Creates a new instance that uses specified <tt>delimiter</tt> byte array as a
90       * message delimiter.
91       */
92      public IoBufferDecoder(byte[] delimiter) {
93          setDelimiter(delimiter, true);
94      }
95  
96      /**
97       * Creates a new instance that will read messages of <tt>contentLength</tt> bytes.
98       */
99      public IoBufferDecoder(int contentLength) {
100         setContentLength(contentLength, false);
101     }
102 
103     /**
104      * Sets the the length of the content line to be decoded.
105      * When set, it overrides the dynamic delimiter setting. The default value is <tt>-1</tt>.
106      * Content length method will be used for decoding on the next decodeOnce call. 
107      * Delimiter matching is reset only if <tt>resetMatchCount</tt> is true.
108      */
109     public void setContentLength(int contentLength, boolean resetMatchCount) {
110         if (contentLength <= 0) {
111             throw new IllegalArgumentException("contentLength: "
112                     + contentLength);
113         }
114 
115         ctx.setContentLength(contentLength);
116         if (resetMatchCount) {
117             ctx.setMatchCount(0);
118         }
119     }
120 
121     /**
122      * Dynamically sets a new delimiter. Next time 
123      * {@link IoBufferDecoder#decodeOnce(IoSession, int) } will be called it will use the new 
124      * delimiter. Delimiter matching is reset only if <tt>resetMatchCount</tt> is true but 
125      * decoding will continue from current position.
126      * 
127      * NB : Delimiter {@link LineDelimiter#AUTO} is not allowed. 
128      */
129     public void setDelimiter(byte[] delim, boolean resetMatchCount) {
130         if (delim == null) {
131             throw new NullPointerException("Null delimiter not allowed");
132         }
133 
134         // Convert delimiter to IoBuffer.
135         IoBuffer delimiter = IoBuffer.allocate(delim.length);
136         delimiter.put(delim);
137         delimiter.flip();
138 
139         ctx.setDelimiter(delimiter);
140         ctx.setContentLength(-1);
141         if (resetMatchCount) {
142             ctx.setMatchCount(0);
143         }
144     }
145 
146     /**
147      * Will return null unless it has enough data to decode. If <code>contentLength</code>
148      * is set then it tries to retrieve <code>contentLength</code> bytes from the buffer
149      * otherwise it will scan the buffer to find the data <code>delimiter</code> and return
150      * all the data and the trailing delimiter.
151      */
152     public IoBuffer decodeFully(IoBuffer in) {
153         int contentLength = ctx.getContentLength();
154         IoBuffer decodedBuffer = ctx.getDecodedBuffer();
155 
156         int oldLimit = in.limit();
157 
158         // Retrieve fixed length content
159         if (contentLength > -1) {
160             if (decodedBuffer == null) {
161                 decodedBuffer = IoBuffer.allocate(contentLength).setAutoExpand(
162                         true);
163             }
164 
165             if (in.remaining() < contentLength) {
166                 int readBytes = in.remaining();
167                 decodedBuffer.put(in);
168                 ctx.setDecodedBuffer(decodedBuffer);
169                 ctx.setContentLength(contentLength - readBytes);
170                 return null;
171 
172             } else {
173                 int newLimit = in.position() + contentLength;
174                 in.limit(newLimit);
175                 decodedBuffer.put(in);
176                 decodedBuffer.flip();
177                 in.limit(oldLimit);
178                 ctx.clean();
179 
180                 return decodedBuffer;
181             }
182         }
183 
184         // Not a fixed length matching so try to find a delimiter match
185         int oldPos = in.position();
186         int matchCount = ctx.getMatchCount();
187         IoBuffer delimiter = ctx.getDelimiter();
188 
189         while (in.hasRemaining()) {
190             byte b = in.get();
191             if (delimiter.get(matchCount) == b) {
192                 matchCount++;
193                 if (matchCount == delimiter.limit()) {
194                     // Found a match.
195                     int pos = in.position();
196                     in.position(oldPos);
197 
198                     in.limit(pos);
199 
200                     if (decodedBuffer == null) {
201                         decodedBuffer = IoBuffer.allocate(in.remaining())
202                                 .setAutoExpand(true);
203                     }
204 
205                     decodedBuffer.put(in);
206                     decodedBuffer.flip();
207 
208                     in.limit(oldLimit);
209                     ctx.clean();
210 
211                     return decodedBuffer;
212                 }
213             } else {
214                 in.position(Math.max(0, in.position() - matchCount));
215                 matchCount = 0;
216             }
217         }
218 
219         // Copy remainder from buf.
220         if (in.remaining() > 0) {
221             in.position(oldPos);
222             decodedBuffer.put(in);
223             in.position(in.limit());
224         }
225 
226         // Save decoding state
227         ctx.setMatchCount(matchCount);
228         ctx.setDecodedBuffer(decodedBuffer);
229 
230         return decodedBuffer;
231     }
232 }