Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Close a WebSocket when it is idle #5713

Closed
wants to merge 3 commits into from

Conversation

patcher454
Copy link

Related: #5696

Motivation:

WebSocket is mostly a long-running request,
it is not easy to limit when it will end with a request timeout.
It would make more sense to terminate the request when no messages are sent or received for a certain period of time.

The existing idle timeout handler operates at the connection level so a new scheduler that can perform at the WebSocket level is necessary.

Modifications:

  • WebSocketServiceChannelHandler
    • Add ServerConfig parameter to the constructor
    • Add a feature closes channels using ScheduledFuture
  • Http1RequestDecoder
    • Modify new WebSocketServiceChannelHandler() Parameter in pipeline.replace()

Result:

@CLAassistant
Copy link

CLAassistant commented Jun 1, 2024

CLA assistant check
All committers have signed the CLA.

Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

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

The approach looks good overall 👍 Can you fix the failing test?

() -> writer.write("text"),
1, TimeUnit.SECONDS
);
Thread.sleep(Duration.ofSeconds(10).toMillis());
Copy link
Contributor

Choose a reason for hiding this comment

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

nit; we probably don't want a 10 second test since it will slow down our build.

@Test
void shouldThrowWhenWritingToIdleWebSocket() throws Exception {
final Throwable ex = assertThrows(RuntimeException.class, () -> {
final WebSocketClient client = WebSocketClient.of(SessionProtocol.H1C, server.httpEndpoint());
Copy link
Contributor

Choose a reason for hiding this comment

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

@ikhoon Did you imagine that only HTTP1 needs this kind of behavior when creating the issue? I'm curious if we don't need to consider H2C as well.

Copy link
Author

Choose a reason for hiding this comment

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

  • Thank you for your quick review.
  • In case of a failed test, it may be a little late because I'm in the military. But I'll fix it ASAP.
  • Below is a summary of why I didn't consider H2C, and what I understood about the WebSocket code.
  • Please let me know if there's anything wrong.

Reason

  1. In HTTP2 protocol, close the idle channel by the AbstractKeepAliveHandler class, the parent of the HTTP2ServerKeepAliveHandler generated by the HTTP2ServerConnectionHandler

  2. HTTP2 idleTimeout features already tested in KeepAliveHandlerTest

  3. I modified, the WebSocketServiceChannelHandler, is registered in Channel Pipeline only for HTTP1 protocol

  4. AbstractKeepAliveHandler was using idleTimeoutMillis in ServerConfig to close the idle channel, WebSocketServiceChannelHandler also uses idleTimeoutMillis in ServerConfig for the same result

About Web Socket Operation

HTTP/2(H2C)

  1. When connected by SessionProtocol.H2C, the Http2ServerConnectionHandler is registered in the channel pipeline as shown in the picture.

image

  1. When the Http2ServerConnectionHandler is created, if the idleTimeoutMillis is not 0, create the Http2ServerKeepAliveHandler as shown in the picture below.

image

  1. In AbstractKeepAliveHandler, parent of the Http2ServerKeepAliveHandler, close the idle channel like the log below.
16:20:27.830 [armeria-common-worker-nio-3-2] DEBUG c.l.a.i.c.AbstractKeepAliveHandler - [id: 0x0af5558b, L:/127.0.0.1:55809 - R:/127.0.0.1:55811] Closing an idle server connection

HTTP/1(H1C)

  1. When you connect to SessionProtocol.H1C, decode the request in the Http1RequestDecoder.

image

  1. If the request is WebSocketUpgradeRequest, replace the Http1RequestDecoder with the WebSocketServiceChannelHandler as shown in the picture below.

image

  1. However, there was no idleTimeout-related code in the existing WebSocketServiceChannelHandler

  2. So when connected with the HTTP1 protocol, the channel did not close if the idle state continued.

Copy link
Contributor

Choose a reason for hiding this comment

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

In HTTP2 protocol, close the idle channel by the AbstractKeepAliveHandler class, the parent of the HTTP2ServerKeepAliveHandler generated by the HTTP2ServerConnectionHandler

Sure, but I thought Http2ServerKeepAliveHandler also operated on the connection level. I was asking the original issue creator because I was looking at the following line which seemed to imply an idle timeout for requests which h2c doesn't have at the moment.

The existing idle timeout handler operates at the connection level so a new scheduler that can perform at the WebSocket level is necessary.

@jrhee17 jrhee17 added this to the 1.30.0 milestone Jun 3, 2024
@trustin
Copy link
Member

trustin commented Jun 5, 2024

What do you think about solving this issue in a more generic way? We could add idle timeout settings to any HttpService, like:

  • "Abort the stream if a peer doesn't send anything for N milliseconds."
  • "Abort the stream if the service doesn't send anything for N milliseconds."
  • "Abort the stream if neither the peer nor the service doesn't send anything for N milliseconds."

@patcher454
Copy link
Author

I think I was just too focused on shutting down the connection in idle state.
I will solve it at the higher level and request it again.

@jrhee17 @trustin
Thank you for your review.

@patcher454 patcher454 closed this Jun 6, 2024
@patcher454 patcher454 deleted the close_idle_web_socket branch June 6, 2024 03:16
@ikhoon ikhoon removed this from the 1.30.0 milestone Aug 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Close a WebSocket when it is idle
5 participants