Closed rorytorneymf closed 1 week ago
Duplicate of #12481
Fixed in PR #12484
Will be available in release 12.0.16 due out end of November.
Thanks, I think that is a different issue to the one I have though, I temporarily reverted to 12.0.2 (released Oct 10, 2023), which is before the change (Dec 6, 2023) that introduced the problem was added, and still get the same error. It is possibly something I'm doing wrong in my app, I'll continue investigating.
I'll reopen.
If you can make your scattered source files into a ready to run project that would be great (just a new project on github should be sufficient). Use maven or gradle, and some simple instructions on how/what to run to trigger the issue. (also if the issue occurs every time, or occasionally)
From that, we can confirm if this is a duplicate of #12481
Apologies, closing this. This was happening because I was calling callback.succeeded()
which was causing the response to be committed prematurely.
@rorytorneymf where are you doing that? The code you presented in your question has no jetty core Handler example present.
It was in a separate class I didn't think was the issue, this is the particular method:
@Override
public AuthenticationState validateRequest(Request req, Response res, Callback callback) throws ServerAuthException {
final ServletContextRequest servletContextRequest = (ServletContextRequest)req;
Request request = resolveRequest(servletContextRequest.getServletApiRequest());
final ServletContextResponse servletContextResponse = (ServletContextResponse)res;
OIDCJettyHttpFacade facade = new OIDCJettyHttpFacade(request, servletContextResponse.getServletApiResponse().getResponse());
KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
if (deployment == null || !deployment.isConfigured()) {
callback.succeeded();
return AuthenticationState.SEND_FAILURE; // TODO how to send .UNAUTHENTICATED?
}
PreAuthActionsHandler handler = new PreAuthActionsHandler(createSessionManagement(request), deploymentContext, facade);
if (handler.handleRequest()) {
callback.succeeded();
return AuthenticationState.SEND_SUCCESS;
}
AdapterTokenStore tokenStore = getTokenStore(request, facade, deployment);
nodesRegistrationManagement.tryRegister(deployment);
tokenStore.checkCurrentToken();
JettyRequestAuthenticator authenticator = createRequestAuthenticator(request, facade, deployment, tokenStore);
AuthOutcome outcome = authenticator.authenticate();
if (outcome == AuthOutcome.AUTHENTICATED) {
if (facade.isEnded()) {
callback.succeeded();
return AuthenticationState.SEND_SUCCESS;
}
AuthenticationState authenticationState = register(request, authenticator.principal);
AuthenticatedActionsHandler authenticatedActionsHandler = new AuthenticatedActionsHandler(deployment, facade);
if (authenticatedActionsHandler.handledRequest()) {
callback.succeeded();
return AuthenticationState.SEND_SUCCESS;
}
final KeycloakAuthentication ka = (KeycloakAuthentication)authenticationState;
// Important: do no call callback.succeeded() here as it results in the response being committed too early.
// E.g. the login.html is returned with a Content-Length: 0 header even though there are >0 bytes in the content.
return authenticationState;
}
AuthChallenge challenge = authenticator.getChallenge();
if (challenge != null) {
challenge.challenge(facade);
}
callback.succeeded();
return AuthenticationState.CHALLENGE;
}
Jetty Version 12.0.14
Jetty Environment ee10
Java Version JRE 21
Question After upgrading my app from Jetty 11.0.24 to Jetty 12.0.14, I am getting the following error when Jetty tries to send a response. It looks like the Content-Length header is getting set to 0, when there are > 0 bytes being written:
o.e.j.ee10.servlet.HttpOutput: {"message":"onWriteComplete(true,java.io.IOException: written 4677 > 0 content-length) s=CLOSING,api=BLOCKED,sc=false,e=null->s=CLOSED,api=BLOCKING,sc=false,e=null c=null cb=Callback@32b98b0b{NON_BLOCKING, org.eclipse.jetty.ee10.servlet.ServletChannel$$Lambda/0x00007fac6f69ada0@3ebbfb77,org.eclipse.jetty.ee10.servlet.ServletChannel$$Lambda/0x00007fac6f69afb8@3bbbe09c} w=false","exception":"java.io.IOException: written 4677 > 0 content-length\n\tat org.eclipse.jetty.server.internal.HttpChannelState$ChannelResponse.write(HttpChannelState.java:1271)\n\tat org.eclipse.jetty.server.Response$Wrapper.write(Response.java:768)\n\tat org.eclipse.jetty.server.handler.gzip.GzipResponseAndCallback.write(GzipResponseAndCallback.java:133)\n\tat org.eclipse.jetty.server.Response$Wrapper.write(Response.java:768)\n\tat org.eclipse.jetty.server.handler.ContextResponse.write(ContextResponse.java:56)\n\tat org.eclipse.jetty.ee10.servlet.ServletContextResponse.write(ServletContextResponse.java:288)\n\tat org.eclipse.jetty.ee10.servlet.HttpOutput.channelWrite(HttpOutput.java:206)\n\tat org.eclipse.jetty.ee10.servlet.HttpOutput.complete(HttpOutput.java:437)\n\tat org.eclipse.jetty.ee10.servlet.ServletContextResponse.completeOutput(ServletContextResponse.java:212)\n\tat org.eclipse.jetty.ee10.servlet.ServletChannel.handle(ServletChannel.java:576)\n\tat org.eclipse.jetty.ee10.servlet.ServletHandler.handle(ServletHandler.java:464)\n\tat org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:575)\n\tat org.eclipse.jetty.ee10.servlet.SessionHandler.handle(SessionHandler.java:717)\n\tat org.eclipse.jetty.server.handler.ContextHandler.handle(ContextHandler.java:1060)\n\tat org.eclipse.jetty.server.Handler$Wrapper.handle(Handler.java:740)\n\tat io.dropwizard.metrics.jetty12.AbstractInstrumentedHandler.handle(AbstractInstrumentedHandler.java:299)\n\tat io.dropwizard.jetty.RoutingHandler.handle(RoutingHandler.java:41)\n\tat org.eclipse.jetty.server.handler.gzip.GzipHandler.handle(GzipHandler.java:597)\n\tat io.dropwizard.jetty.ZipExceptionHandlingGzipHandler.handle(ZipExceptionHandlingGzipHandler.java:21)\n\tat org.eclipse.jetty.server.Handler$Wrapper.handle(Handler.java:740)\n\tat org.eclipse.jetty.server.handler.GracefulHandler.handle(GracefulHandler.java:101)\n\tat org.eclipse.jetty.server.Server.handle(Server.java:182)\n\tat org.eclipse.jetty.server.internal.HttpChannelState$HandlerInvoker.run(HttpChannelState.java:662)\n\tat org.eclipse.jetty.server.internal.HttpConnection.onFillable(HttpConnection.java:414)\n\tat org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:322)\n\tat org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:99)\n\tat org.eclipse.jetty.io.ssl.SslConnection$SslEndPoint.onFillable(SslConnection.java:575)\n\tat org.eclipse.jetty.io.ssl.SslConnection.onFillable(SslConnection.java:390)\n\tat org.eclipse.jetty.io.ssl.SslConnection$2.succeeded(SslConnection.java:150)\n\tat org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:99)\n\tat org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)\n\tat org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:979)\n\tat org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1209)\n\tat org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1164)\n\tat java.base/java.lang.Thread.run(Thread.java:1583)\n"}
I disabled GZIP to determine if it was a GZIP-specific issue, but still got the same error.
Jetty 11 logs (working):
Jetty 12 logs (fail written 4677 > 0 content-length):
Some points:
SecureHeaderFilter
class (show at the top of the log snippets above). In Jetty 11 I see the expected headers, but in Jetty 12 I don't see any of the headers that have just been set in that class, and I also see theContent-Length: 0
header, though I am not sure who or what is setting theContent-Length
headero.e.jetty.http.HttpGenerator: CONTENT_LENGTH
andontent-Length: 1381
indicating that the header is being set correctly. I am not sure why the header is already present (and set to 0) in Jetty 12 by the time theSecureHeaderFilter
is invoked.Hare are the relevant classes in my app:
ApiGatewayApplication.java
CustomServletFilter.java
SecureHeaderFilter.java
LoginResource.java
LoginView.java
login.ftl