/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* All rights reserved.
*
* Licensed 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 com.alibaba.citrus.service.requestcontext.basic.impl;
import static com.alibaba.citrus.util.Assert.*;
import static com.alibaba.citrus.util.StringUtil.*;
import java.io.IOException;
import java.net.URI;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.citrus.service.requestcontext.RequestContext;
import com.alibaba.citrus.service.requestcontext.basic.CookieHeaderValueInterceptor;
import com.alibaba.citrus.service.requestcontext.basic.CookieInterceptor;
import com.alibaba.citrus.service.requestcontext.basic.CookieRejectedException;
import com.alibaba.citrus.service.requestcontext.basic.HeaderNameInterceptor;
import com.alibaba.citrus.service.requestcontext.basic.HeaderValueInterceptor;
import com.alibaba.citrus.service.requestcontext.basic.RedirectLocationInterceptor;
import com.alibaba.citrus.service.requestcontext.basic.RedirectLocationRejectedException;
import com.alibaba.citrus.service.requestcontext.basic.RequestContextLifecycleInterceptor;
import com.alibaba.citrus.service.requestcontext.basic.ResponseHeaderRejectedException;
import com.alibaba.citrus.service.requestcontext.basic.StatusMessageInterceptor;
import com.alibaba.citrus.service.requestcontext.support.AbstractResponseWrapper;
import com.alibaba.citrus.service.requestcontext.util.CookieSupport;
import com.alibaba.citrus.util.StringEscapeUtil;
/**
* 包裹<code>HttpServletResponse</code>,使之具备:
* <ul>
* <li>Header的安全性:过滤CRLF。</li>
* <li>Cookie的安全性:限制cookie的大小。</li>
* </ul>
*
* @author Michael Zhou
*/
public class BasicResponseImpl extends AbstractResponseWrapper {
private static final String LOCATION_HEADER = "Location";
private static final String SET_COOKIE_HEADER = "Set-Cookie";
private final Object[] interceptors;
public BasicResponseImpl(RequestContext context, HttpServletResponse response, Object[] interceptors) {
super(context, response);
if (interceptors == null) {
this.interceptors = new Object[0];
} else {
this.interceptors = interceptors;
}
}
@Override
public void addDateHeader(String name, long date) {
super.addDateHeader(checkHeaderName(name), date);
}
@Override
public void setDateHeader(String name, long date) {
super.setDateHeader(checkHeaderName(name), date);
}
@Override
public void addIntHeader(String name, int value) {
super.addIntHeader(checkHeaderName(name), value);
}
@Override
public void setIntHeader(String name, int value) {
super.setIntHeader(checkHeaderName(name), value);
}
@Override
public void addHeader(String name, String value) {
name = trimToNull(name);
if (LOCATION_HEADER.equalsIgnoreCase(name)) {
value = checkRedirectLocation(value, false);
if (value != null) {
super.setHeader(LOCATION_HEADER, value); // force SET header
}
} else if (SET_COOKIE_HEADER.equalsIgnoreCase(name)) {
value = checkCookieHeaderValue(name, value, false);
if (value != null) {
super.addHeader(SET_COOKIE_HEADER, value);
}
} else {
name = checkHeaderName(name);
value = checkHeaderValue(name, value);
if (value != null) {
super.addHeader(name, value);
}
}
}
@Override
public void setHeader(String name, String value) {
name = trimToNull(name);
if (LOCATION_HEADER.equalsIgnoreCase(name)) {
value = checkRedirectLocation(value, false);
if (value != null) {
super.setHeader(LOCATION_HEADER, value);
}
} else if (SET_COOKIE_HEADER.equalsIgnoreCase(name)) {
value = checkCookieHeaderValue(name, value, true);
if (value != null) {
super.setHeader(SET_COOKIE_HEADER, value);
}
} else {
name = checkHeaderName(name);
value = checkHeaderValue(name, value);
if (value != null) {
super.setHeader(name, value);
}
}
}
private String checkHeaderName(String name) throws ResponseHeaderRejectedException {
String newName = assertNotNull(name, "header name is null"); // name==null报错
for (Object interceptor : interceptors) {
if (interceptor instanceof HeaderNameInterceptor) {
newName = ((HeaderNameInterceptor) interceptor).checkHeaderName(newName);
if (newName == null) {
break;
}
}
}
if (newName == null) {
throw new ResponseHeaderRejectedException("HTTP header rejected: " + StringEscapeUtil.escapeJava(name));
}
return newName;
}
private String checkHeaderValue(String name, String value) throws ResponseHeaderRejectedException {
if (value == null) {
return null; // value==null返回
}
String newValue = value;
for (Object interceptor : interceptors) {
if (interceptor instanceof HeaderValueInterceptor) {
newValue = ((HeaderValueInterceptor) interceptor).checkHeaderValue(name, newValue);
if (newValue == null) {
break;
}
}
}
if (newValue == null) {
throw new ResponseHeaderRejectedException("HTTP header rejected: " + StringEscapeUtil.escapeJava(name)
+ "=" + StringEscapeUtil.escapeJava(value));
}
return newValue;
}
@Override
public void addCookie(Cookie cookie) {
Cookie newCookie = checkCookie(cookie);
CookieSupport newCookieSupport;
if (newCookie instanceof CookieSupport) {
newCookieSupport = (CookieSupport) newCookie;
} else {
newCookieSupport = new CookieSupport(newCookie); // 将cookie强制转化成cookie support
}
newCookieSupport.addCookie(this); // 通过set-cookie header来添加cookie,以便统一监管
}
private Cookie checkCookie(Cookie cookie) throws CookieRejectedException {
assertNotNull(cookie, "no cookie");
Cookie newCookie = cookie;
for (Object interceptor : interceptors) {
if (interceptor instanceof CookieInterceptor) {
newCookie = ((CookieInterceptor) interceptor).checkCookie(newCookie);
if (newCookie == null) {
break;
}
}
}
if (newCookie == null) {
throw new CookieRejectedException("Cookie rejected: " + StringEscapeUtil.escapeJava(cookie.getName()) + "="
+ StringEscapeUtil.escapeJava(cookie.getValue()));
}
return newCookie;
}
private String checkCookieHeaderValue(String name, String value, boolean setHeader) throws CookieRejectedException {
if (value == null) {
return null; // value==null返回
}
String newValue = value;
for (Object interceptor : interceptors) {
if (interceptor instanceof CookieHeaderValueInterceptor) {
newValue = ((CookieHeaderValueInterceptor) interceptor).checkCookieHeaderValue(name, newValue,
setHeader);
if (newValue == null) {
break;
}
}
}
if (newValue == null) {
throw new CookieRejectedException("Set-Cookie rejected: " + StringEscapeUtil.escapeJava(value));
}
return newValue;
}
@Override
public void sendError(int sc, String msg) throws IOException {
msg = checkStatusMessage(sc, msg);
if (msg == null) {
super.sendError(sc);
} else {
super.sendError(sc, msg);
}
}
@Override
@Deprecated
public void setStatus(int sc, String msg) {
msg = checkStatusMessage(sc, msg);
if (msg == null) {
super.setStatus(sc);
} else {
super.setStatus(sc, msg);
}
}
private String checkStatusMessage(int sc, String msg) {
if (msg != null) {
for (Object interceptor : interceptors) {
if (interceptor instanceof StatusMessageInterceptor) {
msg = ((StatusMessageInterceptor) interceptor).checkStatusMessage(sc, msg);
if (msg == null) {
break;
}
}
}
}
return msg;
}
@Override
public void sendRedirect(String location) throws IOException {
super.sendRedirect(checkRedirectLocation(location, true));
}
private String checkRedirectLocation(String location, boolean notNull) throws RedirectLocationRejectedException {
String newLocation = trimToNull(location);
if (newLocation == null && !notNull) {
return null;
}
newLocation = normalizeLocation(newLocation, getRequestContext().getRequest());
for (Object interceptor : interceptors) {
if (interceptor instanceof RedirectLocationInterceptor) {
newLocation = ((RedirectLocationInterceptor) interceptor).checkRedirectLocation(newLocation);
if (newLocation == null) {
break;
}
}
}
if (newLocation == null) {
throw new RedirectLocationRejectedException("Redirect location rejected: "
+ StringEscapeUtil.escapeJava(location));
}
return newLocation;
}
static String normalizeLocation(String location, HttpServletRequest request) {
location = assertNotNull(trimToNull(location), "no redirect location");
URI locationURI = URI.create(location);
if (!locationURI.isAbsolute()) {
URI baseuri = URI.create(request.getRequestURL().toString());
locationURI = baseuri.resolve(locationURI);
}
return locationURI.normalize().toString();
}
void prepareResponse() {
for (Object interceptor : interceptors) {
if (interceptor instanceof RequestContextLifecycleInterceptor) {
((RequestContextLifecycleInterceptor) interceptor).prepare();
}
}
}
void commitHeaders() {
for (int i = interceptors.length - 1; i >= 0; i--) {
Object interceptor = interceptors[i];
if (interceptor instanceof RequestContextLifecycleInterceptor) {
((RequestContextLifecycleInterceptor) interceptor).commitHeaders();
}
}
}
void commitResponse() {
for (int i = interceptors.length - 1; i >= 0; i--) {
Object interceptor = interceptors[i];
if (interceptor instanceof RequestContextLifecycleInterceptor) {
((RequestContextLifecycleInterceptor) interceptor).commit();
}
}
}
}