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.netty5.handler.codec.rtsp;
17  
18  import io.netty5.channel.ChannelHandlerContext;
19  import io.netty5.handler.codec.http.DefaultFullHttpRequest;
20  import io.netty5.handler.codec.http.DefaultFullHttpResponse;
21  import io.netty5.handler.codec.http.DefaultHttpRequest;
22  import io.netty5.handler.codec.http.DefaultHttpResponse;
23  import io.netty5.handler.codec.http.HttpMessage;
24  import io.netty5.handler.codec.http.HttpObjectDecoder;
25  import io.netty5.handler.codec.http.HttpResponseStatus;
26  
27  import java.util.regex.Pattern;
28  
29  /**
30   * Decodes {@link io.netty5.buffer.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 io.netty5.handler.codec.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 io.netty5.handler.codec.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 io.netty5.handler.codec.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 content length.
77       */
78      public static final int DEFAULT_MAX_CONTENT_LENGTH = 8192;
79  
80      /**
81       * Creates a new instance with the default
82       * {@code maxInitialLineLength (4096)}, {@code maxHeaderSize (8192)}, and
83       * {@code maxContentLength (8192)}.
84       */
85      public RtspDecoder() {
86          this(DEFAULT_MAX_INITIAL_LINE_LENGTH,
87               DEFAULT_MAX_HEADER_SIZE,
88               DEFAULT_MAX_CONTENT_LENGTH);
89      }
90  
91      /**
92       * Creates a new instance with the specified parameters.
93       * @param maxInitialLineLength The max allowed length of initial line
94       * @param maxHeaderSize The max allowed size of header
95       * @param maxContentLength The max allowed content length
96       */
97      public RtspDecoder(final int maxInitialLineLength,
98                         final int maxHeaderSize,
99                         final int maxContentLength) {
100         super(maxInitialLineLength, maxHeaderSize, false);
101     }
102 
103     /**
104      * Creates a new instance with the specified parameters.
105      * @param maxInitialLineLength The max allowed length of initial line
106      * @param maxHeaderSize The max allowed size of header
107      * @param maxContentLength The max allowed content length
108      * @param validateHeaders Set to true if headers should be validated
109      */
110     public RtspDecoder(final int maxInitialLineLength,
111                        final int maxHeaderSize,
112                        final int maxContentLength,
113                        final boolean validateHeaders) {
114         super(maxInitialLineLength,
115               maxHeaderSize,
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(ChannelHandlerContext ctx) {
149         if (isDecodingRequest) {
150             return new DefaultFullHttpRequest(RtspVersions.RTSP_1_0, RtspMethods.OPTIONS, "/bad-request",
151                     ctx.bufferAllocator().allocate(0), validateHeaders);
152         } else {
153             return new DefaultFullHttpResponse(RtspVersions.RTSP_1_0, UNKNOWN_STATUS,
154                     ctx.bufferAllocator().allocate(0), validateHeaders);
155         }
156     }
157 
158     @Override
159     protected boolean isDecodingRequest() {
160         return isDecodingRequest;
161     }
162 }