Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Libwebsockets: pure C library for http, websockets, MQTT (github.com/warmcat)
171 points by seansh on Jan 6, 2024 | hide | past | favorite | 61 comments


I started using this library last week. It was easy to get started and running. When I wanted to do stuff beyond the provided example I found nothing worked as I had expected. For example, you supply a user pointer when you create a lws_context. There is a user pointer argument in the callback where you do all the actual work. One would expect these to be the Same, but they are not. Instead you have to use two different calls to get from your current connection to the user pointer which is set for all connections. Did it work? Yes. But it was very suprising behavior. Another problem I ran into was getting the event loop to be non-blocking for use in a coroutine. Appearently I was expected to use one of the preselected event libraries, which I was not. I was expected to implement a dozen or so callbacks for which there was little or no helpfull documentation. Eventually I found that there was a hack where I could pass -1 to the timeout parameter. Now the service call was non blocking. It would have been fine if the call had blocked for a millisecond or so, but that did not work. So the time-out parameter was either -1 or any other value. I kept bumping into suprises like this. The library solved a problem for me, so I will continue to use it. Unfortunately I cant recommend it to anyone unless you are willing and able to spend a good portion of your time getting this beast tamed.


But that "just" sounds like lacking documentation and a bit of missing support for you specific use case, or was anything actually broken or implemented in a completely stupid way? I didn't have to use websockets in C yet, but it would be good to have something ready in case I do at some point. Having to dig through source code instead of docs is something I'm used to. Are there any other contenders you looked at?


I've had a very similar experience a few weeks ago and even fell for the same double-userdata misunderstanding.

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...


I found the same with this library. I'm not sure if it's missing documentation, unintuitive design or both, but I always found it a bit of a struggle to use beyond simple cases.


Has anyone bridged this to Java with JNI and used it well for a HTTP/2 client? Considering doing this some time within the next month but if someone already has done work would be useful to see.

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.


Not a Java developer, but why not use an existing Java library for this? Don't know if they're using C or something else behind the scenes, but a quick Google search brings up a few.


Well because in java you wont be able to do low level stuff like managing individual memory blocks that is essential in a websockets networking lib.


This is just false with NIO buffers. There’s a large corpus of Netty-based servers and clients for all sorts of binary protocols.


I wonder how other websockets libraries for java do it if it is impossible /s


Look at Netty.io, that's the defecto java networking library. They have some optional JNI stuff



When I have a chance, I want to compile this with Cosmopolitan Libc!


Yes, and if that works, it would be great if jart could roll this into redbean.


FWIW there is ongoing work with good progress to add websocket support to redbean (https://github.com/jart/cosmopolitan/pull/967)


Good to know. But the OP library adds even more (http2, MQTT).

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.


Damn I just yesterday was pulling my hair out to get boost::beast to run (how unergonomic can an api be?!). Now I‘m considering to switch but what I really need is long term stability. I‘d rather like to not ever touch this code again, but it should compile over the next 5-10 years. I‘m not sure which one would be preferable in that regard


libcurl? Old project, actively maintained, used in lots of embedded devices, maintainer understands the needs for stability, no silly rewrites.


Afaik libcurl is for clients only, but I also need to implement a server. Otherwise it sounds like exactly what I wish for from the governance perspective


This looks promising: https://gitlab.com/eidheim/Simple-WebSocket-Server

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).


libcurl is excellent.


This is what the Mosquitto MQTT broker is using to offer Websocket support.

Interesting to see that this has a MQTT client integrated.


I've used this at point in the past and it did seem relatively pain free to implement. Never quite got round to how certs could be reloaded though, while still honouring requests.


Love this library. Maintainer answers GitHub issues too!


Amazing library! Super simple and well maintained


Any thoughts on how it compares to libcurl?


As far as I can tell, libcurl's support for websocket is fairly minimal with the main use case being "connect to this ws endpoint and send/receive data". Meanwhile libwebsockets allows you to do everything under the sun that you can think of with ws


steal the example programs, and make them your own.

good performance, good reliability.


HTTP, websockets, MQTT, ... plus the kitchen sink it seems:

  - 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 
  - ...


Is this a joke? On first glance the project looks like a library for serving and consuming HTTP(s)/Web sockets. Everything else you mention appears to be optional integrations, like SSH, or separate examples provided by the developers in other repos to showcase diverse usecases, like the ESP32 applications.


My comment was suggesting that it's more than just a "lightweight" HTTP/websockets/MQTT library.

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.


This is just the endemic result of C++ lacking a package manager.

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


This... really doesn't match my experience at all. Most libraries I encounter in C really are self-contained. Seeing a random websockets library also feature a JPEG decoder is really surprising.


This is C though, so a C++ package manager wouldn't help ;)


Don‘t you need a http server to bring about any websocket connection through the upgrade mechanism? Otherwise it would only be a websocket client library


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?

I find it almost inexcusable at this point.


> what is the use case for starting a new project that does binary encoding and network with such an unsafe language like C?

*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.


You can consider translating this library to Rust. As long as you can't or don't have the time commitment, we take this one over some fable or cool new library somewhere. This one has 13 years proven battlefield use. Any big bugs already iron out or at least not so obvious. 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. This is even worst off when the library say written in Rust just come out less than a couple years or even 6 mths.


> You can consider translating this library to Rust.

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);
    };


>>> 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?

>> 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").


> 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.


Despite what you may think, C is still the most portable language. I can think of several embedded platforms where Rust or C++ is not even an option.


Should be noted that Rust didn't have a 30 year head start unlike C, so it's obvious that it won't have support for every single embedded platform under the sun, at least not yet. It could in principle.

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.


Go on, name a few.


The STM8 processor for instance has no C++ or Rust compiler to my knowledge, even though it was produced in huge volumes [1].

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.


What's even funnier is that for STM8 one can choose between four compilers, all offering first class citizen support for the platform. Also, it's pretty convenient that two of them, namely IAR and SDCC, support the current language standards.


You can't run libwebsockets on STM8.

You can _only_ use it anywhere you can use Linux, BSD, or FreeRTOS, which also means you can use C++ and Rust.


Anything POSIX should do, no?

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?


Context is everything. Bringing up STM8 in the context of libwebsockets is as relevant as bringing up PLCs, not at all.

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.


I was replying to this

> 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).


your point does stand. ignore the trolls.


There are several DSP & VLIW platforms that have their own toolchain (not gcc), and they only support C programming language (for example Trimedia VLIW processors, TM1300, PNX1500, PNX1700 and a lot more SoC based on TM32/TM64 cores).

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


whatever software you're using, including HN the site, nearly all boil down to C at the bottom, C is fine, now and in the future.


If we are playing that game, why stop there? why not ASM? Better yet, why don't you be a real developer and use a magnet?


Because most people want portable programs, whithout having to rewrite them for each target. C helps a lot in this respect, compared to assembly.


Go on then, tell me which platform you can't use Rust or at least C++ when you can use C with standard library?


Not sure what you mean by “with standard library”, but STM8 has no C++ or Rust compiler that I know of.

For other MCUs where you do get C++ support, you often only get C++98.

Replied to your other comment about this as well.


You're not going to be able to run Libwebsockets on STM8, so not sure why that platfrom matters?

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


Perhaps I read your statement too literally. If we are only talking about targets able to run libwebsockets unmodified, STM8 is of course irrelevant.

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).


Why would I do it? You compared C to asm, not rust.


From the website, "It has been developed continuously since 2010".


> With Rust and C++

C isn't great, but at least it's relatively simple and is well understood.

What you're suggesting is a nightmare scenario.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: