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 org.jboss.netty.handler.codec.http.cookie;
17
18 import java.util.Collections;
19 import java.util.Set;
20 import java.util.TreeSet;
21
22 /**
23 * A <a href="http://tools.ietf.org/html/rfc6265">RFC6265</a> compliant cookie decoder to be used server side.
24 *
25 * Only name and value fields are expected, so old fields are not populated (path, domain, etc).
26 *
27 * Old <a href="http://tools.ietf.org/html/rfc2965">RFC2965</a> cookies are still supported,
28 * old fields will simply be ignored.
29 *
30 * @see ServerCookieEncoder
31 */
32 public final class ServerCookieDecoder extends CookieDecoder {
33
34 private static final String RFC2965_VERSION = "$Version";
35
36 private static final String RFC2965_PATH = "$" + CookieHeaderNames.PATH;
37
38 private static final String RFC2965_DOMAIN = "$" + CookieHeaderNames.DOMAIN;
39
40 private static final String RFC2965_PORT = "$Port";
41
42 /**
43 * Strict encoder that validates that name and value chars are in the valid scope
44 * defined in RFC6265
45 */
46 public static final ServerCookieDecoder STRICT = new ServerCookieDecoder(true);
47
48 /**
49 * Lax instance that doesn't validate name and value
50 */
51 public static final ServerCookieDecoder LAX = new ServerCookieDecoder(false);
52
53 private ServerCookieDecoder(boolean strict) {
54 super(strict);
55 }
56
57 /**
58 * Decodes the specified Set-Cookie HTTP header value into a {@link Cookie}.
59 *
60 * @return the decoded {@link Cookie}
61 */
62 public Set<Cookie> decode(String header) {
63 if (header == null) {
64 throw new NullPointerException("header");
65 }
66 final int headerLen = header.length();
67
68 if (headerLen == 0) {
69 return Collections.emptySet();
70 }
71
72 Set<Cookie> cookies = new TreeSet<Cookie>();
73
74 int i = 0;
75
76 boolean rfc2965Style = false;
77 if (header.regionMatches(true, 0, RFC2965_VERSION, 0, RFC2965_VERSION.length())) {
78 // RFC 2965 style cookie, move to after version value
79 i = header.indexOf(';') + 1;
80 rfc2965Style = true;
81 }
82
83 loop: for (;;) {
84
85 // Skip spaces and separators.
86 for (;;) {
87 if (i == headerLen) {
88 break loop;
89 }
90 char c = header.charAt(i);
91 if (c == '\t' || c == '\n' || c == 0x0b || c == '\f'
92 || c == '\r' || c == ' ' || c == ',' || c == ';') {
93 i++;
94 continue;
95 }
96 break;
97 }
98
99 int nameBegin = i;
100 int nameEnd = i;
101 int valueBegin = -1;
102 int valueEnd = -1;
103
104 if (i != headerLen) {
105 keyValLoop: for (;;) {
106
107 char curChar = header.charAt(i);
108 if (curChar == ';') {
109 // NAME; (no value till ';')
110 nameEnd = i;
111 valueBegin = valueEnd = -1;
112 break keyValLoop;
113
114 } else if (curChar == '=') {
115 // NAME=VALUE
116 nameEnd = i;
117 i++;
118 if (i == headerLen) {
119 // NAME= (empty value, i.e. nothing after '=')
120 valueBegin = valueEnd = 0;
121 break keyValLoop;
122 }
123
124 valueBegin = i;
125 // NAME=VALUE;
126 int semiPos = header.indexOf(';', i);
127 valueEnd = i = semiPos > 0 ? semiPos : headerLen;
128 break keyValLoop;
129 } else {
130 i++;
131 }
132
133 if (i == headerLen) {
134 // NAME (no value till the end of string)
135 nameEnd = headerLen;
136 valueBegin = valueEnd = -1;
137 break;
138 }
139 }
140 }
141
142 if (rfc2965Style && (header.regionMatches(nameBegin, RFC2965_PATH, 0, RFC2965_PATH.length()) ||
143 header.regionMatches(nameBegin, RFC2965_DOMAIN, 0, RFC2965_DOMAIN.length()) ||
144 header.regionMatches(nameBegin, RFC2965_PORT, 0, RFC2965_PORT.length()))) {
145
146 // skip obsolete RFC2965 fields
147 continue;
148 }
149
150 DefaultCookie cookie = initCookie(header, nameBegin, nameEnd, valueBegin, valueEnd);
151 if (cookie != null) {
152 cookies.add(cookie);
153 }
154 }
155
156 return cookies;
157 }
158 }