Oddity 1. socket with uuid * does not exist
Consider the following simple edge driver …
local cosock = require "cosock"
local Driver = require "st.driver"
local log = require "log"
Driver("bug", {
discovery = function(_, _, should_continue)
local address = "192.168.0.10"
local port = 2222
local block_size = 128
local block_count = 16
log.debug(""
.. "# on linux host reachable by smartthings hub on address "
.. address
.. ", run:\n"
.. "while :; do dd if=/dev/zero bs="
.. block_size
.. " count="
.. block_count
.. " | socat - TCP-LISTEN:"
.. port
.. ",reuseaddr; done"
)
while should_continue() do
local socket, socket_error = cosock.socket.tcp()
if socket_error then
log.error("socket", socket_error)
else
local _, connect_error = socket:connect(address, port)
if connect_error then
log.error("connect", connect_error)
else
socket:settimeout(1)
local receive_count = 0
while true do
local whole, receive_error, part = socket:receive(block_size)
local receive = whole or part
if receive then
receive_count = receive_count + #receive
end
if receive_error then
log.info("receive", receive_count, receive_error)
break
end
end
end
socket:close()
end
cosock.socket.sleep(1)
end
end,
}):run()
… whose discovery implementation connects and reads everything it can from a server every second until discovery no longer should_continue
.
As the code suggests, one can implement the server simply on a linux machine at the address
referenced in the code (change as necessary).
while :; do dd if=/dev/zero bs=128 count=16 | socat - TCP-LISTEN:2222,reuseaddr; done
It should work fine but often it does not and I see this in the log as a connect_error
.
socket with uuid `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx` does not exist
Huh?
What can I do about that?
Oddity 2. First part of HTTP response missing
I am writing a LAN-based edge driver that interfaces with its devices (for commands and refreshing status) using HTTP. I always expect a complete, well-formed HTTP response but, occasionally, the first part of the response will get dropped and the first part that my code will see will be mid-stream.
I don’t see this behavior outside the SmartThings hub.
I have port-mirrored my SmartThings hub to my Linux development machine on my switch and witnessed a complete, well-formed response from the device on the wire being seen by my edge driver as starting mid-body. This clearly suggests that something is being dropped in the hub.
I wrote the driver above in effort to demonstrate this simply but, so far, it has just exposed another oddity (which I had occasionally seen before as well).
Oddity 3. socket:receive returns success and error all at once.
I am not sure this is a SmartThings-specific oddity or not but, regardless, it certainly seems odd to me. I have always considered these things (success and error) to be mutually exclusive.
However …
local whole, receive_error, part = socket:receive(2048)
… will sometimes both return (successfully) something in whole
or part
but at the same time will also return closed
in receive_error
.
Oddity 4. TCP listener on INADDR_ANY needs to restart on IP address change.
When the SmartThings hub decides it needs to change its IP address (for example, when its DHCP lease cannot be renewed on its old address), TCP listeners on any/every interface/address (INADDR_ANY) are no longer reachable. Restarting them fixes the problem but one should not have to do this. The socket should continue to be bound to any/every interface/address.
Oddity 5. cosock.socket.bind
shortcut does not work.
This cosock shortcut creates the tcp
socket, bind
s it, listen
s on it and then sets the reuseaddr
socket option to true. I have never seen this strange behavior before. Even the code is confused about it
-- I don't know why, but this is what the docs say
ret, err = skt:setoption("reuseaddr", true)
I don’t know what/where “the docs” are but usually such reuseaddr
behavior is required before the bind
as that is where address reuse becomes an issue.
Anyway, much worse behavior is seen on the SmartThings hub where my code fails in this setoption
call. I stopped using the cosock.socket.bind
shortcut and do the tcp
create, bind
and listen
steps myself.