Hi,

We're seeing a problem with Spring Boot 2.3.0.RELEASE when it comes to receiving HTTP 1.1 requests that has a request line that is using a fully-qualified url (similar to this issue)). I.e. if the request looks like this:

GET /http/issue HTTP/1.1
Host: localhost

it works fine, but if it's changed to this:

GET http://localhost:8080/http/issue HTTP/1.1
Host: localhost

we'll receive an error:

<!doctype html><html lang="en"><head><title>HTTP Status 400 – Bad Request</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 400 – Bad Request</h1></body></html>

In our case we're receiving calls from a third-party vendor which we have no control over. Here are step-by-step instructions on how to reproduce:

  1. Create a minimal project from spring initializer using spring-web (spring boot 2.3.0) or clone my demo repository. The entire application looks like this:

    ```java @RestController @RequestMapping(path = "/http/issue") @SpringBootApplication public class HttpIssueApplication {

    @GetMapping
    public String get() {
        return "Hello";
    }
    
    public static void main(String[] args) {
        SpringApplication.run(HttpIssueApplication.class, args);
    }
    

    } ```

  2. Run the application

  3. Try using curl and it'll work: curl http://localhost:8080/http/issue
  4. Use telnet to handcraft a request that fails:

    ```bash $ telnet localhost 8080 Trying ::1... Connected to localhost. Escape character is '^]'. GET http://localhost:8080/http/issue HTTP/1.1 Host: localhost

    HTTP/1.1 400 Content-Type: text/html;charset=utf-8 Content-Language: en Content-Length: 435 Date: Fri, 29 May 2020 11:07:55 GMT Connection: close

    <!doctype html>HTTP Status 400 – Bad Request

    HTTP Status 400 – Bad Request

    Connection closed by foreign host. ```

Note that the request line is using a fully -qualified URL: GET http://localhost:8080/http/issue HTTP/1.1 1. Now simply try using this request line instead and it'll work: GET /http/issue HTTP/1.1:

```bash
$ telnet localhost 8080
Trying ::1...
Connected to localhost.
Escape character is '^]'.
GET /http/issue HTTP/1.1
Host: localhost

HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 5
Date: Fri, 29 May 2020 11:11:55 GMT

Hello
```

I would expect Spring to also work with a fully-qualified URL in the request line hence this issue. I've also tried this with Spring Boot 2.2.7.RELEASE which also doesn't work BUT it seem to work for really old versions of Spring Boot such as 1.4.2.RELEASE.

Regards, /Johan

Comment From: wilkinsona

Thanks for the report. Tomcat is rejecting the request before it reaches any Spring code. If you switch to Jetty:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

It works as expected:

$ telnet localhost 8080
Trying ::1...
Connected to localhost.
Escape character is '^]'.
GET http://localhost:8080/http/issue HTTP/1.1
Host: localhost

HTTP/1.1 200 OK
Date: Fri, 29 May 2020 11:53:35 GMT
Content-Type: text/plain;charset=utf-8
Content-Length: 5

Hello

This looks like a Tomcat bug to me as the HTTP 1.1 RFC states that servers must handle requests with absolute URIs in the request line. You can report the problem to the Tomcat team at https://bz.apache.org/bugzilla/.

/cc @markt-asf

Comment From: markt-asf

Not a Tomcat bug. The request is invalid because the Host header doesn't match the host in the full URI (the port is missing). Tomcat correctly returns a 400 response here. See RFC 7230, section 5.4.

Comment From: wilkinsona

Sorry, Mark. I'd missed the mismatched Host. Thanks for pointing it out. For reference, the RCF states the following:

A client MUST send a Host header field in all HTTP/1.1 request messages. If the target URI includes an authority component, then a client MUST send a field-value for Host that is identical to that authority component, excluding any userinfo subcomponent and its "@" delimiter

i can confirm that it works as expected when the Host header matches the host of the URI:

$ telnet localhost 8080
Trying ::1...
Connected to localhost.
Escape character is '^]'.
GET http://localhost:8080/http/issue HTTP/1.1
Host: localhost:8080

HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 5
Date: Fri, 29 May 2020 13:34:09 GMT

Hello

So, FWIW, the bug's actually in Jetty as it should have rejected the request with a mismatched Host with a 400.

Comment From: wilkinsona

FWIW, Undertow has the same incorrect behaviour:

telnet localhost 8080
Trying ::1...
Connected to localhost.
Escape character is '^]'.
GET http://localhost:8080/http/issue HTTP/1.1
Host: localhost

HTTP/1.1 200 OK
Connection: keep-alive
Content-Type: text/plain;charset=UTF-8
Content-Length: 5
Date: Fri, 29 May 2020 13:39:35 GMT

Hello

Comment From: markt-asf

Note RFC 2616 was more lenient. The absolute URI took priority if they were different. (section 5.2). My understanding is that RFC 7230 tightened this up due to applications tending to look at the Host header. The resulting inconsistency could cause problems. This is configurable in Tomcat. allowHostHeaderMismatch on the Connector. Default changed from true to false between 8.5.x and 9.0.x

Comment From: FredrikFolkesson

How would you enable that setting in Spring Boot?

server:
  tomcat:
    allowHostHeaderMismatch: true

In the application.yaml does not seem to work

Comment From: wilkinsona

With a TomcatConnectorCustomizer bean.