RejectedSoftware Forums

Sign up

WebSocket Send big message in 256 byte tokens

Hi,

I'm implementing a server that sends a message through a websocket with SSL. This message is round 350 KB large the application is deployed on linux docker container.

My server uses a plain "socket.send( mymsg );"

The client is implemented using libwebsockets (C) on windows, and seems to receive this message in tokens 256 bytes each.

The client has no particular setup, and I'm trying to understand where does this tokenization come from, considering that the default receive buffer is 4096bytes and it keeps reading only 256 each.

Can you help me investigate this? Do you know if this behaviour can be caused by the server send implementation and how I could avoid it?

Thank you very much

Re: WebSocket Send big message in 256 byte tokens

I investigated a little more, with a WireShark capture during the data transfer.

The result is that when the server sends the data to the client, on the wire I see a sequence of the following:

Secure Sockets Layer

TLSv1.2 Record Layer: Application Data Protocol: http-over-tls
    Content Type: Application Data (23)
    Version: TLS 1.2 (0x0303)
    Length: 280
    Encrypted Application Data: b3f311d05739fe9f68e8b989c7e16e6989bb1073ccbd72e8...
TLSv1.2 Record Layer: Application Data Protocol: http-over-tls
    Content Type: Application Data (23)
    Version: TLS 1.2 (0x0303)
    Length: 280
    Encrypted Application Data: b3f311d05739fea05d1801defa114bac34bed67bcb979f45...
TLSv1.2 Record Layer: Application Data Protocol: http-over-tls
    Content Type: Application Data (23)
    Version: TLS 1.2 (0x0303)
    Length: 280
    Encrypted Application Data: b3f311d05739fea1caf72a89234756d20634fc35a642aae8...
TLSv1.2 Record Layer: Application Data Protocol: http-over-tls
    Content Type: Application Data (23)
    Version: TLS 1.2 (0x0303)
    Length: 280
    Encrypted Application Data: b3f311d05739fea26100c1886de1772f58ffc6d82857750f...


I don't know exactly how long is the SSL header, but 280 is pretty close to 256...

On the contrary, when my client sends back a big file, what I see is this:

Secure Sockets Layer

TLSv1.2 Record Layer: Application Data Protocol: http-over-tls
    Content Type: Application Data (23)
    Version: TLS 1.2 (0x0303)
    Length: 16408
    Encrypted Application Data: 50678c1059ce995ac6e2bad093310c76f827d38d2ddfff1b...


So the length is the default record buffer.

Given this I can suppose that the tokenizer is on the server side... do you know any implementation detail of your library that might lead to this behaviour?

Re: WebSocket Send big message in 256 byte tokens

On Fri, 02 Jun 2017 23:55:21 GMT, littlerussian wrote:

I investigated a little more, with a WireShark capture during the data transfer.

The result is that when the server sends the data to the client, on the wire I see a sequence of the following:

(...)

Given this I can suppose that the tokenizer is on the server side... do you know any implementation detail of your library that might lead to this behaviour?

I tracked this down to a StreamOutputRange that is actually intended to avoid sending the web socket frame header individually: https://github.com/rejectedsoftware/vibe.d/blob/321e4d85f5abbbda012c63d376deb32eefcb39d0/http/vibe/http/websockets.d#L821
Unfortunately it uses an internal 256 byte accumulation buffer, so this strategy breaks down for anything larger than that.

A first improvement was to change StreamOutputRange.put to write at most two TLS records in this case - one for the header and one for the full chunk of data: e1df68e

Unfortunately, due to limitations of the OpenSSL API, it seems like the only way to reach the optimum of one TLS record per WebSocket frame is to write the header and message body into a single buffer upfront, meaning one additional memory allocation per sent frame, which is quite sub optimal w.r.t. memory use and copy operations.

However, the current implementation already uses an Appender!(ubyte[]) to buffer incoming frame data. If that would always pre-allocate the maximum frame header size, the header could instead be injected there, and the StreamOutputRange could be dropped. I've opened a ticket for this: #1791

This will only work until the WebSocket module is rewritten to avoid dynamic memory allocations, which will happen mid-term. At that point, some other solution needs to be found.

Re: WebSocket Send big message in 256 byte tokens

Thank you! I look forward to try it soon!

On Mon, 26 Jun 2017 20:09:02 GMT, Sönke Ludwig wrote:

On Fri, 02 Jun 2017 23:55:21 GMT, littlerussian wrote:

I investigated a little more, with a WireShark capture during the data transfer.

The result is that when the server sends the data to the client, on the wire I see a sequence of the following:

(...)

Given this I can suppose that the tokenizer is on the server side... do you know any implementation detail of your library that might lead to this behaviour?

I tracked this down to a StreamOutputRange that is actually intended to avoid sending the web socket frame header individually: https://github.com/rejectedsoftware/vibe.d/blob/321e4d85f5abbbda012c63d376deb32eefcb39d0/http/vibe/http/websockets.d#L821
Unfortunately it uses an internal 256 byte accumulation buffer, so this strategy breaks down for anything larger than that.

A first improvement was to change StreamOutputRange.put to write at most two TLS records in this case - one for the header and one for the full chunk of data: e1df68e

Unfortunately, due to limitations of the OpenSSL API, it seems like the only way to reach the optimum of one TLS record per WebSocket frame is to write the header and message body into a single buffer upfront, meaning one additional memory allocation per sent frame, which is quite sub optimal w.r.t. memory use and copy operations.

However, the current implementation already uses an Appender!(ubyte[]) to buffer incoming frame data. If that would always pre-allocate the maximum frame header size, the header could instead be injected there, and the StreamOutputRange could be dropped. I've opened a ticket for this: #1791

This will only work until the WebSocket module is rewritten to avoid dynamic memory allocations, which will happen mid-term. At that point, some other solution needs to be found.