调错-Nginx代理SpringBoot应用时报400 bad request

测试场景

使用Nginx代理SpringBoot应用,版本如下:
SpringBoot 2.1.3.RELEASE(内嵌tomcat9)
nginx-1.14.0

配置如下:

1
2
3
4
5
6
7
8
9
10
11
upstream code_server_pool{
    server 127.0.0.1:63049 weight=10;
   }
 server{
    listen       80;
    server_name  www.pbteach.com;
   #验证码
    location ^~ /checkcode/ {  
        proxy_pass http://code_server_pool/checkcode/;  
    }
    ...

使用postman测试报错
在这里插入图片描述
查看日志:
nginx日志:127.0.0.1 - - [08/May/2020:22:57:55 +0800] “POST /checkcode/getToken HTTP/1.1” 400 812 “-” “PostmanRuntime/7.1.1”

SpringBoot日志如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
java.lang.IllegalArgumentException: The character [_] is never valid in a domain name.
    at org.apache.tomcat.util.http.parser.HttpParser$DomainParseState.next(HttpParser.java:926)
    at org.apache.tomcat.util.http.parser.HttpParser.readHostDomainName(HttpParser.java:822)
    at org.apache.tomcat.util.http.parser.Host.parse(Host.java:71)
    at org.apache.tomcat.util.http.parser.Host.parse(Host.java:45)
    at org.apache.coyote.AbstractProcessor.parseHost(AbstractProcessor.java:288)
    at org.apache.coyote.http11.Http11Processor.prepareRequest(Http11Processor.java:809)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:384)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)

分析与解决

根据SpringBoot的报错得知,下划线字符“_”在域名中无效。
查看org.apache.tomcat.util.http.parser.HttpParser源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 public DomainParseState next(int c) {
            if (HttpParser.isAlpha(c)) {
                return ALPHA;
            } else if (HttpParser.isNumeric(c)) {
                return NUMERIC;
            } else if (c == '.') {
                if (allowsPeriod) {
                    return PERIOD;
                } else {
                    throw new IllegalArgumentException(sm.getString(errorMsg,
                            Character.toString((char) c)));
                }
            } else if (c == ':') {
                if (allowsEnd) {
                    return COLON;
                } else {
                    throw new IllegalArgumentException(sm.getString(errorMsg,
                            Character.toString((char) c)));
                }
            } else if (c == -1) {
                if (allowsEnd) {
                    return END;
                } else {
                    throw new IllegalArgumentException(
                            sm.getString("http.invalidSegmentEndState", this.name()));
                }
            } else if (c == '-') {
                if (allowsHyphen) {
                    return HYPHEN;
                } else {
                    throw new IllegalArgumentException(sm.getString(errorMsg,
                            Character.toString((char) c)));
                }
            } else {
                throw new IllegalArgumentException(sm.getString(
                        "http.illegalCharacterDomain", Character.toString((char) c)));
            }

早期使用SpringBoot低版本时没有此问题,升级SpringBoot后,内嵌的是Tomcat9版本,对域名的合法性进行校验中,包含下划线“_”则抛出异常。

修改nginx配置,将code_server_pool中的下划线去掉,修改如下:

1
2
3
4
5
6
7
8
9
10
11
upstream codeserverpool{
    server 127.0.0.1:63049 weight=10;
   }
 server{
    listen       80;
    server_name  www.pbteach.com;
   #验证码
    location ^~ /checkcode/ {  
        proxy_pass http://codeserverpool/checkcode/;  
    }
    ...

重启nginx解决此问题。