Ver código fonte

Fix virtual printer dropping null-terminated MQTT payloads from OrcaSlicer Linux (#927)

  OrcaSlicer's Linux BBLNetworkPlugin publishes MQTT payloads with the
  C-string null terminator included in the length, so decoded messages
  arrived as `{…}\x00`. The strict json.loads() raised JSONDecodeError
  and the publish handler silently returned — pushall, get_version, and
  project_file were never answered, and the slicer hit its 60 s sync
  timeout. Print_queue mode only (proxy mode tunnels MQTT). The b069b521
  serial-adaptation fix was correct but ran past this earlier silent
  failure.

  _handle_publish now strips trailing \x00/whitespace before parsing and
  logs the raw payload on any remaining decode failure so future silent
  variants are visible in support bundles.
maziggy 4 meses atrás
pai
commit
68920f8c62

Diferenças do arquivo suprimidas por serem muito extensas
+ 1 - 0
CHANGELOG.md


+ 13 - 3
backend/app/services/virtual_printer/mqtt_server.py

@@ -843,9 +843,19 @@ class SimpleMQTTServer:
                 self._client_serials[client_id] = client_serial
 
             try:
-                data = json.loads(message)
-            except json.JSONDecodeError:
-                return  # Non-JSON payloads on request topic are safely ignored
+                # Some slicer builds (observed with OrcaSlicer on Linux, #927)
+                # include the C-string null terminator in the MQTT payload
+                # length, so the decoded message ends with \x00. Real brokers
+                # pass the bytes through; strict json.loads raises "Extra data"
+                # and every pushall/get_version/project_file silently dropped.
+                data = json.loads(message.rstrip("\x00 \r\n\t"))
+            except json.JSONDecodeError as e:
+                logger.debug(
+                    "MQTT publish JSON decode failed: %s (payload=%r)",
+                    e,
+                    message[:200],
+                )
+                return
 
             # Handle pushing command (status request)
             if "pushing" in data:

+ 23 - 0
backend/tests/unit/test_vp_mqtt_server.py

@@ -152,6 +152,29 @@ class TestPublishHandlerAdaptiveSerial:
         assert b'"command": "push_status"' in all_bytes
         assert server._client_serials["c1"] == "CUSTOMSERIAL123"
 
+    def test_handle_publish_tolerates_null_terminated_payload(self):
+        """#927: OrcaSlicer on Linux appends the C-string \\0 to MQTT payloads.
+        The handler must still parse and respond rather than silently dropping."""
+        server = _make_server(serial="01P00A391800001")
+        server._client_serials["c1"] = server.serial
+
+        writer = MagicMock()
+        writer.write = MagicMock()
+        writer.drain = AsyncMock()
+
+        topic = "device/01P00A391800001/request"
+        topic_bytes = topic.encode("utf-8")
+        # Real-world bytes captured from EdwardChamberlain's support log: the
+        # JSON ends with an extra \x00 that strict json.loads rejects.
+        message_bytes = b'{"pushing":{"command":"pushall","sequence_id":"7"}}\x00'
+        payload = len(topic_bytes).to_bytes(2, "big") + topic_bytes + message_bytes
+
+        asyncio.run(server._handle_publish(0x30, payload, writer, "c1"))
+
+        all_bytes = b"".join(call.args[0] for call in writer.write.call_args_list)
+        assert b"device/01P00A391800001/report" in all_bytes
+        assert b'"command": "push_status"' in all_bytes
+
 
 class TestClientSerialLifecycle:
     """_client_serials must be cleaned up on disconnect/stop to avoid leaks."""

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff