jetty / jetty.project

Eclipse Jetty® - Web Container & Clients - supports HTTP/2, HTTP/1.1, HTTP/1.0, websocket, servlets, and more
https://eclipse.dev/jetty
Other
3.86k stars 1.91k forks source link

Jetty 12: Looking for a way to abruptly finish a request, similar to OpenResty's ngx.exit(444) #12305

Closed paulhilliar closed 1 month ago

paulhilliar commented 1 month ago

Jetty Version 12.0.9

Jetty Environment ee10

Java Version 21

Question I'm porting a HTTP 1.1/2 proxy server application from OpenResty to Jetty 12.

There is a scenario where we have already sent the response headers and possibly some of the request body, before learning that the remainder of the request is not valid.

In that scenario in OpenResty, we call ngx.exit(444) and Nginx will abruptly close the connection. This is enough of a signal to curl or OkHttp that something has gone wrong. Other HTTP clients like Apache don't notice it but hey you can't have everything.

Please can you explain how can we tell Jetty server to abruptly close the connection, similar to Nginx?

For further explanation, the scenario here is a response caching feature. The headers and body (too big to hold all in memory) from a previous request have been cached (separately) in Redis. The headers have already been read from Redis and sent back to the client. If there is a failure to read a response chunk then we want to let the client know that they can't rely on the contents of the response.

Thanks in advance

lorban commented 1 month ago

Since Jetty 12.0.13 you can do:

Let us know if this does what you're looking for.

paulhilliar commented 1 month ago

Thanks for the suggestion. I upgraded to 12.0.13 and am calling HttpServletResponse.sendError(-1); but Jetty is still closing the connection 'politely'.

I can compare the same test scenario with nginx via curl and I get

curl -k -v --http1.1 https://localhost:64881/dep/large-response .....

GET /dep/large-response HTTP/1.1 Host: localhost:64881 User-Agent: curl/8.7.1 Accept: /

  • Request completely sent off
  • Empty reply from server
  • Closing connection curl: (52) Empty reply from server

On Jetty 12.0.13 curl -k -v --http1.1 https://localhost:64724/dep/large-response ..... < HTTP/1.1 200 OK < Date: Tue, 24 Sep 2024 14:04:46 GMT < Strict-Transport-Security: max-age=31536000 < Server: Oracle API Gateway < X-Frame-Options: sameorigin < X-Content-Type-Options: nosniff < X-XSS-Protection: 1; mode=block < Opc-Request-Id: /987135FED6D045E599BE2F5ED99513BF/3A8F2C6E8A844DD1AFDCD7568C6E2E66 < X-Cache-Status: HIT < Date: Tue, 24 Sep 2024 14:03:43 GMT < Transfer-Encoding: chunked <

The scenario here on both sides is that the HTTP status + headers are written, then "foobar" is written to the response body and then: On Nginx: nginx.exit(444) On Jetty 12.0.13: httpServletResponse.sendError(-1);

The 'closing connection' response is what I'm looking for, to indicate to the client that "foobar" is actually a response body that cannot be relied upon

joakime commented 1 month ago

This is surprising, as PR #12206 recently attempted to restore the harsh abort behavior from Jetty 11

sbordet commented 1 month ago

This is strange, we have tests that prove that sendError(-1) actually closes the EndPoint, see:

https://github.com/jetty/jetty.project/blob/jetty-12.0.13/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ErrorPageTest.java#L889

The linked test should be identical to your case: promise 10 bytes, write only 6, sendError(-1), and the client sees the response headers, the 6 bytes, and then the -1 from the socket.

paulhilliar commented 1 month ago

Thanks both - I'll dig into the test/PR and see if I can replicate the behaviour.

paulhilliar commented 1 month ago

I did a repo to replicate the sendError(-1) in a little embedded Jetty project. It still doesn't appear to be doing quite what I'm looking for. I'm adding nginx/OpenResty into the repo as a comparison to really dig into the differences. I'll report back with how that pans out.

paulhilliar commented 1 month ago

Ah.... I found out my error. Turns out nginx was just buffering the response and that was why it looked like it was doing something special.

I replicated the situation both in Jetty 12.0.13 and nginx here: https://github.com/paulhilliar/jetty-terminate-connection-replication

And when you explicitly flush the response, you get the same behaviour from both Nginx's ngx.exit(444) and Jetty's HttpServletResponse.sendError(-1).

Thank you very much for your help - I'll close this question.