1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http.cookie;
17
18 import io.netty.handler.codec.DateFormatter;
19 import io.netty.handler.codec.http.HttpConstants;
20 import io.netty.handler.codec.http.HttpResponse;
21
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Date;
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30
31 import static io.netty.handler.codec.http.cookie.CookieUtil.add;
32 import static io.netty.handler.codec.http.cookie.CookieUtil.addQuoted;
33 import static io.netty.handler.codec.http.cookie.CookieUtil.stringBuilder;
34 import static io.netty.handler.codec.http.cookie.CookieUtil.stripTrailingSeparator;
35 import static io.netty.util.internal.ObjectUtil.checkNotNull;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 public final class ServerCookieEncoder extends CookieEncoder {
54
55
56
57
58
59
60
61 public static final ServerCookieEncoder STRICT = new ServerCookieEncoder(true);
62
63
64
65
66
67 public static final ServerCookieEncoder LAX = new ServerCookieEncoder(false);
68
69 private ServerCookieEncoder(boolean strict) {
70 super(strict);
71 }
72
73
74
75
76
77
78
79
80 public String encode(String name, String value) {
81 return encode(new DefaultCookie(name, value));
82 }
83
84
85
86
87
88
89
90 public String encode(Cookie cookie) {
91 final String name = checkNotNull(cookie, "cookie").name();
92 final String value = cookie.value() != null ? cookie.value() : "";
93
94 validateCookie(name, value);
95
96 StringBuilder buf = stringBuilder();
97
98 if (cookie.wrap()) {
99 addQuoted(buf, name, value);
100 } else {
101 add(buf, name, value);
102 }
103
104 if (cookie.maxAge() != Long.MIN_VALUE) {
105 add(buf, CookieHeaderNames.MAX_AGE, cookie.maxAge());
106 Date expires = new Date(cookie.maxAge() * 1000 + System.currentTimeMillis());
107 buf.append(CookieHeaderNames.EXPIRES);
108 buf.append('=');
109 DateFormatter.append(expires, buf);
110 buf.append(';');
111 buf.append(HttpConstants.SP_CHAR);
112 }
113
114 if (cookie.path() != null) {
115 add(buf, CookieHeaderNames.PATH, cookie.path());
116 }
117
118 if (cookie.domain() != null) {
119 add(buf, CookieHeaderNames.DOMAIN, cookie.domain());
120 }
121 if (cookie.isSecure()) {
122 add(buf, CookieHeaderNames.SECURE);
123 }
124 if (cookie.isHttpOnly()) {
125 add(buf, CookieHeaderNames.HTTPONLY);
126 }
127 if (cookie instanceof DefaultCookie) {
128 DefaultCookie c = (DefaultCookie) cookie;
129 if (c.sameSite() != null) {
130 add(buf, CookieHeaderNames.SAMESITE, c.sameSite().name());
131 }
132 if (c.isPartitioned()) {
133 add(buf, CookieHeaderNames.PARTITIONED);
134 }
135 }
136
137 return stripTrailingSeparator(buf);
138 }
139
140
141
142
143
144
145
146 private static List<String> dedup(List<String> encoded, Map<String, Integer> nameToLastIndex) {
147 boolean[] isLastInstance = new boolean[encoded.size()];
148 for (int idx : nameToLastIndex.values()) {
149 isLastInstance[idx] = true;
150 }
151 List<String> dedupd = new ArrayList<String>(nameToLastIndex.size());
152 for (int i = 0, n = encoded.size(); i < n; i++) {
153 if (isLastInstance[i]) {
154 dedupd.add(encoded.get(i));
155 }
156 }
157 return dedupd;
158 }
159
160
161
162
163
164
165
166 public List<String> encode(Cookie... cookies) {
167 if (checkNotNull(cookies, "cookies").length == 0) {
168 return Collections.emptyList();
169 }
170
171 List<String> encoded = new ArrayList<String>(cookies.length);
172 Map<String, Integer> nameToIndex = strict && cookies.length > 1 ? new HashMap<String, Integer>() : null;
173 boolean hasDupdName = false;
174 for (int i = 0; i < cookies.length; i++) {
175 Cookie c = cookies[i];
176 encoded.add(encode(c));
177 if (nameToIndex != null) {
178 hasDupdName |= nameToIndex.put(c.name(), i) != null;
179 }
180 }
181 return hasDupdName ? dedup(encoded, nameToIndex) : encoded;
182 }
183
184
185
186
187
188
189
190 public List<String> encode(Collection<? extends Cookie> cookies) {
191 if (checkNotNull(cookies, "cookies").isEmpty()) {
192 return Collections.emptyList();
193 }
194
195 List<String> encoded = new ArrayList<String>(cookies.size());
196 Map<String, Integer> nameToIndex = strict && cookies.size() > 1 ? new HashMap<String, Integer>() : null;
197 int i = 0;
198 boolean hasDupdName = false;
199 for (Cookie c : cookies) {
200 encoded.add(encode(c));
201 if (nameToIndex != null) {
202 hasDupdName |= nameToIndex.put(c.name(), i++) != null;
203 }
204 }
205 return hasDupdName ? dedup(encoded, nameToIndex) : encoded;
206 }
207
208
209
210
211
212
213
214 public List<String> encode(Iterable<? extends Cookie> cookies) {
215 Iterator<? extends Cookie> cookiesIt = checkNotNull(cookies, "cookies").iterator();
216 if (!cookiesIt.hasNext()) {
217 return Collections.emptyList();
218 }
219
220 List<String> encoded = new ArrayList<String>();
221 Cookie firstCookie = cookiesIt.next();
222 Map<String, Integer> nameToIndex = strict && cookiesIt.hasNext() ? new HashMap<String, Integer>() : null;
223 int i = 0;
224 encoded.add(encode(firstCookie));
225 boolean hasDupdName = nameToIndex != null && nameToIndex.put(firstCookie.name(), i++) != null;
226 while (cookiesIt.hasNext()) {
227 Cookie c = cookiesIt.next();
228 encoded.add(encode(c));
229 if (nameToIndex != null) {
230 hasDupdName |= nameToIndex.put(c.name(), i++) != null;
231 }
232 }
233 return hasDupdName ? dedup(encoded, nameToIndex) : encoded;
234 }
235 }