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