View Javadoc

1   /*
2    * Copyright 2012 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.handler.codec;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.channel.ChannelHandlerContext;
20  import io.netty.util.ByteProcessor;
21  
22  import java.util.List;
23  
24  /**
25   * A decoder that splits the received [email protected] ByteBuf}s on line endings.
26   * <p>
27   * Both [email protected] "\n"} and [email protected] "\r\n"} are handled.
28   * For a more general delimiter-based decoder, see [email protected] DelimiterBasedFrameDecoder}.
29   */
30  public class LineBasedFrameDecoder extends ByteToMessageDecoder {
31  
32      /** Maximum length of a frame we're willing to decode.  */
33      private final int maxLength;
34      /** Whether or not to throw an exception as soon as we exceed maxLength. */
35      private final boolean failFast;
36      private final boolean stripDelimiter;
37  
38      /** True if we're discarding input because we're already over maxLength.  */
39      private boolean discarding;
40      private int discardedBytes;
41  
42      /**
43       * Creates a new decoder.
44       * @param maxLength  the maximum length of the decoded frame.
45       *                   A [email protected] TooLongFrameException} is thrown if
46       *                   the length of the frame exceeds this value.
47       */
48      public LineBasedFrameDecoder(final int maxLength) {
49          this(maxLength, true, false);
50      }
51  
52      /**
53       * Creates a new decoder.
54       * @param maxLength  the maximum length of the decoded frame.
55       *                   A [email protected] TooLongFrameException} is thrown if
56       *                   the length of the frame exceeds this value.
57       * @param stripDelimiter  whether the decoded frame should strip out the
58       *                        delimiter or not
59       * @param failFast  If <tt>true</tt>, a [email protected] TooLongFrameException} is
60       *                  thrown as soon as the decoder notices the length of the
61       *                  frame will exceed <tt>maxFrameLength</tt> regardless of
62       *                  whether the entire frame has been read.
63       *                  If <tt>false</tt>, a [email protected] TooLongFrameException} is
64       *                  thrown after the entire frame that exceeds
65       *                  <tt>maxFrameLength</tt> has been read.
66       */
67      public LineBasedFrameDecoder(final int maxLength, final boolean stripDelimiter, final boolean failFast) {
68          this.maxLength = maxLength;
69          this.failFast = failFast;
70          this.stripDelimiter = stripDelimiter;
71      }
72  
73      @Override
74      protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
75          Object decoded = decode(ctx, in);
76          if (decoded != null) {
77              out.add(decoded);
78          }
79      }
80  
81      /**
82       * Create a frame out of the [email protected] ByteBuf} and return it.
83       *
84       * @param   ctx             the [email protected] ChannelHandlerContext} which this [email protected] ByteToMessageDecoder} belongs to
85       * @param   buffer          the [email protected] ByteBuf} from which to read data
86       * @return  frame           the [email protected] ByteBuf} which represent the frame or [email protected] null} if no frame could
87       *                          be created.
88       */
89      protected Object decode(ChannelHandlerContext ctx, ByteBuf buffer) throws Exception {
90          final int eol = findEndOfLine(buffer);
91          if (!discarding) {
92              if (eol >= 0) {
93                  final ByteBuf frame;
94                  final int length = eol - buffer.readerIndex();
95                  final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
96  
97                  if (length > maxLength) {
98                      buffer.readerIndex(eol + delimLength);
99                      fail(ctx, length);
100                     return null;
101                 }
102 
103                 if (stripDelimiter) {
104                     frame = buffer.readSlice(length);
105                     buffer.skipBytes(delimLength);
106                 } else {
107                     frame = buffer.readSlice(length + delimLength);
108                 }
109 
110                 return frame.retain();
111             } else {
112                 final int length = buffer.readableBytes();
113                 if (length > maxLength) {
114                     discardedBytes = length;
115                     buffer.readerIndex(buffer.writerIndex());
116                     discarding = true;
117                     if (failFast) {
118                         fail(ctx, "over " + discardedBytes);
119                     }
120                 }
121                 return null;
122             }
123         } else {
124             if (eol >= 0) {
125                 final int length = discardedBytes + eol - buffer.readerIndex();
126                 final int delimLength = buffer.getByte(eol) == '\r'? 2 : 1;
127                 buffer.readerIndex(eol + delimLength);
128                 discardedBytes = 0;
129                 discarding = false;
130                 if (!failFast) {
131                     fail(ctx, length);
132                 }
133             } else {
134                 discardedBytes += buffer.readableBytes();
135                 buffer.readerIndex(buffer.writerIndex());
136             }
137             return null;
138         }
139     }
140 
141     private void fail(final ChannelHandlerContext ctx, int length) {
142         fail(ctx, String.valueOf(length));
143     }
144 
145     private void fail(final ChannelHandlerContext ctx, String length) {
146         ctx.fireExceptionCaught(
147                 new TooLongFrameException(
148                         "frame length (" + length + ") exceeds the allowed maximum (" + maxLength + ')'));
149     }
150 
151     /**
152      * Returns the index in the buffer of the end of line found.
153      * Returns -1 if no end of line was found in the buffer.
154      */
155     private static int findEndOfLine(final ByteBuf buffer) {
156         int i = buffer.forEachByte(ByteProcessor.FIND_LF);
157         if (i > 0 && buffer.getByte(i - 1) == '\r') {
158             i--;
159         }
160         return i;
161     }
162 }