/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.http.cookie;
import io.netty.handler.codec.http.
HttpConstants;
import io.netty.util.internal.
InternalThreadLocalMap;
import java.util.
BitSet;
final class
CookieUtil {
private static final
BitSet VALID_COOKIE_NAME_OCTETS =
validCookieNameOctets();
private static final
BitSet VALID_COOKIE_VALUE_OCTETS =
validCookieValueOctets();
private static final
BitSet VALID_COOKIE_ATTRIBUTE_VALUE_OCTETS =
validCookieAttributeValueOctets();
// token = 1*<any CHAR except CTLs or separators>
// separators = "(" | ")" | "<" | ">" | "@"
// | "," | ";" | ":" | "\" | <">
// | "/" | "[" | "]" | "?" | "="
// | "{" | "}" | SP | HT
private static
BitSet validCookieNameOctets() {
BitSet bits = new
BitSet();
for (int
i = 32;
i < 127;
i++) {
bits.
set(
i);
}
int[]
separators = new int[]
{ '(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' ', '\t' };
for (int
separator :
separators) {
bits.
set(
separator, false);
}
return
bits;
}
// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
// US-ASCII characters excluding CTLs, whitespace, DQUOTE, comma, semicolon, and backslash
private static
BitSet validCookieValueOctets() {
BitSet bits = new
BitSet();
bits.
set(0x21);
for (int
i = 0x23;
i <= 0x2B;
i++) {
bits.
set(
i);
}
for (int
i = 0x2D;
i <= 0x3A;
i++) {
bits.
set(
i);
}
for (int
i = 0x3C;
i <= 0x5B;
i++) {
bits.
set(
i);
}
for (int
i = 0x5D;
i <= 0x7E;
i++) {
bits.
set(
i);
}
return
bits;
}
// path-value = <any CHAR except CTLs or ";">
private static
BitSet validCookieAttributeValueOctets() {
BitSet bits = new
BitSet();
for (int
i = 32;
i < 127;
i++) {
bits.
set(
i);
}
bits.
set(';', false);
return
bits;
}
static
StringBuilder stringBuilder() {
return
InternalThreadLocalMap.
get().
stringBuilder();
}
/**
* @param buf a buffer where some cookies were maybe encoded
* @return the buffer String without the trailing separator, or null if no cookie was appended.
*/
static
String stripTrailingSeparatorOrNull(
StringBuilder buf) {
return
buf.
length() == 0 ? null :
stripTrailingSeparator(
buf);
}
static
String stripTrailingSeparator(
StringBuilder buf) {
if (
buf.
length() > 0) {
buf.
setLength(
buf.
length() - 2);
}
return
buf.
toString();
}
static void
add(
StringBuilder sb,
String name, long
val) {
sb.
append(
name);
sb.
append((char)
HttpConstants.
EQUALS);
sb.
append(
val);
sb.
append((char)
HttpConstants.
SEMICOLON);
sb.
append((char)
HttpConstants.
SP);
}
static void
add(
StringBuilder sb,
String name,
String val) {
sb.
append(
name);
sb.
append((char)
HttpConstants.
EQUALS);
sb.
append(
val);
sb.
append((char)
HttpConstants.
SEMICOLON);
sb.
append((char)
HttpConstants.
SP);
}
static void
add(
StringBuilder sb,
String name) {
sb.
append(
name);
sb.
append((char)
HttpConstants.
SEMICOLON);
sb.
append((char)
HttpConstants.
SP);
}
static void
addQuoted(
StringBuilder sb,
String name,
String val) {
if (
val == null) {
val = "";
}
sb.
append(
name);
sb.
append((char)
HttpConstants.
EQUALS);
sb.
append((char)
HttpConstants.
DOUBLE_QUOTE);
sb.
append(
val);
sb.
append((char)
HttpConstants.
DOUBLE_QUOTE);
sb.
append((char)
HttpConstants.
SEMICOLON);
sb.
append((char)
HttpConstants.
SP);
}
static int
firstInvalidCookieNameOctet(
CharSequence cs) {
return
firstInvalidOctet(
cs,
VALID_COOKIE_NAME_OCTETS);
}
static int
firstInvalidCookieValueOctet(
CharSequence cs) {
return
firstInvalidOctet(
cs,
VALID_COOKIE_VALUE_OCTETS);
}
static int
firstInvalidOctet(
CharSequence cs,
BitSet bits) {
for (int
i = 0;
i <
cs.
length();
i++) {
char
c =
cs.
charAt(
i);
if (!
bits.
get(
c)) {
return
i;
}
}
return -1;
}
static
CharSequence unwrapValue(
CharSequence cs) {
final int
len =
cs.
length();
if (
len > 0 &&
cs.
charAt(0) == '"') {
if (
len >= 2 &&
cs.
charAt(
len - 1) == '"') {
// properly balanced
return
len == 2 ? "" :
cs.
subSequence(1,
len - 1);
} else {
return null;
}
}
return
cs;
}
static
String validateAttributeValue(
String name,
String value) {
if (
value == null) {
return null;
}
value =
value.
trim();
if (
value.
isEmpty()) {
return null;
}
int
i =
firstInvalidOctet(
value,
VALID_COOKIE_ATTRIBUTE_VALUE_OCTETS);
if (
i != -1) {
throw new
IllegalArgumentException(
name + " contains the prohibited characters: " +
value.
charAt(
i));
}
return
value;
}
private
CookieUtil() {
// Unused
}
}