FHEMWEB: WebSocket-Abbruch „Invalid frame header“ bei UTF8-geflaggten Readings

Begonnen von schwatter, 14 November 2026, 09:09:44

Vorheriges Thema - Nächstes Thema

schwatter

Morgen Rudi,

bei FHEMWEB über HTTPS reißt ab und zu WebSocket ab. Chrome meldet dann ,,Invalid frame header", und die Seite verbindet sich neu. Ausgelöst wird das bei mir durch HMCCU-Geräte, deren Readings Perl-intern als UTF-8 markiert sind. Über normales HTTP tritt der Fehler (fast) nicht auf.

Ich habe die Ursache in FW_addToWritebuffer gefunden und einen kleinen Patch geschrieben. Zum Nachstellen habe ich eine Demo mit Dummy und Notify erstellt. Mit dem Patch läuft es bei mir seitdem über HTTPS und HTTP stabil.

define wsDemo dummy
attr wsDemo room wsDemo
attr wsDemo setList normal:noArg ascii:noArg grad:noArg euro:noArg gradbytes:noArg lang:noArg mix:noArg
attr wsDemo webCmd normal:ascii:grad:euro:gradbytes:lang:mix
attr wsDemo stateFormat result
define wsDemo_n notify wsDemo:(normal|ascii|grad|euro|gradbytes|lang|mix) { readingsSingleUpdate($defs{wsDemo}, "result", +{ normal => "Test normal ".TimeNow(), ascii => Encode::decode("UTF-8", "Test ascii ".TimeNow()), grad => Encode::decode("UTF-8", "Test grad 17.3 \xc2\xb0C ".TimeNow()), euro => Encode::decode("UTF-8", "Test euro 5 \xe2\x82\xac ".TimeNow()), gradbytes => "Test gradbytes 17.3 \xc2\xb0C ".TimeNow(), lang => Encode::decode("UTF-8", "Test lang ".("x" x 200)." ".TimeNow()), mix => Encode::decode("UTF-8", "Test mix 17.3")." \xc2\xb0C ".TimeNow() }->{$EVENT}, 1) }

--- a/FHEM/01_FHEMWEB.pm    (revision 31693)
+++ b/FHEM/01_FHEMWEB.pm    (working copy)
@@ -749,6 +749,18 @@
           if($hash->{encoding} && !$encoded && ($unicodeEncoding ||
                             (utf8::is_utf8($txt) && $txt =~ m/[^\x00-\xFF]/)));
   if( $hash->{websocket} ) {
+    # The frame must be built from UTF-8 bytes. A remaining UTF8-flagged
+    # string (chars <= 0xFF) would turn the header into UTF-8 (0x81 -> c2 81)
+    # when written by IO::Socket::SSL, or into invalid UTF-8 via plain syswrite.
+    if(utf8::is_utf8($txt)) {
+      my $b = $txt;
+      my $c;
+      if(utf8::downgrade($b, 1) && utf8::decode($c = $b)) {
+        $txt = $b;              # already UTF-8 bytes inside (as plain syswrite sends it)
+      } else {
+        utf8::encode($txt);     # real characters, e.g. from decode_json
+      }
+    }
     my $len = length($txt);
     if( $len < 126 ) {
       $txt = chr(0x81) . chr($len) . $txt;

Gruß schwatter

rudolfkoenig

Mit "attr global encoding bytestream" (Voreinstellung / Normalfall) muessen alle Module die Daten (Reading/etc) als UTF-8 halten, anders forumliert im "utf8:encode" Format.
Mit "attr global encoding unicode" muss alles im sog. Wide-Character (utf8:decode) Format gehalten werden.
Module, die mit der Aussenwelt kommunizieren (FHEMWEB/telnet/etc) konvertieren diese Daten entsprechend.
"attr global encoding unicode" ist nicht so alt, und wird nicht von allen Modulen beachtet.

Dein Demo beachtet das Attribut nicht, und funktioniert dementsprechend in der Voreinstellung nicht.
Wenn ich "attr global encoding unicode" setze, dann gibt es keine Ausfaelle.

Dein Patch wuerde nur einen kleinen Teil der falschen internen Codierung kaschieren, ich ziehe es vor, dass es auch hier sichtbar wird, und die Ursache abgestellt wird.