Skip to content

Discard the following HttpContent for preflight request#15941

Merged
normanmaurer merged 25 commits into
netty:4.2from
skyguard1:fix_cors_handler_last_http_content
Dec 3, 2025
Merged

Discard the following HttpContent for preflight request#15941
normanmaurer merged 25 commits into
netty:4.2from
skyguard1:fix_cors_handler_last_http_content

Conversation

@skyguard1

@skyguard1 skyguard1 commented Nov 29, 2025

Copy link
Copy Markdown
Contributor

Motivation:
HttpServerCodec always constructs an EmptyLastHttpContent and passes it to the Handler chain when processing OPTIONS requests, The EmptyLastHttpContent propagate through the handler chain.

However, the CORS handler might still propagate the EmptyLastHttpContent to downstream handlers via fireChannelRead(), causing subsequent handlers to receive only this empty content and lose access to the original request URL.
Because CorsHandler does not consume this message, it calls ctx.fireChannelRead(msg) for the EmptyLastHttpContent.

Downstream handlers then observe:

No HttpRequest

A LastHttpContent with empty content

This often breaks other handlers that rely on receiving the HttpRequest first or expect consistent HTTP message.

This PR fixes an issue in CorsHandler where, after handling a CORS preflight (OPTIONS) request, the handler still propagates the subsequent EmptyLastHttpContent sent by the HttpServerCodec to downstream handler.
Modification:

This PR adds option to CorsHandler to track when a preflight request has been fully handled.
Track handled preflight and consume the following inbound HttpContent,LastHttpContent instead of firing it downstream.
Scope: Only affects CORS preflight flow; keeps normal request forwarding unchanged.
Tests: Added cases to verify empty and non-empty LastHttpContent are discarded after preflight and that normal requests continue to forward HttpRequest and content.

It also improves compatibility with application frameworks and routing handlers that expect well-formed HTTP request/response flows.

Result:

Fixes #15148

CorsHandler track handled preflight and consume the following HttpContent, LastHttpContent until the next HttpRequest is forwarded instead of firing it downstream.

@normanmaurer

Copy link
Copy Markdown
Member

hmm... I don't think this is correct. We either need to always pass down a HttpRequest + LastHttpContent or non of these at all. I don't think there should be any configuration be involved at all.

@skyguard1

Copy link
Copy Markdown
Contributor Author

hmm... I don't think this is correct. We either need to always pass down a HttpRequest + LastHttpContent or non of these at all. I don't think there should be any configuration be involved at all.

However, for preflight requests, the HttpRequest is not being passed down, but the LastHttpContent is. Did I missing something?

@normanmaurer

Copy link
Copy Markdown
Member

@skyguard1 I am just saying we either always need to pass down the request + last content or never.. it always needs to be the combination or none at all.

@skyguard1

skyguard1 commented Dec 1, 2025

Copy link
Copy Markdown
Contributor Author

Preflight (OPTIONS + ACRM) is not a business request; it's merely a control request used internally by the browser to retrieve CORS metadata.

Therefore, the business logic should never:

receive the preflight HttpRequest

receive its HttpContent/LastHttpContent. So, for preflight requests, if you don't pass the HttpRequest, you should also not pass the LastHttpContent. This PR is essentially discarding the LastHttpContent for preflight requests, so I don't know what I'm missing.

@normanmaurer

Copy link
Copy Markdown
Member

@skyguard1 What I am saying is that I don't think this should be configurable at all...

@skyguard1

skyguard1 commented Dec 1, 2025

Copy link
Copy Markdown
Contributor Author

I got it. So you think the flag should be removed, but this fix is ​​valid. Right?

@skyguard1

Copy link
Copy Markdown
Contributor Author

My idea is to first minimize changes through the configuration, and then remove the flag after the release when users have configured and validated the behavior of discarding LastHttpContent. This satisfies the principle of minimal changes. Of course, we could also directly delete the flag to perform this fix.

@normanmaurer

Copy link
Copy Markdown
Member

imho we should just fix it and not have the configuration API. As this is just another API that we need to keep. We could have a system property to fallback to the old behavior but I am not sure this is needed at all.

@skyguard1

Copy link
Copy Markdown
Contributor Author

Agreed. Then I will delete this configuration.

@skyguard1 skyguard1 changed the title Allow user to discard the lastHttpContent for preflight request Discard the lastHttpContent for preflight request Dec 1, 2025
@skyguard1

Copy link
Copy Markdown
Contributor Author

Done

@normanmaurer normanmaurer added this to the 4.2.8.Final milestone Dec 1, 2025
@normanmaurer normanmaurer added needs-cherry-pick-4.1 This PR should be cherry-picked to 4.1 once merged. needs-cherry-pick-5.0 This PR should be cherry-picked to 5.0 once merged. labels Dec 1, 2025
…uest is forwarded

Signed-off-by: skyguard1 <qhdxssm@qq.com>
Comment thread codec-http/src/test/java/io/netty/handler/codec/http/cors/CorsHandlerTest.java Outdated
Signed-off-by: skyguard1 <qhdxssm@qq.com>
Signed-off-by: skyguard1 <qhdxssm@qq.com>
Comment thread codec-http/src/test/java/io/netty/handler/codec/http/cors/CorsHandlerTest.java Outdated
Comment thread codec-http/src/test/java/io/netty/handler/codec/http/cors/CorsHandlerTest.java Outdated
Comment thread codec-http/src/test/java/io/netty/handler/codec/http/cors/CorsHandlerTest.java Outdated
Comment thread codec-http/src/test/java/io/netty/handler/codec/http/cors/CorsHandlerTest.java Outdated
skyguard1 and others added 4 commits December 2, 2025 10:02
…HandlerTest.java

Co-authored-by: Norman Maurer <norman_maurer@apple.com>
…HandlerTest.java

Co-authored-by: Norman Maurer <norman_maurer@apple.com>
…HandlerTest.java

Co-authored-by: Norman Maurer <norman_maurer@apple.com>
…HandlerTest.java

Co-authored-by: Norman Maurer <norman_maurer@apple.com>
@skyguard1

Copy link
Copy Markdown
Contributor Author

AutoScalingEventExecutorChooserFactoryTest.testScaleUp. It seems that the failure of this test case is not related to this PR

@normanmaurer normanmaurer requested a review from yawkat December 2, 2025 07:40
@normanmaurer

Copy link
Copy Markdown
Member

@yawkat @vietj PTAL

Comment thread codec-http/src/main/java/io/netty/handler/codec/http/cors/CorsHandler.java Outdated

@normanmaurer normanmaurer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost there... Please also update the PR description etc to reflect what is actual done now

}

@Test
public void allowPrivateNetwork() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This seems to be not related to the PR

@skyguard1 skyguard1 Dec 2, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. But I found this allowPrivateNetwork is missing for unit test, maybe remove this?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just do it in a separate PR

assertFalse(ch.writeInbound(preflight));
ReferenceCountUtil.release(ch.readOutbound());

LastHttpContent first = LastHttpContent.EMPTY_LAST_CONTENT;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: You might also want to test with one non-empty one as it should be the same behavior

@skyguard1 skyguard1 changed the title Discard the lastHttpContent for preflight request Discard the following HttpContent for preflight request Dec 2, 2025
Signed-off-by: skyguard1 <qhdxssm@qq.com>
@normanmaurer normanmaurer merged commit 28e8017 into netty:4.2 Dec 3, 2025
19 checks passed
@normanmaurer

Copy link
Copy Markdown
Member

@skyguard1 thanks!

normanmaurer added a commit that referenced this pull request Dec 3, 2025
Motivation:
HttpServerCodec always constructs an `EmptyLastHttpContent` and passes
it to the Handler chain when processing OPTIONS requests, The
`EmptyLastHttpContent` propagate through the handler chain.

However, the CORS handler might still propagate the EmptyLastHttpContent
to downstream handlers via fireChannelRead(), causing subsequent
handlers to receive only this empty content and lose access to the
original request URL.
Because `CorsHandler` does not consume this message, it calls
`ctx.fireChannelRead(msg)` for the EmptyLastHttpContent.

Downstream handlers then observe:

No HttpRequest

A LastHttpContent with empty content

This often breaks other handlers that rely on receiving the
`HttpRequest` first or expect consistent HTTP message.


This PR fixes an issue in `CorsHandler` where, after handling a CORS
preflight (OPTIONS) request, the handler still propagates the subsequent
`EmptyLastHttpContent` sent by the `HttpServerCodec` to downstream
handler.
Modification:

- `CorsHandler` track handled preflight and consume the following
HttpContent, LastHttpContent until the next HttpRequest is forwarded
instead of firing it downstream.
- Add tests

Result:

Fixes #15148 
It also improves compatibility with application frameworks and routing
handlers that expect well-formed HTTP request/response flows.

---------

Signed-off-by: skyguard1 <qhdxssm@qq.com>
Co-authored-by: xingrufei <xingrufei@yl.com>
Co-authored-by: code-xing_wcar <code.xing@wirelesscar.com>
Co-authored-by: Norman Maurer <norman_maurer@apple.com>
normanmaurer added a commit that referenced this pull request Dec 3, 2025
Motivation:
HttpServerCodec always constructs an `EmptyLastHttpContent` and passes
it to the Handler chain when processing OPTIONS requests, The
`EmptyLastHttpContent` propagate through the handler chain.

However, the CORS handler might still propagate the EmptyLastHttpContent
to downstream handlers via fireChannelRead(), causing subsequent
handlers to receive only this empty content and lose access to the
original request URL.
Because `CorsHandler` does not consume this message, it calls
`ctx.fireChannelRead(msg)` for the EmptyLastHttpContent.

Downstream handlers then observe:

No HttpRequest

A LastHttpContent with empty content

This often breaks other handlers that rely on receiving the
`HttpRequest` first or expect consistent HTTP message.


This PR fixes an issue in `CorsHandler` where, after handling a CORS
preflight (OPTIONS) request, the handler still propagates the subsequent
`EmptyLastHttpContent` sent by the `HttpServerCodec` to downstream
handler.
Modification:

- `CorsHandler` track handled preflight and consume the following
HttpContent, LastHttpContent until the next HttpRequest is forwarded
instead of firing it downstream.
- Add tests

Result:

Fixes #15148 
It also improves compatibility with application frameworks and routing
handlers that expect well-formed HTTP request/response flows.

---------

Signed-off-by: skyguard1 <qhdxssm@qq.com>
Co-authored-by: xingrufei <xingrufei@yl.com>
Co-authored-by: code-xing_wcar <code.xing@wirelesscar.com>
Co-authored-by: Norman Maurer <norman_maurer@apple.com>
chrisvest pushed a commit that referenced this pull request Dec 3, 2025
)

Motivation:
HttpServerCodec always constructs an `EmptyLastHttpContent` and passes
it to the Handler chain when processing OPTIONS requests, The
`EmptyLastHttpContent` propagate through the handler chain.

However, the CORS handler might still propagate the EmptyLastHttpContent
to downstream handlers via fireChannelRead(), causing subsequent
handlers to receive only this empty content and lose access to the
original request URL.
Because `CorsHandler` does not consume this message, it calls
`ctx.fireChannelRead(msg)` for the EmptyLastHttpContent.

Downstream handlers then observe:

No HttpRequest

A LastHttpContent with empty content

This often breaks other handlers that rely on receiving the
`HttpRequest` first or expect consistent HTTP message.


This PR fixes an issue in `CorsHandler` where, after handling a CORS
preflight (OPTIONS) request, the handler still propagates the subsequent
`EmptyLastHttpContent` sent by the `HttpServerCodec` to downstream
handler.
Modification:

- `CorsHandler` track handled preflight and consume the following
HttpContent, LastHttpContent until the next HttpRequest is forwarded
instead of firing it downstream.
- Add tests

Result:

Fixes #15148 
It also improves compatibility with application frameworks and routing
handlers that expect well-formed HTTP request/response flows.

Signed-off-by: skyguard1 <qhdxssm@qq.com>
Co-authored-by: skyguard1 <qhdxssm@qq.com>
Co-authored-by: xingrufei <xingrufei@yl.com>
Co-authored-by: code-xing_wcar <code.xing@wirelesscar.com>
@skyguard1

Copy link
Copy Markdown
Contributor Author

It is my pleasure

normanmaurer added a commit that referenced this pull request Dec 4, 2025
)

Motivation:
HttpServerCodec always constructs an `EmptyLastHttpContent` and passes
it to the Handler chain when processing OPTIONS requests, The
`EmptyLastHttpContent` propagate through the handler chain.

However, the CORS handler might still propagate the EmptyLastHttpContent
to downstream handlers via fireChannelRead(), causing subsequent
handlers to receive only this empty content and lose access to the
original request URL.
Because `CorsHandler` does not consume this message, it calls
`ctx.fireChannelRead(msg)` for the EmptyLastHttpContent.

Downstream handlers then observe:

No HttpRequest

A LastHttpContent with empty content

This often breaks other handlers that rely on receiving the
`HttpRequest` first or expect consistent HTTP message.


This PR fixes an issue in `CorsHandler` where, after handling a CORS
preflight (OPTIONS) request, the handler still propagates the subsequent
`EmptyLastHttpContent` sent by the `HttpServerCodec` to downstream
handler.
Modification:

- `CorsHandler` track handled preflight and consume the following
HttpContent, LastHttpContent until the next HttpRequest is forwarded
instead of firing it downstream.
- Add tests

Result:

Fixes #15148 
It also improves compatibility with application frameworks and routing
handlers that expect well-formed HTTP request/response flows.

Signed-off-by: skyguard1 <qhdxssm@qq.com>
Co-authored-by: skyguard1 <qhdxssm@qq.com>
Co-authored-by: xingrufei <xingrufei@yl.com>
Co-authored-by: code-xing_wcar <code.xing@wirelesscar.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-cherry-pick-4.1 This PR should be cherry-picked to 4.1 once merged. needs-cherry-pick-5.0 This PR should be cherry-picked to 5.0 once merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HttpServerCodec always constructs an "EmptyLastHttpContent" and passes it to the Handler chain when processing OPTIONS requests

4 participants