No. It is a simple bug. The payload is defined to be a byte stream. It should
never be manipulated. The implementation MUST send the bytes exactly as the api
caller provided.
But I think there is something else at play - the implementation should not be
reading the bytes at all - which is the only time byte order would matter -
something larger than a byte.
> On Mar 13, 2025, at 9:27 PM, Mark Sheppard <mshep...@openjdk.org> wrote:
>
> On Thu, 13 Mar 2025 13:42:40 GMT, Volkan Yazici <vyaz...@openjdk.org> wrote:
>
>>> Fixes endian handling `jdk.internal.net.http.websocket.Frame.Masker`.
>>>
>>> ### Implementation notes
>>>
>>> I deleted the `Frame` clone in tests, and rewired the test code depending
>>> on it to the actual `Frame`. To enable this, I relaxed the visibility of
>>> the actual `Frame`. I guess the `Frame` clone was introduced to have strict
>>> visibility in the actual `Frame`. Though this is not needed since the
>>> actual `Frame` is in an internal package. Plus, the fact that bug is in the
>>> `Frame` class hints in the direction that there should be one `Frame`.
>>
>> Volkan Yazici has updated the pull request incrementally with one additional
>> commit since the last revision:
>>
>> Fix copyright years
>
> Expanding the paint job on the bikeshed for Masker
>
> I’d proffer some alternative names to the current on offer and a possible
> restructuring of Masker providing a set of static utility
>
> I haven’t looked at any use cases for Masker and the use relationship between
> Frame and Masker.
> As such, working under the assumption that by not having a factory method or
> an explicit constructor, its
> instantiation is purely meant to be in the context of the mask operation
> (formerly transferMasking).
>
> Masker purpose is to hold state
>
> private final int[] maskBytes = new int[4];
> private int offset;
> private long maskLongBe; // maybe rename beLongMask
> private long maskLongLe; // maybe rename leLongMask
>
> Which in turn could be encapsulated in a Mask class. A Mask is instantiated
> in the Masker.createMask(int mask) method (formerly reset).
> And this Mask instance is passed as a parameter to various masking operations
> in the mask call flow. Masker’s method then become become static methods
>
> Reset is not really a reset but more an init (substituting for a constructor)
> and its returning Masker is purely to facilitate, the compound invocation
> chain in mask.
> Masker objects have a transient existence, are there any GC implication with
> the current instantiation use case?
> It is possible to collapse reset into a Masker constructor ?
>
> Looking at the proposed methods, then
>
> Alternative names to the static “mask” method are copyWithMask to
> transferWithMask
>
> mask —> applyMask // the non static mask
> initGallopingMasking —> initLongMask
> doGallopingMasking —> applyLongMask
> doPlainMAsking —> endApplyMask
>
> Using a Mask abstraction to contain the state of the mask processing, then
> reset becomes a factory method createMask() returning a Mask object.
>
> And this instance of Mask is passed in the processing call chain
>
> class Mask {
> public Mask ( int[] maskBytes, int offset,long
> beLongMask,long leLongMask) {
> . . .
> }
>
> }
>
> static void mask(ByteBuffer src, ByteBuffer dst, int mask) {
> if (src.remaining() > dst.remaining()) {
> throw new IllegalArgumentException(dump(src, dst));
> }
> Mask theMask = createMask(mask);
> applyMask(src, dot, theMask);
> }
>
> /*
> * Clears this instance's state and sets the mask.
> *
> * The behaviour is as if the mask was set on a newly created instance.
> */
> public Mask createMask(int mask) {
> ByteBuffer acc =
> ByteBuffer.allocate(8).putInt(mask).putInt(mask).flip();
> final int[] maskBytes = new int[4];
> for (int i = 0; i < maskBytes.length; i++) {
> maskBytes[i] = acc.get(i);
> }
> offset = 0;
> beLongMask = acc.getLong(0);
> leLongMask = Long.reverseBytes(maskLongBe);
> return new Mask(maskBytes, offset, beLongMask, leLongMask);
> }
>
> /*
> * Reads as many remaining bytes as possible from the given input
> * buffer, masks them with the previously set mask and writes the
> * resulting bytes to the given output buffer.
> *
> * The source and the destination buffers may be the same instance. If
> * the mask hasn't been previously set it is assumed to be 0.
> */
> public void applyMask(ByteBuffer src, ByteBuffer dst, theMask) {
> if (src.order() == dst.order()) {
> initLongMask(src, dst, theMask);
> applyLongMask(src, dst, theMask);
> }
> endApplyMask(src, dst, theMask);
> }
>
> /**
> * Positions the {@link #offset} at 0, which is needed for galloping,
> by masking necessary amount of bytes.
> */
> private static void initLongMask(ByteBuffer src, ByteBuffer dst, Mask
> aMask) {
> . . .
> }
>
>
> private static void applyLongMask(ByteBuffer src, ByteBuffer dst, Mask
> aMask) {
> . . .
> }
>
> private void endApplyMask(ByteBuffer src, ByteBuffer dst, Mask aMask) {
> . . .
> }
>
> -------------
>
> PR Comment: https://git.openjdk.org/jdk/pull/24033#issuecomment-2723180720