注意HTTP请求头相等判断有坑

888 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第4天,点击查看活动详情

我朋友最近做一个转发接口的时候,想要过滤一些请求头,比如果客户端设置了accept-encoding请求头,如果下游做了压缩返回,需要处理请求结果就比较麻烦,所以这个请求头不直接传过去,使用Java遍历请求头遇到目标请求头就去掉,结果硬是没去掉。

复现

先使用spring boot接收到客户端http请求,之后通过httpclient转发给下游。遍历请求头的key之后设置到httpclient请求参数中,排除accept-encoding请求头,关键代码如下。

Enumeration<String> keys = request.getHeaderNames();
while(keys.hasMoreElements()) {
	String key = keys.nextElement();
	//将请求头accept-encoding去掉防止用户携带后服务器返回压缩的内容处理比较麻烦
	if ("accept-encoding".equals(key)) {
		continue;
	}
	String value = req.getHeader(key);
	httpRequest.setHeader(key, value);
}

像请求头这种应该使用一个常量代替比较好,这样字符串有变化时修改比较方便,所以就使用spring提供的请求头常量做修改如下

Enumeration<String> keys = request.getHeaderNames();
while(keys.hasMoreElements()) {
	String key = keys.nextElement();
	//将请求头accept-encoding去掉防止用户携带后服务器返回压缩的内容处理比较麻烦
	if (HttpHeaders.ACCEPT_ENCODING.equals(key)) {
		continue;
	}
	String value = req.getHeader(key);
	httpRequest.setHeader(key, value);
}

代码是变优雅了,但是改成常量后反而没有过滤掉accept-encoding请求头,断点一看原来HttpServlet传过来的请求头是全部小写的比如content-length,但是spring定义的常量是首字母大写的比如Content-Length。都是HTTP协议,而且前端传参时使用的是首字母大写的形式,到HttpServlet就变成小写了,是不是tomcat做了什么特殊处理?

分析

spring和tomcat使用的请求头不一样这种简单又坑爹的bug不可能会让我发现的,这两个牛逼框架会犯这种低级的错误?所以只有一种答案就是两种写法都是正确的,有了猜想就去找验证的证据,直接去翻HTTP规范,果然不出我所料http协议header字段名大小写不敏感。 www.w3.org/Protocols/r…

image.png

知道了这个前提就问题就好解决,既然大小不敏感那么在过滤请求时,判断相等的时候大小写都认为想等即可。
但又好奇一个问题,既然大小不敏感那么tomcat全部处理成小写字母,万一用户通过getHeader获取请求头使用大写呢?比如request.getHeader(HttpHeaders.ACCEPT_ENCODING)这种很常见的用法
猜想肯定是getHeader()方法做过处理,跟进代码就能找到答案了,跟进tomcat的HttpServletRequest 最后找到如下代码,方法equalsIgnoreCase()看名字就知道,当用户通过getHeader()时是做了忽略大小写判断的。

    public MessageBytes getValue(String name) {
        for (int i = 0; i < count; i++) {
            if (headers[i].getName().equalsIgnoreCase(name)) {
                return headers[i].getValue();
            }
        }
        return null;
    }

解决

http协议header字段名大小写不敏感的,所以每个框架的写法可能不一样,如果需要判断请求头相等需要都考虑,做忽略大小写判断。如下

Enumeration<String> keys = request.getHeaderNames();
while(keys.hasMoreElements()) {
	String key = keys.nextElement();
	//将请求头accept-encoding去掉防止用户携带后服务器返回压缩的内容处理比较麻烦
	if (HttpHeaders.ACCEPT_ENCODING.equalsIgnoreCase(key)) {
		continue;
	}
	String value = req.getHeader(key);
	httpRequest.setHeader(key, value);
}