Libwebsockets: pure C library for http, websockets, MQTT
github.com
github.com
The documentation itself is actually good and extensive, however it feels like the authors expect its users to deeply understand the library itself. It is also harder to find some "Getting Started" docs as most provided examples felt way too bloated coming from a nodejs/ws background. Compared to the other library[1] I was considering, it took much longer to get even a simple "echo" server running.
However having used lws for some time now, I am really happy with it! The API is very clean, mostly intuitive and provides everything you need, without feeling bloated or becoming too verbose. Sometimes documentation still feels a bit harder to find, but it can be figured out eventually. One great feature for me was being able to accept both WebSocket as well as raw TCP connections on the same port, this is extremely easy and just required settings the flag LWS_SERVER_OPTION_FALLBACK_TO_RAW.
I encountered other hiccups. They are fully documented and completely valid, but were really confusing to me as a first-time user:
* Sending data requires a specific memory layout[2] – namely having to allocate memory for the websocket header youself before the actual message you want to send. This gave me confusing segfaults in the beginning.
* Sending data / responding to a client message will probably (but not always) fail when just naively using "lws_write()". To correctly send data you need to manually queue your message data, request the "ON_WRITABLE" callback[3] and only then actually send.
[1]: https://github.com/Theldus/wsServer
[2]: https://libwebsockets.org/lws-api-doc-v3.0-stable/html/group... (see the "IMPORTANT NOTICE")
[3]: https://libwebsockets.org/lws-api-doc-main/html/group__callb...
Response to below user since rate limited:
Few Java libraries support everything required. At the socket level if you want to get cmsg struct information you’re not going to get that from most Java libraries and you need that configurable since sometimes you use SO_TIMESTAMP et al. and other times you don’t and if you use SW for that you can lose a few hundred micros there.
OTOH libwebsockets very easy to modify. Downside is it manages the loop. So you need to do some engineering. But if someone has already learned the lessons there it would be helpful to me. If they haven’t, I’ll just do it I suppose.
I've yet to actually build anything with redbean but the more functionality it has, the better.
The project I'd like to explore with it would be to create a lua-based CLI toolkit a la the goodness that charmbracelet is building https://github.com/charmbracelet. Too many distractions atm and I don't have need of that CLI functionality so the impetus is lacking.
It includes http/https/ws/wss client and server code in just a few files (although last I checked, some files are needlessly duplicated in 2 different repos).
Interesting to see that this has a MQTT client integrated.
good performance, good reliability.
- JSON parsing
- DNS, DHCP and NTP clients
- SOCKS5 proxy support
- DBUS client and server
- Threadpools
- SSH
- JPEG and PNG decoding
- HTML and CSS parsing
- DOM layout and rendering to a frame buffer
- OTA update client
- GPIO, i2c, PWM, SPI
- SSH server
- ACME client
- ...The majority of the things I list are optional features of the core library (rather than examples) enabled by cmake flags, some of which look to be enabled by default:
option(LWS_WITH_JPEG "Enable stateful JPEG stream decoder" ON)
I would be slightly surprised if I saw a dependency on "libwebsockets" and found that it brought in a JPEG decoder, for example.All these extra features may imply that it's grown past its original goals, and that the other bits and pieces could be split into smaller parts, etc.
Every library starts out small, adds its own vendored utilities, adds its own dependencies, and eventually becomes boost.
To be fair to this project, theyve worked quite hard to make the library consumable by others… but yes, its hard to look at the code like https://github.com/warmcat/libwebsockets/blob/50ba61082dc40b...
…and not go… really? As part of the core of libwebsocket?
When you read the justification it’s mostly “well we have a good framework now, so why not use it for other things too?”
C++ life.
/shrug
I find it almost inexcusable at this point.
For other MCUs where you do get C++ support, you often only get C++98.
Replied to your other comment about this as well.
Also, if you're not sure what “with standard library” in the context of C programming means, start here: https://en.wikipedia.org/wiki/C_standard_library
That assumption was not clear to me though.
Many embedded code bases do not have a 100% standard-conforming libc and e.g. miss I/O functions like fopen and friends (because there is no file system).
And at that level, you will sometimes only get compilers for C (or older C++ dialects).
There is no need for it. Rust already has libraries for http, websockets, and mqtt.
> This one has 13 years proven battlefield use. And by lord this myth of "Old therefore must be battle-tested" must die.
Case in appoint, I had to interact with a piece of software that used this library and it is as battle-tested as expecting HTTP headers in very specific casing!
Here is a comment from some code I wrote circa 2020 to deal with this issue. So it is not hypothetical.
// libwebsocket/1.X.X is naive about http headers
// and expects them in a specific case but node.js as
// permitted by http spec doesn't care about case.
// this patches node.js http to send headers as expected
// by libwebsocket/VAA
const { setHeader } = http.OutgoingMessage.prototype;
http.OutgoingMessage.prototype.setHeader = function (name, value) {
name = name
.replace(/\b(\w)/g, (match, submatch) => (submatch ? submatch.toUpperCase() : ''))
.replace(/(websocket|Websocket)/g, () => 'WebSocket');
value = value.replace(/(websocket|Websocket)/g, () => 'WebSocket');
setHeader.call(this, name, value);
};>> Rust may have safe memory coding built-in, it doesnt guarantee the coding to be 100% bug free or fully secure from zero day attacks.
> Case in appoint, I had to interact with a piece of software that used this library and it is as battle-tested as expecting HTTP headers in very specific casing!
How would rewriting in $SOMETHING_OTHER_THAN_C help with this bug? This is a bug that would have happened in any language.
After all, Gow8876 was very specific about what he was referring to ("memory safety" bugs), because you were very specific about what you were referring to ("unsafe language").
I pointed out this defect because handling HTTP headers "correctly" is such a basic requirement for a websocket library, yet it failed at it, so the claim for "13 years proven battlefield use" is baseless and only based on the fallacy of "it is old therefore battle-tested".
> Rust may have safe memory coding built-in, it doesnt guarantee the coding to be 100% bug free or fully secure from zero day attacks.
No one is making this absurd claim, but what we know is that majority of RCE bugs are memory related.
*Shrug* Some people prefer simplicity.
> I find it almost inexcusable at this point.
C has been deployed, since the 80s, in millions upon millions of life-critical systems with loss of human life due to C-specific errors close to zero.
It's fascinating that you think that, until Rust (or modern C++ came along), people were being killed in significant numbers due to C-specific errors.
It is still quite common to see proprietary compiler-suites in the embedded world.
Most only support C. Some do not even support C99 :)
Sure for many ARM targets (e.g. Cortex series), you can use GCC or LLVM etc. and get access to C++.
Many embedded shops targetting the lower end (8-32 bit CPUs <100MHz + 8-32kb RAM) prefer C for the simplicity.
1: STMicro announced back in 2016 that they’d sold more than 2 billion of these chips, so it’s not some obscure little niche processor.
You can _only_ use it anywhere you can use Linux, BSD, or FreeRTOS, which also means you can use C++ and Rust.
Anyway, I still think the point stands: many lower level targets do not have C++ or Rust compilers, but do have okay-ish C compilers.
You replied to
> I can think of several embedded platforms where Rust or C++ is not even an option.
… asking “Go on, name a few”. I understood this as “name a few targets for which C++ and/or Rust is unavailable”.
These targets do exist and are perhaps more common than you assume. In my own experience, it is mostly seen with small micros though.
But perhaps I misunderstood you?
Of course there will always platforms where C remains the only option because honestly, C is just quasi-portable glorified asm; but the point is that if you have the luxury of a platform that can run Libwebsockets, you're most likely in a position to use C++ or Rust, or some other safe language. That is the point.
> Genuine question. With Rust and C++, even for embedded systems, what is the use case for starting a new project that does binary encoding and network with such an unsafe language like C?
And I gave a genuine answer :)
If you want to restrict the context to libwebsockets, I suggest you edit your question to clarify that.
Network code != libwebsockets.
I have written Ethernet-connected firmware without OS/RTOS on 8-32 bit MCUs that definitely would not be able to run libwebsocket.
There is a big world in embedded between PLCs and e.g. RasPi. That is often where you use C (in my experience).
In addition there are tiny microcontrollers (8051/AT89, STM32 etc)
Essentially these are Platforms with
1. Very specific instruction set (DSPs) 2. extremely limited resources 3. specialized toolchain and no LLVM support
And because C is more popular in embedded, new vendors are more likely to pick it for new platforms instead of Rust, which doesn't help.
C isn't great, but at least it's relatively simple and is well understood.
What you're suggesting is a nightmare scenario.