Java tutorial
/* * Copyright 2012 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; import static io.netty.handler.codec.http.CookieUtil.firstInvalidCookieNameOctet; import static io.netty.handler.codec.http.CookieUtil.firstInvalidCookieValueOctet; import static io.netty.handler.codec.http.CookieUtil.unwrapValue; import io.netty.handler.codec.DateFormatter; import io.netty.handler.codec.http.cookie.CookieHeaderNames; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Set; import java.util.TreeSet; /** * @deprecated Use {@link io.netty.handler.codec.http.cookie.ClientCookieDecoder} * or {@link io.netty.handler.codec.http.cookie.ServerCookieDecoder} instead. * * Decodes an HTTP header value into {@link Cookie}s. This decoder can decode * the HTTP cookie version 0, 1, and 2. * * <pre> * {@link HttpRequest} req = ...; * String value = req.getHeader("Cookie"); * Set<{@link Cookie}> cookies = {@link CookieDecoder}.decode(value); * </pre> * * @see io.netty.handler.codec.http.cookie.ClientCookieDecoder * @see io.netty.handler.codec.http.cookie.ServerCookieDecoder */ @Deprecated public final class CookieDecoder { private final InternalLogger logger = InternalLoggerFactory.getInstance(getClass()); private static final CookieDecoder STRICT = new CookieDecoder(true); private static final CookieDecoder LAX = new CookieDecoder(false); private static final String COMMENT = "Comment"; private static final String COMMENTURL = "CommentURL"; private static final String DISCARD = "Discard"; private static final String PORT = "Port"; private static final String VERSION = "Version"; private final boolean strict; public static Set<Cookie> decode(String header) { return decode(header, true); } public static Set<Cookie> decode(String header, boolean strict) { return (strict ? STRICT : LAX).doDecode(header); } /** * Decodes the specified HTTP header value into {@link Cookie}s. * * @return the decoded {@link Cookie}s */ private Set<Cookie> doDecode(String header) { List<String> names = new ArrayList<String>(8); List<String> values = new ArrayList<String>(8); extractKeyValuePairs(header, names, values); if (names.isEmpty()) { return Collections.emptySet(); } int i; int version = 0; // $Version is the only attribute that can appear before the actual // cookie name-value pair. if (names.get(0).equalsIgnoreCase(VERSION)) { try { version = Integer.parseInt(values.get(0)); } catch (NumberFormatException e) { // Ignore. } i = 1; } else { i = 0; } if (names.size() <= i) { // There's a version attribute, but nothing more. return Collections.emptySet(); } Set<Cookie> cookies = new TreeSet<Cookie>(); for (; i < names.size(); i++) { String name = names.get(i); String value = values.get(i); if (value == null) { value = ""; } Cookie c = initCookie(name, value); if (c == null) { break; } boolean discard = false; boolean secure = false; boolean httpOnly = false; String comment = null; String commentURL = null; String domain = null; String path = null; long maxAge = Long.MIN_VALUE; List<Integer> ports = new ArrayList<Integer>(2); for (int j = i + 1; j < names.size(); j++, i++) { name = names.get(j); value = values.get(j); if (DISCARD.equalsIgnoreCase(name)) { discard = true; } else if (CookieHeaderNames.SECURE.equalsIgnoreCase(name)) { secure = true; } else if (CookieHeaderNames.HTTPONLY.equalsIgnoreCase(name)) { httpOnly = true; } else if (COMMENT.equalsIgnoreCase(name)) { comment = value; } else if (COMMENTURL.equalsIgnoreCase(name)) { commentURL = value; } else if (CookieHeaderNames.DOMAIN.equalsIgnoreCase(name)) { domain = value; } else if (CookieHeaderNames.PATH.equalsIgnoreCase(name)) { path = value; } else if (CookieHeaderNames.EXPIRES.equalsIgnoreCase(name)) { Date date = DateFormatter.parseHttpDate(value); if (date != null) { long maxAgeMillis = date.getTime() - System.currentTimeMillis(); maxAge = maxAgeMillis / 1000 + (maxAgeMillis % 1000 != 0 ? 1 : 0); } } else if (CookieHeaderNames.MAX_AGE.equalsIgnoreCase(name)) { maxAge = Integer.parseInt(value); } else if (VERSION.equalsIgnoreCase(name)) { version = Integer.parseInt(value); } else if (PORT.equalsIgnoreCase(name)) { String[] portList = value.split(","); for (String s1 : portList) { try { ports.add(Integer.valueOf(s1)); } catch (NumberFormatException e) { // Ignore. } } } else { break; } } c.setVersion(version); c.setMaxAge(maxAge); c.setPath(path); c.setDomain(domain); c.setSecure(secure); c.setHttpOnly(httpOnly); if (version > 0) { c.setComment(comment); } if (version > 1) { c.setCommentUrl(commentURL); c.setPorts(ports); c.setDiscard(discard); } cookies.add(c); } return cookies; } private static void extractKeyValuePairs(final String header, final List<String> names, final List<String> values) { final int headerLen = header.length(); loop: for (int i = 0;;) { // Skip spaces and separators. for (;;) { if (i == headerLen) { break loop; } switch (header.charAt(i)) { case '\t': case '\n': case 0x0b: case '\f': case '\r': case ' ': case ',': case ';': i++; continue; } break; } // Skip '$'. for (;;) { if (i == headerLen) { break loop; } if (header.charAt(i) == '$') { i++; continue; } break; } String name; String value; if (i == headerLen) { name = null; value = null; } else { int newNameStart = i; keyValLoop: for (;;) { switch (header.charAt(i)) { case ';': // NAME; (no value till ';') name = header.substring(newNameStart, i); value = null; break keyValLoop; case '=': // NAME=VALUE name = header.substring(newNameStart, i); i++; if (i == headerLen) { // NAME= (empty value, i.e. nothing after '=') value = ""; break keyValLoop; } int newValueStart = i; char c = header.charAt(i); if (c == '"' || c == '\'') { // NAME="VALUE" or NAME='VALUE' StringBuilder newValueBuf = new StringBuilder(header.length() - i); final char q = c; boolean hadBackslash = false; i++; for (;;) { if (i == headerLen) { value = newValueBuf.toString(); break keyValLoop; } if (hadBackslash) { hadBackslash = false; c = header.charAt(i++); switch (c) { case '\\': case '"': case '\'': // Escape last backslash. newValueBuf.setCharAt(newValueBuf.length() - 1, c); break; default: // Do not escape last backslash. newValueBuf.append(c); } } else { c = header.charAt(i++); if (c == q) { value = newValueBuf.toString(); break keyValLoop; } newValueBuf.append(c); if (c == '\\') { hadBackslash = true; } } } } else { // NAME=VALUE; int semiPos = header.indexOf(';', i); if (semiPos > 0) { value = header.substring(newValueStart, semiPos); i = semiPos; } else { value = header.substring(newValueStart); i = headerLen; } } break keyValLoop; default: i++; } if (i == headerLen) { // NAME (no value till the end of string) name = header.substring(newNameStart); value = null; break; } } } names.add(name); values.add(value); } } private CookieDecoder(boolean strict) { this.strict = strict; } private DefaultCookie initCookie(String name, String value) { if (name == null || name.length() == 0) { logger.debug("Skipping cookie with null name"); return null; } if (value == null) { logger.debug("Skipping cookie with null value"); return null; } CharSequence unwrappedValue = unwrapValue(value); if (unwrappedValue == null) { logger.debug("Skipping cookie because starting quotes are not properly balanced in '{}'", unwrappedValue); return null; } int invalidOctetPos; if (strict && (invalidOctetPos = firstInvalidCookieNameOctet(name)) >= 0) { if (logger.isDebugEnabled()) { logger.debug("Skipping cookie because name '{}' contains invalid char '{}'", name, name.charAt(invalidOctetPos)); } return null; } final boolean wrap = unwrappedValue.length() != value.length(); if (strict && (invalidOctetPos = firstInvalidCookieValueOctet(unwrappedValue)) >= 0) { if (logger.isDebugEnabled()) { logger.debug("Skipping cookie because value '{}' contains invalid char '{}'", unwrappedValue, unwrappedValue.charAt(invalidOctetPos)); } return null; } DefaultCookie cookie = new DefaultCookie(name, unwrappedValue.toString()); cookie.setWrap(wrap); return cookie; } }