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    *   https://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 content length.
76       */
77      public static final int DEFAULT_MAX_CONTENT_LENGTH = 8192;
78  
79      /**
80       * Creates a new instance with the default
81       * {@code maxInitialLineLength (4096)}, {@code maxHeaderSize (8192)}, and
82       * {@code maxContentLength (8192)}.
83       */
84      public RtspDecoder() {
85          this(DEFAULT_MAX_INITIAL_LINE_LENGTH,
86               DEFAULT_MAX_HEADER_SIZE,
87               DEFAULT_MAX_CONTENT_LENGTH);
88      }
89  
90      /**
91       * Creates a new instance with the specified parameters.
92       * @param maxInitialLineLength The max allowed length of initial line
93       * @param maxHeaderSize The max allowed size of header
94       * @param maxContentLength The max allowed content length
95       */
96      public RtspDecoder(final int maxInitialLineLength,
97                         final int maxHeaderSize,
98                         final int maxContentLength) {
99          super(maxInitialLineLength, maxHeaderSize, maxContentLength * 2, false);
100     }
101 
102     /**
103      * Creates a new instance with the specified parameters.
104      * @param maxInitialLineLength The max allowed length of initial line
105      * @param maxHeaderSize The max allowed size of header
106      * @param maxContentLength The max allowed content length
107      * @param validateHeaders Set to true if headers should be validated
108      */
109     public RtspDecoder(final int maxInitialLineLength,
110                        final int maxHeaderSize,
111                        final int maxContentLength,
112                        final boolean validateHeaders) {
113         super(maxInitialLineLength,
114               maxHeaderSize,
115               maxContentLength * 2,
116               false,
117               validateHeaders);
118     }
119 
120     @Override
121     protected HttpMessage createMessage(final String[] initialLine)
122             throws Exception {
123         // If the first element of the initial line is a version string then
124         // this is a response
125         if (versionPattern.matcher(initialLine[0]).matches()) {
126             isDecodingRequest = false;
127             return new DefaultHttpResponse(RtspVersions.valueOf(initialLine[0]),
128                 new HttpResponseStatus(Integer.parseInt(initialLine[1]),
129                                        initialLine[2]),
130                 validateHeaders);
131         } else {
132             isDecodingRequest = true;
133             return new DefaultHttpRequest(RtspVersions.valueOf(initialLine[2]),
134                     RtspMethods.valueOf(initialLine[0]),
135                     initialLine[1],
136                     validateHeaders);
137         }
138     }
139 
140     @Override
141     protected boolean isContentAlwaysEmpty(final HttpMessage msg) {
142         // Unlike HTTP, RTSP always assumes zero-length body if Content-Length
143         // header is absent.
144         return super.isContentAlwaysEmpty(msg) || !msg.headers().contains(RtspHeaderNames.CONTENT_LENGTH);
145     }
146 
147     @Override
148     protected HttpMessage createInvalidMessage() {
149         if (isDecodingRequest) {
150             return new DefaultFullHttpRequest(RtspVersions.RTSP_1_0,
151                        RtspMethods.OPTIONS, "/bad-request", validateHeaders);
152         } else {
153             return new DefaultFullHttpResponse(RtspVersions.RTSP_1_0,
154                                                UNKNOWN_STATUS,
155                                                validateHeaders);
156         }
157     }
158 
159     @Override
160     protected boolean isDecodingRequest() {
161         return isDecodingRequest;
162     }
163 }