Fix random number generators in WebSocketUtil
Motivation: Implementation of WebSocketUtil/randomNumber is incorrect and might violate the API returning values > maximum specified. Modifications: * WebSocketUtil/randomNumber is reimplemented, the idea of the solution described in the comment in the code * Implementation of WebSocketUtil/randomBytes changed to nextBytes method * PlatformDependet.threadLocalRandom is used instead of Math.random to improve efficiency * Added test cases to check random numbers generator * To ensure corretness, we now assert that min < max when generating random number Result: WebSocketUtil/randomNumber always produces correct result. Covers https://github.com/netty/netty/issues/8023
This commit is contained in:
parent
9ffdec302e
commit
fa4e28ba1c
@ -20,6 +20,7 @@ import io.netty.buffer.Unpooled;
|
|||||||
import io.netty.handler.codec.base64.Base64;
|
import io.netty.handler.codec.base64.Base64;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
import io.netty.util.concurrent.FastThreadLocal;
|
import io.netty.util.concurrent.FastThreadLocal;
|
||||||
|
import io.netty.util.internal.PlatformDependent;
|
||||||
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
@ -105,11 +106,7 @@ final class WebSocketUtil {
|
|||||||
*/
|
*/
|
||||||
static byte[] randomBytes(int size) {
|
static byte[] randomBytes(int size) {
|
||||||
byte[] bytes = new byte[size];
|
byte[] bytes = new byte[size];
|
||||||
|
PlatformDependent.threadLocalRandom().nextBytes(bytes);
|
||||||
for (int index = 0; index < size; index++) {
|
|
||||||
bytes[index] = (byte) randomNumber(0, 255);
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +118,29 @@ final class WebSocketUtil {
|
|||||||
* @return A pseudo-random number
|
* @return A pseudo-random number
|
||||||
*/
|
*/
|
||||||
static int randomNumber(int minimum, int maximum) {
|
static int randomNumber(int minimum, int maximum) {
|
||||||
return (int) (Math.random() * maximum + minimum);
|
assert minimum < maximum;
|
||||||
|
double fraction = PlatformDependent.threadLocalRandom().nextDouble();
|
||||||
|
|
||||||
|
// the idea here is that nextDouble gives us a random value
|
||||||
|
//
|
||||||
|
// 0 <= fraction <= 1
|
||||||
|
//
|
||||||
|
// the distance from min to max declared as
|
||||||
|
//
|
||||||
|
// dist = max - min
|
||||||
|
//
|
||||||
|
// satisfies the following
|
||||||
|
//
|
||||||
|
// min + dist = max
|
||||||
|
//
|
||||||
|
// taking into account
|
||||||
|
//
|
||||||
|
// 0 <= fraction * dist <= dist
|
||||||
|
//
|
||||||
|
// we've got
|
||||||
|
//
|
||||||
|
// min <= min + fraction * dist <= max
|
||||||
|
return (int) (minimum + fraction * (maximum - minimum));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -240,8 +240,8 @@ public abstract class WebSocketClientHandshakerTest {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
byte[] data = new byte[24];
|
// use randomBytes helper from utils to check that it functions properly
|
||||||
PlatformDependent.threadLocalRandom().nextBytes(data);
|
byte[] data = WebSocketUtil.randomBytes(24);
|
||||||
|
|
||||||
// Create a EmbeddedChannel which we will use to encode a BinaryWebsocketFrame to bytes and so use these
|
// Create a EmbeddedChannel which we will use to encode a BinaryWebsocketFrame to bytes and so use these
|
||||||
// to test the actual handshaker.
|
// to test the actual handshaker.
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at:
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
* License for the specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package io.netty.handler.codec.http.websocketx;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class WebSocketUtilTest {
|
||||||
|
|
||||||
|
// how many times do we want to run each random variable checker
|
||||||
|
private static final int NUM_ITERATIONS = 1000;
|
||||||
|
|
||||||
|
private static void assertRandomWithinBoundaries(int min, int max) {
|
||||||
|
int r = WebSocketUtil.randomNumber(min, max);
|
||||||
|
assertTrue(min <= r && r <= max);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRandomNumberGenerator() {
|
||||||
|
int iteration = 0;
|
||||||
|
while (++iteration < NUM_ITERATIONS) {
|
||||||
|
assertRandomWithinBoundaries(0, 1);
|
||||||
|
assertRandomWithinBoundaries(0, 1);
|
||||||
|
assertRandomWithinBoundaries(-1, 1);
|
||||||
|
assertRandomWithinBoundaries(-1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user