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 static org.jboss.netty.handler.codec.http.cookie.CookieUtil.*;
19
20 import org.jboss.netty.handler.codec.http.HttpHeaderDateFormat;
21 import org.jboss.netty.handler.codec.http.HttpRequest;
22
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.Date;
27 import java.util.List;
28
29 /**
30 * A <a href="http://tools.ietf.org/html/rfc6265">RFC6265</a> compliant cookie encoder to be used server side,
31 * so some fields are sent (Version is typically ignored).
32 *
33 * As Netty's Cookie merges Expires and MaxAge into one single field, only Max-Age field is sent.
34 *
35 * Note that multiple cookies are supposed to be sent at once in a single "Set-Cookie" header.
36 *
37 * <pre>
38 * // Example
39 * {@link HttpRequest} req = ...;
40 * res.setHeader("Cookie", {@link ServerCookieEncoder}.encode("JSESSIONID", "1234"));
41 * </pre>
42 *
43 * @see ServerCookieDecoder
44 */
45 public final class ServerCookieEncoder extends CookieEncoder {
46
47 /**
48 * Strict encoder that validates that name and value chars are in the valid scope
49 * defined in RFC6265
50 */
51 public static final ServerCookieEncoder STRICT = new ServerCookieEncoder(true);
52
53 /**
54 * Lax instance that doesn't validate name and value
55 */
56 public static final ServerCookieEncoder LAX = new ServerCookieEncoder(false);
57
58 private ServerCookieEncoder(boolean strict) {
59 super(strict);
60 }
61
62 /**
63 * Encodes the specified cookie name-value pair into a Set-Cookie header value.
64 *
65 * @param name the cookie name
66 * @param value the cookie value
67 * @return a single Set-Cookie header value
68 */
69 public String encode(String name, String value) {
70 return encode(new DefaultCookie(name, value));
71 }
72
73 /**
74 * Encodes the specified cookie into a Set-Cookie header value.
75 *
76 * @param cookie the cookie
77 * @return a single Set-Cookie header value
78 */
79 public String encode(Cookie cookie) {
80 if (cookie == null) {
81 throw new NullPointerException("cookie");
82 }
83 final String name = cookie.name();
84 final String value = cookie.value() != null ? cookie.value() : "";
85
86 validateCookie(name, value);
87
88 StringBuilder buf = new StringBuilder();
89
90 if (cookie.wrap()) {
91 addQuoted(buf, name, value);
92 } else {
93 add(buf, name, value);
94 }
95
96 if (cookie.maxAge() != Integer.MIN_VALUE) {
97 add(buf, CookieHeaderNames.MAX_AGE, cookie.maxAge());
98 Date expires = new Date((long) cookie.maxAge() * 1000 + System.currentTimeMillis());
99 add(buf, CookieHeaderNames.EXPIRES, HttpHeaderDateFormat.get().format(expires));
100 }
101
102 if (cookie.path() != null) {
103 add(buf, CookieHeaderNames.PATH, cookie.path());
104 }
105
106 if (cookie.domain() != null) {
107 add(buf, CookieHeaderNames.DOMAIN, cookie.domain());
108 }
109 if (cookie.isSecure()) {
110 add(buf, CookieHeaderNames.SECURE);
111 }
112 if (cookie.isHttpOnly()) {
113 add(buf, CookieHeaderNames.HTTPONLY);
114 }
115
116 return stripTrailingSeparator(buf);
117 }
118
119 /**
120 * Batch encodes cookies into Set-Cookie header values.
121 *
122 * @param cookies a bunch of cookies
123 * @return the corresponding bunch of Set-Cookie headers
124 */
125 public List<String> encode(Cookie... cookies) {
126 if (cookies == null) {
127 throw new NullPointerException("cookies");
128 }
129 if (cookies.length == 0) {
130 return Collections.emptyList();
131 }
132
133 List<String> encoded = new ArrayList<String>(cookies.length);
134 for (Cookie c : cookies) {
135 if (c == null) {
136 break;
137 }
138 encoded.add(encode(c));
139 }
140 return encoded;
141 }
142
143 /**
144 * Batch encodes cookies into Set-Cookie header values.
145 *
146 * @param cookies a bunch of cookies
147 * @return the corresponding bunch of Set-Cookie headers
148 */
149 public List<String> encode(Collection<? extends Cookie> cookies) {
150 if (cookies == null) {
151 throw new NullPointerException("cookies");
152 }
153 if (cookies.isEmpty()) {
154 return Collections.emptyList();
155 }
156
157 List<String> encoded = new ArrayList<String>(cookies.size());
158 for (Cookie c : cookies) {
159 if (c == null) {
160 break;
161 }
162 encoded.add(encode(c));
163 }
164 return encoded;
165 }
166
167 /**
168 * Batch encodes cookies into Set-Cookie header values.
169 *
170 * @param cookies a bunch of cookies
171 * @return the corresponding bunch of Set-Cookie headers
172 */
173 public List<String> encode(Iterable<? extends Cookie> cookies) {
174 if (cookies == null) {
175 throw new NullPointerException("cookies");
176 }
177 if (cookies.iterator().hasNext()) {
178 return Collections.emptyList();
179 }
180
181 List<String> encoded = new ArrayList<String>();
182 for (Cookie c : cookies) {
183 if (c == null) {
184 break;
185 }
186 encoded.add(encode(c));
187 }
188 return encoded;
189 }
190 }