treefrog
treefrog

Reputation: 1057

how to modify a http header if already set?

I need to modify this header based on page contents.

eg: this is a contrieved example-since it was requested. I cannot go into the details of the specific header The servlet/page sets a header specialheader=xyz based on what backend system was accessed. If this specialheader is set the filter will modify it to xyz+abc. If the header is not set, the filter will set it to 123.

I could use a filter to create a wrapped HttpServletResponse and modify it on its way back. However, I am fuzzy on the details.

I know I have to prevent the output from being sent back to client before my filter does its job. In order to do this, do I need to pass it my own OutputStream and buffer the output?

Do I really need to do that ? can I avoid buffering the output which may put load on the server

The basic question is- if I need to modify the header AFTER the doFilter call, what is the minimum do I need to do ? Is there some other way of preventing the output from being committed such as overriuciding flush etc., ?

Upvotes: 4

Views: 10979

Answers (4)

Nicholas DiPiazza
Nicholas DiPiazza

Reputation: 10595

I found myself having to use the "reset" method to clear the response. Then re-applying the changes from scratch.

    Map<String, Collection<String>> originalHeaders = readHeaders(response);
    response.reset(); // Removes all the headers

    // at this point you can add your own headers here
    response.addHeader("Set-Cookie", "MyCookie=val");

    // finally, put back all the headers except the ones you want.
    // in this case I am replacing the Set-Cookie header so I skip that one
    originalHeaders.forEach((headerName, headerValues) -> {
        if (!headerName.equalsIgnoreCase("Set-Cookie")) {
            headerValues.forEach(headerValue -> response.addHeader(headerName, headerValue));
        }
    });

I now replaced the cookie with a new cookie.

Upvotes: 0

Anushka Senarathna
Anushka Senarathna

Reputation: 169

you can do something like below by overriding HttpServletRequestWrapper's getHeader method.

@Override
public Enumeration<String> getHeaders(String name) {
    List<String> values = Collections.list(super.getHeaders(name));
    if (headerMap.containsKey(name)) {
        if (name.equals("Header_attibutes_needs_to_be_replace")) {
            values.clear();
        }
        values.add(headerMap.get(name));
    }
    return Collections.enumeration(values);
}

And inside your filter, you set new value

 wrappedRequest = new HeaderMapRequestWrapper(request);
 wrappedRequest.addHeader("Header_attibutes_needs_to_be_replace", value);

Upvotes: 0

Doston
Doston

Reputation: 637

Whenever I search "How to modify the header in servlet response that is already set" this post comes first and even if the question description is different, I (maybe like many others) want to know how to modify the header in the servlet response that is already set. In this regard, I want to post my answer for those who are looking for (instead of writing a question/answer).

public class SameSiteCookieHeaderFilter implements Filter {


private static final String LOCALE_ID_COOKIE = "locale";


private static final String SET_COOKIE_HEADER = "Set-Cookie";

@Override
public void destroy() {
}

@Override
public void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
                     final FilterChain filterChain) throws IOException, ServletException {
    final HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
    final HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
    final Collection<String> setCookieHeaders = httpServletResponse.getHeaders(SET_COOKIE_HEADER);

    for (final String setCookieHeader : setCookieHeaders) {
        httpServletResponse.addHeader(SET_COOKIE_HEADER, setCookieHeader + "; Secure; SameSite=None");
    }

    if (setCookieHeaders.size() == 0) {
        final Cookie[] cookies = httpServletRequest.getCookies();

        for (final Cookie cookie : cookies) {
            if (cookie.getName().equals(LOCALE_ID_COOKIE)) {
                httpServletResponse.addHeader(SET_COOKIE_HEADER, buildSessionIdCookie(cookie.getValue()));
            }
        }
    }

    filterChain.doFilter(servletRequest, servletResponse);
}

@Override
public void init(FilterConfig filterConfig) throws ServletException {
}


private String buildSessionIdCookie(final String value) {
    return LOCALE_ID_COOKIE + "=" + value + "; " + "Path=/; " + "SameSite=None; " + "Secure; HttpOnly;";
}

web.xml

<filter>
    <filter-name>SameSiteCookieHeaderFilter</filter-name>
    <filter-class>de.chemmedia.kw.core.filter.SameSiteCookieHeaderFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>SameSiteCookieHeaderFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

so what it does, it detects Set-Cookie header and adds SameSite=None property, or takes Cookie and constructs Set-Cookie with SameSite=None in every response. In this way, there will be two Set-Cookie headers with the same value, and the second one will overwrite the previous one, **or modify the header which exists already ** in the response.

Set-Cookie: locale=EN; Path=/; HttpOnly;
Set-Cookie: locale=EN; Path=/; SameSite=None; Secure; HttpOnly;

FYI: tomcat 7.0.104, Servlet 3.1 and Spring 4.2.x

Upvotes: 1

thedayofcondor
thedayofcondor

Reputation: 3876

Just implement getHeader and getHeaderNames to ignore the header you want to discard and set the Wrapper as a filter.

import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.Collection;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

public class GenericResponseWrapper extends HttpServletResponseWrapper {



    public GenericResponseWrapper(final HttpServletResponse response) {
        super(response);    
    }

    @Override
    public String getHeader(String name) {
        // TODO Auto-generated method stub
        return super.getHeader(name);
    }
    @Override
    public Collection<String> getHeaderNames() {
        // TODO Auto-generated method stub
        return super.getHeaderNames();
    }
}


public class Wrapper implements Filter {

    @Override
    public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
        final HttpServletRequest httpRequest = (HttpServletRequest) request;
        final HttpServletResponse httpResponse = (HttpServletResponse) response;
            final GenericResponseWrapper wrapper = new GenericResponseWrapper(httpResponse);
            wrapper.getResponse().setCharacterEncoding("UTF-8");

            chain.doFilter(request, wrapper);
}
}

Upvotes: 1

Related Questions