embedded-websocket

A lightweight rust websocket library for embedded systems (no_std)

This library facilitates the encoding and decoding of websocket messages and can be used for both clients and servers. The library is intended to be used in constrained memory environments like embedded microcontrollers which cannot reference the rust standard library. The library will work with arbitrarily small buffers regardless of websocket frame size as long as the websocket header can be read (2 - 14 bytes depending)

Running the examples

To run the demo web server

cargo run --example server To run the demo websocket client cargo run --example client or use this url in your browser http://127.0.0.1:1337/

Working example project

See https://github.com/ninjasource/led-display-websocket-demo for a complete end-to-end example of this library working with and stm32 m3 bluepill MCU and an embedded ethernet card.

Example websocket client usage:

The following example initiates a opening handshake, checks the handshake response, sends a short message, initiates a close handshake, checks the close handshake response and quits.

```rust use embedded_websocket as ws;

let mut buffer1: [u8; 1000] = [0; 1000]; let mut buffer2: [u8; 1000] = [0; 1000]; // use ws::random::EmptyRng::new() as an alternative below let mut websocket = ws::WebSocketClient::newclient(rand::threadrng());

// initiate a websocket opening handshake let websocketoptions = WebSocketOptions { path: "/chat", host: "localhost", origin: "http://localhost", subprotocols: None, additionalheaders: None, }; let (len, websocketkey) = websocket.clientconnect(&websocket_options, &mut buffer1)?;

// ... open TCP Stream and write len bytes from buffer1 to stream ... // ... read some receivedsize data from a TCP stream into buffer1 ... let receivedsize = 0;

// check the server response against the websocketkey we generated websocket.clientaccept(&websocketkey, &mut buffer1[..received_size])?;

// send a Text websocket frame let len = websocket.write( ws::WebSocketSendMessageType::Text, true, &"hello".as_bytes(), &mut buffer1, )?;

// ... write len bytes from buffer1 to TCP Stream ... // ... read some received_size data from a TCP stream into buffer1 ...

// the server (in this case) echos the text frame back. Read it. You can check the wsresult for frame type let wsresult = websocket.read(&buffer1[..receivedsize], &mut buffer2)?; let response = std::str::fromutf8(&buffer2[..wsresult.lento])?;

// initiate a close handshake let _len = websocket.close( ws::WebSocketCloseStatusCode::NormalClosure, None, &mut buffer1, )?;

// ... write len bytes from buffer1 to TCP Stream ... // ... read some received_size data from a TCP stream into buffer1 ...

// check the close handshake response from the server let wsresult = websocket.read(&buffer1[..receivedsize], &mut buffer2)?;

// ... close handshake is complete, close the TCP connection ```

Example websocket server usage:

The following example expects an http websocket upgrade message, sends a handshake response, reads a Text frame, echo's the frame back, reads a Close frame, sends a Close frame response.

```rust use embedded_websocket as ws;

let mut buffer1: [u8; 1000] = [0; 1000]; let mut buffer2: [u8; 1000] = [0; 1000]; let mut websocket = ws::WebSocketServer::new_server();

// ... read some data from a TCP stream into buffer1 ... let received_size = 0;

// repeat the read above and check below until Error::HttpHeaderIncomplete is no longer returned // (i.e. \r\n\r\n has been read from the stream as per the http spec) let header = ws::readhttpheader(&buffer1[..receivedsize])?; let wscontext = header.websocket_context.unwrap();

// check for Some(httpheader.websocketcontext) let len = websocket.serveraccept(&wscontext.secwebsocketkey, None, &mut buffer1)?;

// ... write len bytes from buffer1 to TCP Stream ... // ... read some received_size data from a TCP stream into buffer1 ...

// in this particular example we get a Text message below which we simply want to echo back let wsresult = websocket.read(&buffer1[..receivedsize], &mut buffer2)?;

// text messages are always utf8 encoded strings let response = std::str::fromutf8(&buffer2[..wsresult.len_to])?; // log this

// echo text message back let len = websocket.write( ws::WebSocketSendMessageType::Text, true, &buffer2[..wsresult.lento], &mut buffer1, )?;

// ... write len bytes from buffer1 to TCP Stream ... // ... read some received_size data from a TCP stream into buffer1 ...

// in this example say we get a CloseMustReply message below, we need to respond to complete the close handshake let wsresult = websocket.read(&buffer1[..receivedsize], &mut buffer2)?; let len = websocket.write( ws::WebSocketSendMessageType::CloseReply, true, &buffer2[..wsresult.lento], &mut buffer1, )?;

// ... write len bytes from buffer1 to TCP Stream... // ... close the TCP Stream ... ```

Upgrading from version "0.1.1" to "0.2.0"

Be sure to update your Cargo.toml file to reference the following: rand_core = "0.5" and rand if you use it: rand = "0.7" Run Cargo update

Upgrading from version "0.2.0" to "0.3.0"

Use ws::WebSocketClient::new_client(rand::thread_rng()) to create a client and ws::WebSocketServer::new_server() to create a server Use WebSocketClient<EmptyRng> instead of WebSocket<EmptyRng>

License

Licensed under either MIT or Apache-2.0 at your option