View Javadoc
1   /*
2    * Copyright 2015 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.rtsp;
17  
18  import java.util.regex.Pattern;
19  
20  import io.netty.buffer.Unpooled;
21  import io.netty.handler.codec.http.DefaultFullHttpRequest;
22  import io.netty.handler.codec.http.DefaultFullHttpResponse;
23  import io.netty.handler.codec.http.DefaultHttpRequest;
24  import io.netty.handler.codec.http.DefaultHttpResponse;
25  import io.netty.handler.codec.http.HttpMessage;
26  import io.netty.handler.codec.http.HttpObjectDecoder;
27  import io.netty.handler.codec.http.HttpResponseStatus;
28  
29  /**
30   * Decodes {@link ByteBuf}s into RTSP messages represented in
31   * {@link HttpMessage}s.
32   * <p>
33   * <h3>Parameters that prevents excessive memory consumption</h3>
34   * <table border="1">
35   * <tr>
36   * <th>Name</th><th>Meaning</th>
37   * </tr>
38   * <tr>
39   * <td>{@code maxInitialLineLength}</td>
40   * <td>The maximum length of the initial line
41   *     (e.g. {@code "SETUP / RTSP/1.0"} or {@code "RTSP/1.0 200 OK"})
42   *     If the length of the initial line exceeds this value, a
43   *     {@link TooLongFrameException} will be raised.</td>
44   * </tr>
45   * <tr>
46   * <td>{@code maxHeaderSize}</td>
47   * <td>The maximum length of all headers. If the sum of the length of each
48   *     header exceeds this value, a {@link TooLongFrameException} will be
49   *     raised.</td>
50   * </tr>
51   * <tr>
52   * <td>{@code maxContentLength}</td>
53   * <td>The maximum length of the content.  If the content length exceeds this
54   *     value, a {@link TooLongFrameException} will be raised.</td>
55   * </tr>
56   * </table>
57   */
58  public class RtspDecoder extends HttpObjectDecoder {
59      /**
60       * Status code for unknown responses.
61       */
62      private static final HttpResponseStatus UNKNOWN_STATUS =
63              new HttpResponseStatus(999, "Unknown");
64      /**
65       * True if the message to decode is a request.
66       * False if the message to decode is a response.
67       */
68      private boolean isDecodingRequest;
69  
70      /**
71       * Regex used on first line in message to detect if it is a response.
72       */
73      private static final Pattern versionPattern = Pattern.compile("RTSP/\\d\\.\\d");
74  
75      /**
76       * Constant for default max initial line length.
77       */
78      public static final int DEFAULT_MAX_INITIAL_LINE_LENGTH = 4096;
79  
80      /**
81       * Constant for default max header size.
82       */
83      public static final int DEFAULT_MAX_HEADER_SIZE = 8192;
84  
85      /**
86       * Constant for default max content length.
87       */
88      public static final int DEFAULT_MAX_CONTENT_LENGTH = 8192;
89  
90      /**
91       * Creates a new instance with the default
92       * {@code maxInitialLineLength (4096)}, {@code maxHeaderSize (8192)}, and
93       * {@code maxContentLength (8192)}.
94       */
95      public RtspDecoder() {
96          this(DEFAULT_MAX_INITIAL_LINE_LENGTH,
97               DEFAULT_MAX_HEADER_SIZE,
98               DEFAULT_MAX_CONTENT_LENGTH);
99      }
100 
101     /**
102      * Creates a new instance with the specified parameters.
103      * @param maxInitialLineLength The max allowed length of initial line
104      * @param maxHeaderSize The max allowed size of header
105      * @param maxContentLength The max allowed content length
106      */
107     public RtspDecoder(final int maxInitialLineLength,
108                        final int maxHeaderSize,
109                        final int maxContentLength) {
110         super(maxInitialLineLength, maxHeaderSize, maxContentLength * 2, false);
111     }
112 
113     /**
114      * Creates a new instance with the specified parameters.
115      * @param maxInitialLineLength The max allowed length of initial line
116      * @param maxHeaderSize The max allowed size of header
117      * @param maxContentLength The max allowed content length
118      * @param validateHeaders Set to true if headers should be validated
119      */
120     public RtspDecoder(final int maxInitialLineLength,
121                        final int maxHeaderSize,
122                        final int maxContentLength,
123                        final boolean validateHeaders) {
124         super(maxInitialLineLength,
125               maxHeaderSize,
126               maxContentLength * 2,
127               false,
128               validateHeaders);
129     }
130 
131     @Override
132     protected HttpMessage createMessage(final String[] initialLine)
133             throws Exception {
134         // If the first element of the initial line is a version string then
135         // this is a response
136         if (versionPattern.matcher(initialLine[0]).matches()) {
137             isDecodingRequest = false;
138             return new DefaultHttpResponse(RtspVersions.valueOf(initialLine[0]),
139                 new HttpResponseStatus(Integer.parseInt(initialLine[1]),
140                                        initialLine[2]),
141                 validateHeaders);
142         } else {
143             isDecodingRequest = true;
144             return new DefaultHttpRequest(RtspVersions.valueOf(initialLine[2]),
145                     RtspMethods.valueOf(initialLine[0]),
146                     initialLine[1],
147                     validateHeaders);
148         }
149     }
150 
151     @Override
152     protected boolean isContentAlwaysEmpty(final HttpMessage msg) {
153         // Unlike HTTP, RTSP always assumes zero-length body if Content-Length
154         // header is absent.
155         return super.isContentAlwaysEmpty(msg) || !msg.headers().contains(RtspHeaders.Names.CONTENT_LENGTH);
156     }
157 
158     @Override
159     protected HttpMessage createInvalidMessage() {
160         if (isDecodingRequest) {
161             return new DefaultFullHttpRequest(RtspVersions.RTSP_1_0,
162                        RtspMethods.OPTIONS, "/bad-request", Unpooled.EMPTY_BUFFER, validateHeaders);
163         } else {
164             return new DefaultFullHttpResponse(RtspVersions.RTSP_1_0,
165                                                UNKNOWN_STATUS, Unpooled.EMPTY_BUFFER,
166                                                validateHeaders);
167         }
168     }
169 
170     @Override
171     protected boolean isDecodingRequest() {
172         return isDecodingRequest;
173     }
174 }