50

I'm integrating with an existing servlet that pulls some properties out of the HTTP header. Basically, I'm implementing an interface that doesn't have access to the actual request, it just has access to a map of k->v for the HTTP headers.

I need to pass in a request parameter. The plan is to use a servlet filter to go from parameter to header value but of course the HttpServletRequest object doesn't have an addHeader() method.

Any ideas?

Mason
  • 8,767
  • 10
  • 33
  • 34

3 Answers3

57

Extend HttpServletRequestWrapper, override the header getters to return the parameters as well:

public class AddParamsToHeader extends HttpServletRequestWrapper {
    public AddParamsToHeader(HttpServletRequest request) {
        super(request);
    }

    public String getHeader(String name) {
        String header = super.getHeader(name);
        return (header != null) ? header : super.getParameter(name); // Note: you can't use getParameterValues() here.
    }

    public Enumeration getHeaderNames() {
        List<String> names = Collections.list(super.getHeaderNames());
        names.addAll(Collections.list(super.getParameterNames()));
        return Collections.enumeration(names);
    }
}

..and wrap the original request with it:

chain.doFilter(new AddParamsToHeader((HttpServletRequest) request), response);

That said, I personally find this a bad idea. Rather give it direct access to the parameters or pass the parameters to it.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • 3
    I think it's not a great idea either... the issue is I'm just one small class integrating into a large existing system. – Mason May 11 '10 at 15:52
  • 1
    Is there a way to do this by actually modifying the headers instead of overriding the getHeader method? It looks like something further down the line overrides my getHeader method. – Mason May 11 '10 at 18:55
  • 2
    The only way would be to act for a proxy and create an entirely new HTTP request and fire that on the URL of the servlet in question with help of `java.net.URLConnection` and then stream its response back. Not really efficient. – BalusC May 11 '10 at 20:31
  • 7
    Great answer. Just a little addition: Sometimes it is necessary to override [`getHeaders`](http://docs.oracle.com/javaee/6/api/javax/servlet/http/HttpServletRequestWrapper.html#getHeaders(java.lang.String)), too (e.g. when using JAX-RS via Jersey 1.X). – miku Feb 03 '13 at 19:08
  • @miku find an answer with getHeaders below – Wolfgang Fahl May 11 '14 at 08:32
  • @BalusC sir , Its wrong way to do this but, paste the question link here http://stackoverflow.com/questions/39437414/added-new-requestheader-fileds-is-not-adding bt I am trying to add cookie in the HttpRequest header but still not adding my code , I am Not sure what I did wrongly plz guide me sir – SakthiSureshAnand Sep 11 '16 at 17:41
31

as https://stackoverflow.com/users/89391/miku pointed out this would be a complete ServletFilter example that uses the code that also works for Jersey to add the remote_addr header.

package com.bitplan.smartCRM.web;

import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

/**
 * 
 * @author wf
 * 
 */
public class RemoteAddrFilter implements Filter {

    @Override
    public void destroy() {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HeaderMapRequestWrapper requestWrapper = new HeaderMapRequestWrapper(req);
        String remote_addr = request.getRemoteAddr();
        requestWrapper.addHeader("remote_addr", remote_addr);
        chain.doFilter(requestWrapper, response); // Goes to default servlet.
    }

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

    }

    // https://stackoverflow.com/questions/2811769/adding-an-http-header-to-the-request-in-a-servlet-filter
    // http://sandeepmore.com/blog/2010/06/12/modifying-http-headers-using-java/
    // http://bijubnair.blogspot.de/2008/12/adding-header-information-to-existing.html
    /**
     * allow adding additional header entries to a request
     * 
     * @author wf
     * 
     */
    public class HeaderMapRequestWrapper extends HttpServletRequestWrapper {
        /**
         * construct a wrapper for this request
         * 
         * @param request
         */
        public HeaderMapRequestWrapper(HttpServletRequest request) {
            super(request);
        }

        private Map<String, String> headerMap = new HashMap<String, String>();

        /**
         * add a header with given name and value
         * 
         * @param name
         * @param value
         */
        public void addHeader(String name, String value) {
            headerMap.put(name, value);
        }

        @Override
        public String getHeader(String name) {
            String headerValue = super.getHeader(name);
            if (headerMap.containsKey(name)) {
                headerValue = headerMap.get(name);
            }
            return headerValue;
        }

        /**
         * get the Header names
         */
        @Override
        public Enumeration<String> getHeaderNames() {
            List<String> names = Collections.list(super.getHeaderNames());
            for (String name : headerMap.keySet()) {
                names.add(name);
            }
            return Collections.enumeration(names);
        }

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

    }

}

web.xml snippet:

<!--  first filter adds remote addr header -->
<filter>
    <filter-name>remoteAddrfilter</filter-name>
    <filter-class>com.bitplan.smartCRM.web.RemoteAddrFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>remoteAddrfilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
Community
  • 1
  • 1
Wolfgang Fahl
  • 15,016
  • 11
  • 93
  • 186
  • 2
    Http headers are case insensitive but the implementation of your HeaderMapRequestWrapper does not take this into account. – Juru Jul 15 '21 at 15:42
20

You'll have to use an HttpServletRequestWrapper:

public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
    final HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(httpRequest) {
        @Override
        public String getHeader(String name) {
            final String value = request.getParameter(name);
            if (value != null) {
                return value;
            }
            return super.getHeader(name);
        }
    };
    chain.doFilter(wrapper, response);
}

Depending on what you want to do you may need to implement other methods of the wrapper like getHeaderNames for instance. Just be aware that this is trusting the client and allowing them to manipulate any HTTP header. You may want to sandbox it and only allow certain header values to be modified this way.

laz
  • 28,320
  • 5
  • 53
  • 50
  • 3
    Is there a way to do this by actually modifying the headers instead of overriding the getHeader method? It looks like something further down the line overrides my getHeader method. – Mason May 11 '10 at 18:55
  • You probably need to reorder your filters in web.xml then. Make this the very last one in the chain. – laz May 13 '10 at 16:51
  • what if I will call `getHeaders`, not `getHeader`? – Mikhail Ionkin Jul 27 '22 at 09:27