SocksAuthRequest constructor occasionally throws IllegalStateException (#9558)

Motivation:

There appears to be a thread-safety issue in the way that `SocksAuthRequest` is using its `CharsetEncoder` instance.  `CharsetUtil#encoder` returns a cached thread-local encoder instance, so it is not correct to store this instance in a static member variable and reuse it across multiple threads.  The result is an occasional `IllegalStateException` as in the following example:

```
java.lang.IllegalStateException: Current state = RESET, new state = FLUSHED
	at java.base/java.nio.charset.CharsetEncoder.throwIllegalStateException(CharsetEncoder.java:989)
	at java.base/java.nio.charset.CharsetEncoder.flush(CharsetEncoder.java:672)
	at java.base/java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:801)
	at java.base/java.nio.charset.CharsetEncoder.canEncode(CharsetEncoder.java:907)
	at java.base/java.nio.charset.CharsetEncoder.canEncode(CharsetEncoder.java:982)
	at io.netty.handler.codec.socks.SocksAuthRequest.<init>(SocksAuthRequest.java:43)
```

Modification:

Instead of retrieving the thread-local encoder instance once and storing it as a static member instance, the encoder should be retrieved each time the constructor is invoked.  This change prevents any potential concurrency issues where multiple threads may end up using the same encoder instance.

Result:

Fixes #9556.
This commit is contained in:
James Baldassari 2019-09-09 15:08:21 -04:00 committed by Norman Maurer
parent 38e5983463
commit 4a60152dd8

View File

@ -29,7 +29,6 @@ import java.nio.charset.CharsetEncoder;
* @see SocksAuthRequestDecoder
*/
public final class SocksAuthRequest extends SocksRequest {
private static final CharsetEncoder asciiEncoder = CharsetUtil.encoder(CharsetUtil.US_ASCII);
private static final SocksSubnegotiationVersion SUBNEGOTIATION_VERSION = SocksSubnegotiationVersion.AUTH_PASSWORD;
private final String username;
private final String password;
@ -38,6 +37,8 @@ public final class SocksAuthRequest extends SocksRequest {
super(SocksRequestType.AUTH);
requireNonNull(username, "username");
requireNonNull(password, "password");
final CharsetEncoder asciiEncoder = CharsetUtil.encoder(CharsetUtil.US_ASCII);
if (!asciiEncoder.canEncode(username) || !asciiEncoder.canEncode(password)) {
throw new IllegalArgumentException(
"username: " + username + " or password: **** values should be in pure ascii");