spring-projects / spring-session

Spring Session
https://spring.io/projects/spring-session
Apache License 2.0
1.85k stars 1.1k forks source link

Session cookie not issued when output buffer size exceeds when including multibyte characters on Jetty server #3082

Open mjhashimoto opened 2 weeks ago

mjhashimoto commented 2 weeks ago

Describe the bug Session cookie is not issued when the output buffer size exceeds due to including multibyte characters on Spring Boot with Jetty server.

To Reproduce

  1. include Jetty server instead of tomcat

spring boot version: 3.3.1

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.3.1'
    id 'io.spring.dependency-management' version '1.1.5'
}

Gradle dependency:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation('org.springframework.boot:spring-boot-starter-web') {
        exclude group:'org.springframework.boot', module:'spring-boot-starter-tomcat'
    }
    implementation 'org.springframework.boot:spring-boot-starter-jetty'
    implementation 'org.springframework.session:spring-session-core'
}
  1. In the controller, render a document over 32768 bytes including multibyte characters:
@Controller
public class SessionCookieController {

    public static final String HELLO_IN_JA = "こんにちは"; // 15bytes

    @GetMapping("/cookie")
    public String cookie(HttpSession session, Model model){
        // < 32768 bytes doc
        String html = IntStream.range(0, 2184)
                .mapToObj(i -> HELLO_IN_JA)
                .collect(Collectors.joining());
        model.addAttribute("html", html);
        return "index";
    }

    @GetMapping("/nocookie")
    public String nocookie(HttpSession session, Model model){
        // > 32768 bytes doc
        String html = IntStream.range(0, 2185)
                .mapToObj(i -> HELLO_IN_JA)
                .collect(Collectors.joining());
        model.addAttribute("html", html);
        return "index";
    }
}

index.html <p th:utext="${html}"></p>

  1. Test with curl:

OK case:

> curl -s -D - localhost:8080/cookie -o /dev/null
HTTP/1.1 200 OK
Date: Tue, 09 Jul 2024 11:19:06 GMT
Content-Language: ja-JP
Content-Type: text/html;charset=utf-8
Set-Cookie: SESSION=ZGZjYWNiMDctZjc1My00YmU1LWEwYmItZGY1MTdkMDg5N2U2; Path=/; HttpOnly; SameSite=Lax
Transfer-Encoding: chunked

NG case:

> curl -s -D - localhost:8080/nocookie -o /dev/null
HTTP/1.1 200 OK
Date: Tue, 09 Jul 2024 11:19:08 GMT
Content-Language: ja-JP
Content-Type: text/html;charset=utf-8
Transfer-Encoding: chunked

Expected behavior When the document size is over 32768 bytes (the default output buffer size on Jetty), the response header should include the Set-Cookie header with the session cookie.

When using Tomcat, this issue does not occur. The difference lies in how buffer length is counted: Tomcat uses char array length, while Jetty uses bytes. Spring Session counts length in org.springframework.session.web.http.OnCommittedResponseWrapper#trackContentLength (ref:https://github.com/spring-projects/spring-session/issues/851)

Sample

(https://github.com/mjhashimoto/springsessionjettysample/tree/issued20240709)