package com.google.sitebricks;
import com.google.inject.Singleton;
import com.google.sitebricks.rendering.Strings;
import net.jcip.annotations.Immutable;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.util.Locale;
/**
* Enables browsers making a simulated PUT, PATCH and DELETE requests. Currently browsers support making
* GET and POST requests. This {@link javax.servlet.Filter} checks if a hidden field is set and
* renames HTTP method, retrieved via {@link javax.servlet.http.HttpServletRequest#getMethod()} to a method
* set in the hidden field
*
* @author Peter Knego
*/
@Immutable
@Singleton
class HiddenMethodFilter implements Filter {
private static final String FILTER_DONE_SUFFIX = "__done";
private static final String HIDDEN_FIELD_NAME = "hiddenFieldName";
/**
* Name of the hidden field. Shared across the entire App. Is guarded by
* init so no danger of visibility issues.
*/
static String hiddenFieldName = "__sitebricks__action";
private String filterDoneAttributeName;
public void init(FilterConfig filterConfig) throws ServletException {
String param = filterConfig.getInitParameter(HIDDEN_FIELD_NAME);
if (param != null) {
hiddenFieldName = param;
}
// Request attribute name to signal that filtering was already done in a request
filterDoneAttributeName = hiddenFieldName + FILTER_DONE_SUFFIX;
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
// check if filtering was already done on this request
if (httpRequest.getAttribute(filterDoneAttributeName) != null) {
// Filtering done, forward to another filter in chain
filterChain.doFilter(httpRequest, response);
} else {
httpRequest.setAttribute(filterDoneAttributeName, Boolean.TRUE);
try {
String methodName = httpRequest.getParameter(hiddenFieldName);
if ("POST".equalsIgnoreCase(httpRequest.getMethod()) && !Strings.empty(methodName)) {
String methodNameUppercase = methodName.toUpperCase(Locale.ENGLISH);
HttpServletRequest wrapper = new HttpMethodRequestWrapper(methodNameUppercase, httpRequest);
filterChain.doFilter(wrapper, response);
} else {
// Filtering done, forward to another filter in chain
filterChain.doFilter(httpRequest, response);
}
} finally {
// Remove the filterDone attribute for this request.
request.removeAttribute(filterDoneAttributeName);
}
}
}
public void destroy() {
}
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
private final String method;
public HttpMethodRequestWrapper(String method, HttpServletRequest request) {
super(request);
this.method = method;
}
@Override
public String getMethod() {
return this.method;
}
}
}