-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
When shutting down receive direction we need to ensure that `closedRead` event is dispatched - as the receive direction is only considered closed if the available data is drained and `closedRead` is dispatched. The code did not account for a possibility that socket has no available data and as such is not dispatching read events so closing receive direction and then separately closing send direction would leave the socket in a state where both directions are closed but the socket is not disposed because `closedRead` is not dispatched - such socket objects will simply leak (even if the other side terminates the connection). This CL also updates documentation around `RawSocket.shutdown` and `RawSocket.readEventsEnabled` to make it clear that users are responsible for draining accumulated data if they want to shutdown receive direction. Fixes #27414 TEST=standalone/io/issue_27414 CoreLibraryReviewExempt: Documentation only changes in VM specific library Change-Id: I4b0ffb4cc67836c2849ec6e49b788a4f3b4c07d3 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/396340 Reviewed-by: Brian Quinlan <[email protected]> Commit-Queue: Slava Egorov <[email protected]>
- Loading branch information
Showing
4 changed files
with
111 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
// | ||
// This test verifies that shuting down receive and send directions separately | ||
// on a socket correctly shuts the socket down instead of leaking it. | ||
|
||
import 'dart:async'; | ||
import 'dart:convert'; | ||
import 'dart:io'; | ||
|
||
import 'package:expect/expect.dart'; | ||
import 'package:expect/async_helper.dart'; | ||
|
||
const messageContent = "hello, from the client!"; | ||
late RawServerSocket server; | ||
late StreamSubscription clientSubscription; | ||
|
||
void handleConnection(RawSocket serverSide) { | ||
var readClosedReceived = false; | ||
|
||
void serveData(RawSocketEvent event) async { | ||
switch (event) { | ||
case RawSocketEvent.read: | ||
final data = serverSide.read(); | ||
Expect.equals(messageContent, utf8.decode(data!)); | ||
|
||
// There might be a read event in flight, wait for microtasks to drain | ||
// and then shutdown read and write directions separately. This | ||
// should cause [readClosed] to be dispatched. | ||
Future.delayed(Duration(milliseconds: 0), () { | ||
serverSide.shutdown(SocketDirection.receive); | ||
serverSide.shutdown(SocketDirection.send); | ||
}); | ||
break; | ||
|
||
case RawSocketEvent.readClosed: | ||
Expect.isFalse(readClosedReceived); | ||
readClosedReceived = true; | ||
break; | ||
|
||
case RawSocketEvent.closed: | ||
Expect.isTrue(readClosedReceived); | ||
await clientSubscription.cancel(); | ||
await server.close(); | ||
asyncEnd(); | ||
break; | ||
} | ||
} | ||
|
||
serverSide.listen(serveData); | ||
} | ||
|
||
Future test() async { | ||
server = await RawServerSocket.bind(InternetAddress.loopbackIPv4, 0); | ||
server.listen(handleConnection); | ||
|
||
final client = await RawSocket.connect( | ||
InternetAddress.loopbackIPv4, | ||
server.port, | ||
); | ||
clientSubscription = client.listen((RawSocketEvent event) { | ||
switch (event) { | ||
case RawSocketEvent.write: | ||
client.write(utf8.encode(messageContent)); | ||
break; | ||
} | ||
}); | ||
} | ||
|
||
void main() { | ||
asyncStart(); | ||
test(); | ||
} |