上一篇博客Tomcat源码学习--Session创建销毁中我们学习了Session创建、使用和销毁的操作,接下来我们看看Tomcat中对Cookie是如何做处理的。首先我们要了解Cookie是由浏览器创建、管理和销毁操作的,Tomcat只能获取浏览器请求过来的cookie值,或者创建cookie。
一、获取Cookie
1、通过request获取cookie
Cookie [] cookies = request.getCookies();
在Request的外观类RequestFacade类中提供getCookies()方法
@Override
public Cookie[] getCookies() {
if (request == null) {
throw new IllegalStateException(
sm.getString("requestFacade.nullRequest"));
}
Cookie[] ret = null;
/*
* Clone the returned array only if there is a security manager
* in place, so that performance won't suffer in the non-secure case
*/
if (SecurityUtil.isPackageProtectionEnabled()){
ret = AccessController.doPrivileged(
new GetCookiesPrivilegedAction());
if (ret != null) {
ret = ret.clone();
}
} else {
ret = request.getCookies();
}
return ret;
}
在tomcat的类Rquest中提供getCookies()方法,cookiesConverted会将浏览器发送过来的cookie数据进行解析
@Override
public Cookie[] getCookies() {
if (!cookiesConverted) {
convertCookies();
}
return cookies;
}
covertCookies中会对cookie进行转换并返回
protected void convertCookies() {
if (cookiesConverted) {
return;
}
cookiesConverted = true;
if (getContext() == null) {
return;
}
//解析cookie,浏览器将cookie数据添加到header中发送到后端
parseCookies();
ServerCookies serverCookies = coyoteRequest.getCookies();
CookieProcessor cookieProcessor = getContext().getCookieProcessor();
int count = serverCookies.getCookieCount();
if (count <= 0) {
return;
}
cookies = new Cookie[count];
//获取解析后的cookie并组装后返回
int idx=0;
for (int i = 0; i < count; i++) {
ServerCookie scookie = serverCookies.getCookie(i);
try {
/*
we must unescape the '\\' escape character
*/
Cookie cookie = new Cookie(scookie.getName().toString(),null);
int version = scookie.getVersion();
cookie.setVersion(version);
scookie.getValue().getByteChunk().setCharset(cookieProcessor.getCharset());
cookie.setValue(unescape(scookie.getValue().toString()));
cookie.setPath(unescape(scookie.getPath().toString()));
String domain = scookie.getDomain().toString();
if (domain!=null)
{
cookie.setDomain(unescape(domain));//avoid NPE
}
String comment = scookie.getComment().toString();
cookie.setComment(version==1?unescape(comment):null);
cookies[idx++] = cookie;
} catch(IllegalArgumentException e) {
// Ignore bad cookie
}
}
if( idx < count ) {
Cookie [] ncookies = new Cookie[idx];
System.arraycopy(cookies, 0, ncookies, 0, idx);
cookies = ncookies;
}
}
parseCookies()方法会将浏览器发送过来的header中的cookie提取出来
protected void parseCookies() {
if (cookiesParsed) {
return;
}
cookiesParsed = true;
ServerCookies serverCookies = coyoteRequest.getCookies();
serverCookies.setLimit(connector.getMaxCookieCount());
CookieProcessor cookieProcessor = getContext().getCookieProcessor();
cookieProcessor.parseCookieHeader(coyoteRequest.getMimeHeaders(), serverCookies);
}
CookieProcessor的实现类Rfc6265CookieProcessor会根据一定的规则对头部的cookie解析并转换为Cookie数组
在浏览器发送到后台的数据中Cookie是一个字符串,通过;分好进行数据分割。
@Override
public void parseCookieHeader(MimeHeaders headers,
ServerCookies serverCookies) {
if (headers == null) {
// nothing to process
return;
}
// process each "cookie" header
//查找头部中key为Cookie的值
int pos = headers.findHeader("Cookie", 0);
while (pos >= 0) {
MessageBytes cookieValue = headers.getValue(pos);
if (cookieValue != null && !cookieValue.isNull() ) {
if (cookieValue.getType() != MessageBytes.T_BYTES ) {
if (log.isDebugEnabled()) {
Exception e = new Exception();
// TODO: Review this in light of HTTP/2
log.debug("Cookies: Parsing cookie as String. Expected bytes.", e);
}
cookieValue.toBytes();
}
if (log.isDebugEnabled()) {
log.debug("Cookies: Parsing b[]: " + cookieValue.toString());
}
ByteChunk bc = cookieValue.getByteChunk();
Cookie.parseCookie(bc.getBytes(), bc.getOffset(), bc.getLength(),
serverCookies);
}
// search from the next position
pos = headers.findHeader("Cookie", ++pos);
}
}
这样就可以从头部中获取所有的Cookie数据,并返回Cookie的数组集合。
二、Cookie销毁
cookie会有日期生存时间,cookie的销毁操作是浏览器来实现的,tomcat无法完成cookie销毁操作。
三、Cookie创建
cookie创建简单来说就是tomcat通过设置一定的key、value和生存时间让浏览器来创建cookie。
调用HttpServletResponse的addCookie来创建cookie
Cookie testCookie = new Cookie("test", "test");
testCookie.setMaxAge(30);
response.addCookie(testCookie);
HttpServletResponse的实现类ResponseFacade调用addCookie
@Override
public void addCookie(Cookie cookie) {
if (isCommitted()) {
return;
}
response.addCookie(cookie);
}
Response的addCookie中会将cookie对象转为String数据并添加到头部中
@Override
public void addCookie(final Cookie cookie) {
// Ignore any call from an included servlet
if (included || isCommitted()) {
return;
}
cookies.add(cookie);
//将cookie对象转为String头部数据
String header = generateCookieString(cookie);
//if we reached here, no exception, cookie is valid
// the header name is Set-Cookie for both "old" and v.1 ( RFC2109 )
// RFC2965 is not supported by browsers and the Servlet spec
// asks for 2109.
//将cookie值添加到头部中
addHeader("Set-Cookie", header, getContext().getCookieProcessor().getCharset());
}