Skip to content

HTTP POSTs to Sonoff B05BL (DIY mode) fail intermittently with lws #3599

Description

@RodrigoDornelles

A Sonoff B05BL in DIY mode randomly refuses POSTs sent through libwebsockets — it answers "read failed" and the bulb doesn't switch. The same request sent with curl or a raw socket works every time. Capturing the traffic, the bytes lws sends are identical to curl's, but the device never ACKs lws's first data packet, so the request is lost and times out.

Environment: libwebsockets 3e8ec88, LWS_WITH_LIBUV=ON, foreign libuv loop, cleartext HTTP/1.1.

Repository: https://github.com/gly-engine/third-party-issues/tree/main/sonoff-warmcat-libwebsocket

Server: Sonoff B05BL, DIY mode, POST /zeroconf/switch with JSON body and Connection: close. Reports Server: openresty but it's really a bare lwIP/ESP stack (win 4380, MSS 1460, no SACK/timestamps).

Works every time with curl:

curl -X POST -H "Content-Type: application/json" \
  -d '{"deviceid":"10023d8f20","data":{"switch":"on"}}' \
  http://192.168.3.233:8081/zeroconf/switch

Works every time raw:

printf 'POST /zeroconf/switch HTTP/1.1\r\nHost: x\r\nContent-Type: application/json\r\nContent-Length: 48\r\nConnection: close\r\n\r\n{"deviceid":"10023d8f20","data":{"switch":"on"}}' \
  | nc 192.168.3.233 8081

Fails intermittently (sometimes always) through lws with LWS_CALLBACK_CLIENT_CONNECTION_ERROR = "read failed".

Observations

  • tcpdump: curl/raw → device ACKs + 145-byte response → close (~28ms). lws → first data segment un-ACKed ~244ms → TCP retransmit → device ACKs but answers nothing, sits ~3s, FINs empty → "read failed".
  • The device has a very fragile/non-standard HTTP parser, and that's exactly what the lws failure looks like. If the request is even slightly off — Content-Length bigger than the body actually sent, no Content-Length, a path without the leading slash, or HTTP/1.0 — it doesn't reject it cleanly: it just hangs for a few seconds and closes with no response, which curl reports as curl: (52) Empty reply from server. The lws failure is the same empty-reply behavior. So from the device's point of view the lws request seems to arrive malformed/incomplete (e.g. it got headers promising a body but not all of it in time), even though the captured bytes match a working curl request.
  • lws request is byte-identical to curl's (same line/headers/body, single segment).
  • Headers (Pragma/Cache-Control/Origin lws adds): device 200s with them via curl — not the cause.
  • HTTP/2: cleartext stays H1; building -DLWS_ROLE_H2=OFF changes nothing.
  • Header+body in one segment vs the default separate body write — no change.
  • Delay 0–50ms between connect and send (raw socket) — 48/48 ok.
  • Blocking vs non-blocking connect (raw socket) — both 100%.
  • Wi-Fi is stable and curl passes 100% of the tests expected to pass — so this isn't link flakiness, it's specific to lws.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions