0

I am trying to reinvent the wheel and create my own websocket server. It is connecting fine on both Google Chrome and Firefox and will receive and echo text frames correctly up to 127 characters in length. However, beyond that Google gives me the following error:

WebSocket connection to 'ws://localhost:9999/' failed: The minimal number of bytes MUST be used to encode the length

Firefox will receive/interpret the first few characters sometimes then fail code:1006.

The server shows to be receiving the message in full and attempting to broadcast it in full with no run-time errors or . It's also going to the 16 bit length creator as shown by my System.out.println(). My java console from server reads:

websocket server started client connected message received 1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij12345678 broadcasting: 1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij1234567890abcdefghij12345678 2nd data

I'm currently writing a frame tester to test the frame I'm sending with broadcast but was hoping the community might be able to save me some of that leg work. The really odd thing is 126 and 127 payload lengths work and reflect using the 2 bytes for length.

public void broadcast(String mess) throws IOException {
        System.out.println("broadcasting: "+mess);
        byte[] rawData = mess.getBytes();

        int frameCount = 0;
        byte[] frame = new byte[10];

        frame[0] = (byte) 129;

        if (rawData.length <= 125) {
            frame[1] = (byte) rawData.length;
            frameCount = 2;
        } else if (rawData.length >= 126 && rawData.length <= 65535) {
            System.out.println("2nd data");
            frame[1] = (byte) 126;
            byte len = (byte) rawData.length;
            frame[2] = (byte) ((len >> 8) & (byte) 255);
            frame[3] = (byte) (len & (byte) 255);                
            frameCount = 4;
        } else {
            System.out.println("3rd data");
            frame[1] = (byte) 127;
            byte len = (byte) rawData.length;
            frame[2] = (byte) ((len >> 56) & (byte) 255);
            frame[3] = (byte) ((len >> 48) & (byte) 255);
            frame[4] = (byte) ((len >> 40) & (byte) 255);
            frame[5] = (byte) ((len >> 32) & (byte) 255);
            frame[6] = (byte) ((len >> 24) & (byte) 255);
            frame[7] = (byte) ((len >> 16) & (byte) 255);
            frame[8] = (byte) ((len >> 8) & (byte) 255);
            frame[9] = (byte) (len & (byte) 255);
            frameCount = 10;
        }

        int bLength = frameCount + rawData.length;

        byte[] reply = new byte[bLength];

        int bLim = 0;
        for (int i = 0; i < frameCount; i++) {
            reply[bLim] = frame[i];
            bLim++;
        }
        for (int i = 0; i < rawData.length; i++) {
            reply[bLim] = rawData[i];
            bLim++;
        }
        for (OutputStream writer : writers) {
            writer.write(reply);   
            writer.flush();
        }            

    }

Thank you community for any help.

1 Answers1

2

A few notes.

  1. You are handling length improperly
  2. You are not converting the String to bytes properly (must be done via UTF-8)

Here's a simple example.

The code below is licensed under : Eclipse Public License 1.0

package websocket;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;

public class RawGenerate
{
    /**
     * The overhead (maximum) for a framing header. Assuming a maximum sized payload with masking key.
     */
    public static final int OVERHEAD = 28;

    public ByteBuffer asClientInitiatedTextFrame(String msg)
    {
        ByteBuffer buf = ByteBuffer.allocate(msg.length() + OVERHEAD);
        putOpFin(buf,(byte)0x01,true);
        byte mask[] = new byte[] { 1, 2, 3, 4 }; // Needs to be random
        byte payload[] = msg.getBytes(Charset.forName("UTF-8")); // must be UTF-8 (per spec)
        putLengthAndMask(buf,payload.length,mask);
        for (int i = 0; i < payload.length; i++)
        {
            buf.put((byte)(payload[i] ^= mask[i % 4]));
        }
        buf.flip();
        return buf;
    }

    public static void putOpFin(ByteBuffer buf, byte opcode, boolean fin)
    {
        byte b = 0x00;
        if (fin)
        {
            b |= 0x80;
        }
        b |= opcode & 0x0F;
        buf.put(b);
    }

    public static void putLengthAndMask(ByteBuffer buf, int length, byte mask[])
    {
        if (mask != null)
        {
            assert (mask.length == 4);
            putLength(buf,length,(mask != null));
            buf.put(mask);
        }
        else
        {
            putLength(buf,length,false);
        }
    }

    public static void putLength(ByteBuffer buf, int length, boolean masked)
    {
        if (length < 0)
        {
            throw new IllegalArgumentException("Length cannot be negative");
        }
        byte b = (masked?(byte)0x80:0x00);

        if (length > 0xFF_FF)
        {
            buf.put((byte)(b | 0x7F));
            buf.put((byte)0x00);
            buf.put((byte)0x00);
            buf.put((byte)0x00);
            buf.put((byte)0x00);
            buf.put((byte)((length >> 24) & 0xFF));
            buf.put((byte)((length >> 16) & 0xFF));
            buf.put((byte)((length >> 8) & 0xFF));
            buf.put((byte)(length & 0xFF));
        }
        else if (length >= 0x7E)
        {
            buf.put((byte)(b | 0x7E));
            buf.put((byte)(length >> 8));
            buf.put((byte)(length & 0xFF));
        }
        else
        {
            buf.put((byte)(b | length));
        }
    }
}
Joakim Erdfelt
  • 46,896
  • 7
  • 86
  • 136
  • Thank you for your response I'm currently away so can't verify and not familiar enough with java shorthand commands or bit operators but assume your reputationmakes me confident this class will work in place of mine. Other thanhandling string how might my code be adapted for length properly? – JavaIntermediate Aug 22 '13 at 04:34
  • Not much shorthand here. Just need to learn binary/hex along with bit manipulations (like you would for most any binary protocol) `&` (AND), `|` (OR), `^` (XOR), `>>` (SHIFT RIGHT). If you look at your length code and mine, you'll see that for the 8 byte length version you attempt to fill in all 8 bytes with a single `byte len`. Which wont work. I only worry about 4 byte (32 bit) lengths, as that's already the limits of a java int. (No point supporting buffer sizes over 2,147,483,647 bytes in length) – Joakim Erdfelt Aug 22 '13 at 05:09
  • So I emplimented your code in place of mine however and it does indeed work however immediately errors out. Google error: WebSocket connection to 'ws://localhost:9999/' failed: A server must not mask any frames that it sends to the client. Since I can't fully read the class I apologize might you lead me by the nose to fix this? I will continue to try and work it out if I find the solution I will edit or new comment thanks again! I changed the asClientInitiatedTextFrame line putLengthAndMask(buf,payload.length,mask); to putLength(buf,payload.length,false); gave gibberish and closed again – JavaIntermediate Aug 22 '13 at 05:57
  • Got it to work thanks. I simply changed putLengthAndMask(buf,payload.length,mask); to putLengthAndMask(buf,payload.length,null); and set buf size so I could call buf.array(). Wasn't sure how else to write the bits ByteBuffer buf; if(msg.length()>65535){ buf=ByteBuffer.allocate(msg.length()+10); }else if(msg.length()>125){ buf=ByteBuffer.allocate(msg.length()+4); }else{ buf=ByteBuffer.allocate(msg.length()+2); } – JavaIntermediate Aug 22 '13 at 06:33
  • @JoakimErdfelt this is frame encoder right? Is this some library you have done? is it somewhere on GitHub? I'm using https://issues.apache.org/jira/browse/DIRMINA-907 and I'm afraid the encoder there uses incorrect frame encoding. – Martin Asenov Jul 16 '17 at 19:36
  • @MartinAsenov i wrote the websocket implementation for the [Eclipse Jetty project](https://www.eclipse.org/jetty/), the above code is a distillation of the specific topic from the OP. – Joakim Erdfelt Jul 17 '17 at 13:20
  • @JoakimErdfelt ok, but is it open-source? is the code accessible – Martin Asenov Jul 17 '17 at 14:05