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 org.jboss.netty.handler.codec.http;
17
18 import java.util.regex.Matcher;
19 import java.util.regex.Pattern;
20
21 /**
22 * The version of HTTP or its derived protocols, such as
23 * <a href="http://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol">RTSP</a> and
24 * <a href="http://en.wikipedia.org/wiki/Internet_Content_Adaptation_Protocol">ICAP</a>.
25 * @apiviz.exclude
26 */
27 public class HttpVersion implements Comparable<HttpVersion> {
28
29 private static final Pattern VERSION_PATTERN =
30 Pattern.compile("(\\S+)/(\\d+)\\.(\\d+)");
31
32 /**
33 * HTTP/1.0
34 */
35 public static final HttpVersion HTTP_1_0 = new HttpVersion("HTTP", 1, 0, false);
36
37 /**
38 * HTTP/1.1
39 */
40 public static final HttpVersion HTTP_1_1 = new HttpVersion("HTTP", 1, 1, true);
41
42 /**
43 * Returns an existing or new {@link HttpVersion} instance which matches to
44 * the specified protocol version string. If the specified {@code text} is
45 * equal to {@code "HTTP/1.0"}, {@link #HTTP_1_0} will be returned. If the
46 * specified {@code text} is equal to {@code "HTTP/1.1"}, {@link #HTTP_1_1}
47 * will be returned. Otherwise, a new {@link HttpVersion} instance will be
48 * returned.
49 */
50 public static HttpVersion valueOf(String text) {
51 if (text == null) {
52 throw new NullPointerException("text");
53 }
54
55 text = text.trim().toUpperCase();
56 if ("HTTP/1.1".equals(text)) {
57 return HTTP_1_1;
58 }
59 if ("HTTP/1.0".equals(text)) {
60 return HTTP_1_0;
61 }
62 return new HttpVersion(text, true);
63 }
64
65 private final String protocolName;
66 private final int majorVersion;
67 private final int minorVersion;
68 private final String text;
69 private final boolean keepAliveDefault;
70
71 /**
72 * @deprecated Use {@link #HttpVersion(String, boolean)} instead.
73 */
74 @Deprecated
75 public HttpVersion(String text) {
76 this(text, true);
77 }
78
79 /**
80 * Creates a new HTTP version with the specified version string. You will
81 * not need to create a new instance unless you are implementing a protocol
82 * derived from HTTP, such as
83 * <a href="http://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol">RTSP</a> and
84 * <a href="http://en.wikipedia.org/wiki/Internet_Content_Adaptation_Protocol">ICAP</a>.
85 *
86 * @param keepAliveDefault
87 * {@code true} if and only if the connection is kept alive unless
88 * the {@code "Connection"} header is set to {@code "close"} explicitly.
89 */
90 public HttpVersion(String text, boolean keepAliveDefault) {
91 if (text == null) {
92 throw new NullPointerException("text");
93 }
94
95 text = text.trim().toUpperCase();
96 if (text.length() == 0) {
97 throw new IllegalArgumentException("empty text");
98 }
99
100 Matcher m = VERSION_PATTERN.matcher(text);
101 if (!m.matches()) {
102 throw new IllegalArgumentException("invalid version format: " + text);
103 }
104
105 protocolName = m.group(1);
106 majorVersion = Integer.parseInt(m.group(2));
107 minorVersion = Integer.parseInt(m.group(3));
108 this.text = protocolName + '/' + majorVersion + '.' + minorVersion;
109 this.keepAliveDefault = keepAliveDefault;
110 }
111
112 /**
113 * @deprecated Use {@link #HttpVersion(String, int, int, boolean)} instead.
114 */
115 @Deprecated
116 public HttpVersion(
117 String protocolName, int majorVersion, int minorVersion) {
118 this(protocolName, majorVersion, minorVersion, true);
119 }
120
121 /**
122 * Creates a new HTTP version with the specified protocol name and version
123 * numbers. You will not need to create a new instance unless you are
124 * implementing a protocol derived from HTTP, such as
125 * <a href="http://en.wikipedia.org/wiki/Real_Time_Streaming_Protocol">RTSP</a> and
126 * <a href="http://en.wikipedia.org/wiki/Internet_Content_Adaptation_Protocol">ICAP</a>
127 *
128 * @param keepAliveDefault
129 * {@code true} if and only if the connection is kept alive unless
130 * the {@code "Connection"} header is set to {@code "close"} explicitly.
131 */
132 public HttpVersion(
133 String protocolName, int majorVersion, int minorVersion,
134 boolean keepAliveDefault) {
135 if (protocolName == null) {
136 throw new NullPointerException("protocolName");
137 }
138
139 protocolName = protocolName.trim().toUpperCase();
140 if (protocolName.length() == 0) {
141 throw new IllegalArgumentException("empty protocolName");
142 }
143
144 for (int i = 0; i < protocolName.length(); i ++) {
145 if (Character.isISOControl(protocolName.charAt(i)) ||
146 Character.isWhitespace(protocolName.charAt(i))) {
147 throw new IllegalArgumentException("invalid character in protocolName");
148 }
149 }
150
151 if (majorVersion < 0) {
152 throw new IllegalArgumentException("negative majorVersion");
153 }
154 if (minorVersion < 0) {
155 throw new IllegalArgumentException("negative minorVersion");
156 }
157
158 this.protocolName = protocolName;
159 this.majorVersion = majorVersion;
160 this.minorVersion = minorVersion;
161 text = protocolName + '/' + majorVersion + '.' + minorVersion;
162 this.keepAliveDefault = keepAliveDefault;
163 }
164
165 /**
166 * Returns the name of the protocol such as {@code "HTTP"} in {@code "HTTP/1.0"}.
167 */
168 public String getProtocolName() {
169 return protocolName;
170 }
171
172 /**
173 * Returns the name of the protocol such as {@code 1} in {@code "HTTP/1.0"}.
174 */
175 public int getMajorVersion() {
176 return majorVersion;
177 }
178
179 /**
180 * Returns the name of the protocol such as {@code 0} in {@code "HTTP/1.0"}.
181 */
182 public int getMinorVersion() {
183 return minorVersion;
184 }
185
186 /**
187 * Returns the full protocol version text such as {@code "HTTP/1.0"}.
188 */
189 public String getText() {
190 return text;
191 }
192
193 /**
194 * Returns {@code true} if and only if the connection is kept alive unless
195 * the {@code "Connection"} header is set to {@code "close"} explicitly.
196 */
197 public boolean isKeepAliveDefault() {
198 return keepAliveDefault;
199 }
200
201 /**
202 * Returns the full protocol version text such as {@code "HTTP/1.0"}.
203 */
204 @Override
205 public String toString() {
206 return getText();
207 }
208
209 @Override
210 public int hashCode() {
211 return (getProtocolName().hashCode() * 31 + getMajorVersion()) * 31 +
212 getMinorVersion();
213 }
214
215 @Override
216 public boolean equals(Object o) {
217 if (!(o instanceof HttpVersion)) {
218 return false;
219 }
220
221 HttpVersion that = (HttpVersion) o;
222 return getMinorVersion() == that.getMinorVersion() &&
223 getMajorVersion() == that.getMajorVersion() &&
224 getProtocolName().equals(that.getProtocolName());
225 }
226
227 public int compareTo(HttpVersion o) {
228 int v = getProtocolName().compareTo(o.getProtocolName());
229 if (v != 0) {
230 return v;
231 }
232
233 v = getMajorVersion() - o.getMajorVersion();
234 if (v != 0) {
235 return v;
236 }
237
238 return getMinorVersion() - o.getMinorVersion();
239 }
240 }