-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
use net/x/websocket for tinygo (for now)
- Loading branch information
1 parent
f8fe441
commit 46675ed
Showing
4 changed files
with
228 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
//go:build tinygo | ||
|
||
package device | ||
|
||
import ( | ||
"net/http" | ||
"net/url" | ||
"time" | ||
|
||
"golang.org/x/net/websocket" | ||
) | ||
|
||
func newConfig(wsUrl *url.URL, user, passwd string) (*websocket.Config, error) { | ||
|
||
// Set the origin to match the WebSocket server’s scheme and host | ||
origin := &url.URL{Scheme: "http", Host: wsUrl.Host} | ||
if wsUrl.Scheme == "wss" { | ||
origin.Scheme = "https" | ||
} | ||
|
||
// Configure the websocket | ||
config, err := websocket.NewConfig(wsUrl.String(), origin.String()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// If valid user, set the basic auth header for the request | ||
if user != "" { | ||
req, err := http.NewRequest("GET", wsUrl.String(), nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
req.SetBasicAuth(user, passwd) | ||
config.Header = req.Header | ||
} | ||
|
||
return config, nil | ||
} | ||
|
||
func wsDial(url *url.URL, user, passwd string) { | ||
cfg, err := newConfig(url, user, passwd) | ||
if err != nil { | ||
LogError("Configuring websocket", "err", err) | ||
return | ||
} | ||
|
||
for { | ||
// Dial the websocket | ||
conn, err := websocket.DialConfig(cfg) | ||
if err == nil { | ||
// Service the client websocket | ||
wsClient(conn) | ||
} else { | ||
LogError("Dialing", "url", url, "err", err) | ||
} | ||
|
||
// Try again in a second | ||
time.Sleep(time.Second) | ||
} | ||
} | ||
|
||
func wsClient(conn *websocket.Conn) { | ||
defer conn.Close() | ||
|
||
var link = &wsLink{conn: conn} | ||
var ann = announcement{ | ||
Id: root.Id, | ||
Model: root.Model, | ||
Name: root.Name, | ||
DeployParams: root.DeployParams, | ||
} | ||
var pkt = &Packet{ | ||
Dst: ann.Id, | ||
Path: "/announce", | ||
} | ||
|
||
pkt.Marshal(&ann) | ||
|
||
// Send announcement | ||
LogInfo("Sending announcement", "pkt", pkt) | ||
err := link.Send(pkt) | ||
if err != nil { | ||
LogError("Sending", "err", err) | ||
return | ||
} | ||
|
||
// Receive welcome within 1 sec | ||
pkt, err = link.receiveTimeout(time.Second) | ||
if err != nil { | ||
LogError("Receiving", "err", err) | ||
return | ||
} | ||
|
||
LogInfo("Reply from announcement", "pkt", pkt) | ||
if pkt.Path != "/welcome" { | ||
LogError("Not welcomed, got", "path", pkt.Path) | ||
return | ||
} | ||
|
||
LogInfo("Adding Uplink") | ||
uplinksAdd(link) | ||
|
||
// Send /state packets to all devices | ||
devicesSendState(link) | ||
|
||
// Route incoming packets down to the destination device. Stop and | ||
// disconnect on EOF. | ||
|
||
LogInfo("Receiving packets") | ||
for { | ||
pkt, err := link.receivePoll() | ||
if err != nil { | ||
LogError("Receiving packet", "err", err) | ||
break | ||
} | ||
LogInfo("Route packet DOWN", "pkt", pkt) | ||
deviceRouteDown(pkt.Dst, pkt) | ||
} | ||
|
||
LogInfo("Removing Uplink") | ||
uplinksRemove(link) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,8 @@ | ||
//go:build !tinygo | ||
|
||
// TODO get gorilla/websocket working on tinygo. Currently hit: | ||
// ../../../go/pkg/mod/github.com/gorilla/[email protected]/client.go:18:2: package net/http/httptrace is not in std (/root/... | ||
|
||
package device | ||
|
||
import ( | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
//go:build tinygo | ||
|
||
package device | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"html/template" | ||
"net" | ||
"sync" | ||
"time" | ||
|
||
"golang.org/x/net/websocket" | ||
) | ||
|
||
type wsLink struct { | ||
conn *websocket.Conn | ||
sync.Mutex | ||
lastRecv time.Time | ||
lastSend time.Time | ||
} | ||
|
||
type announcement struct { | ||
Id string | ||
Model string | ||
Name string | ||
DeployParams template.HTML | ||
} | ||
|
||
func (l *wsLink) Send(pkt *Packet) error { | ||
data, err := json.Marshal(pkt) | ||
if err != nil { | ||
return fmt.Errorf("Marshal error: %w", err) | ||
} | ||
l.Lock() | ||
defer l.Unlock() | ||
if err := websocket.Message.Send(l.conn, string(data)); err != nil { | ||
return fmt.Errorf("Send error: %w", err) | ||
} | ||
l.lastSend = time.Now() | ||
return nil | ||
} | ||
|
||
func (l *wsLink) Close() { | ||
l.conn.Close() | ||
} | ||
|
||
func (l *wsLink) receive() (*Packet, error) { | ||
var data []byte | ||
var pkt Packet | ||
|
||
if err := websocket.Message.Receive(l.conn, &data); err != nil { | ||
return nil, err | ||
} | ||
l.lastRecv = time.Now() | ||
if err := json.Unmarshal(data, &pkt); err != nil { | ||
LogError("Unmarshal Error", "data", string(data)) | ||
return nil, fmt.Errorf("Unmarshalling error: %w", err) | ||
} | ||
return &pkt, nil | ||
} | ||
|
||
func (l *wsLink) receiveTimeout(timeout time.Duration) (*Packet, error) { | ||
l.conn.SetReadDeadline(time.Now().Add(timeout)) | ||
pkt, err := l.receive() | ||
l.conn.SetReadDeadline(time.Time{}) | ||
return pkt, err | ||
} | ||
|
||
var pingDuration = 4 * time.Second | ||
var pingTimeout = 2*pingDuration + time.Second | ||
|
||
func (l *wsLink) receivePoll() (*Packet, error) { | ||
for { | ||
if time.Since(l.lastSend) >= pingDuration { | ||
if err := l.Send(&Packet{Path: "/ping"}); err != nil { | ||
return nil, err | ||
} | ||
} | ||
pkt, err := l.receiveTimeout(time.Second) | ||
if err == nil { | ||
if pkt.Path == "/ping" { | ||
continue | ||
} | ||
return pkt, nil | ||
} | ||
if netErr, ok := err.(*net.OpError); ok && netErr.Timeout() { | ||
if time.Since(l.lastRecv) > pingTimeout { | ||
return nil, err | ||
} | ||
continue | ||
} | ||
return nil, err | ||
} | ||
return nil, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,8 @@ | ||
//go:build !tinygo | ||
|
||
// TODO get gorilla/websocket working on tinygo. Currently hit: | ||
// ../../../go/pkg/mod/github.com/gorilla/[email protected]/client.go:18:2: package net/http/httptrace is not in std (/root/... | ||
|
||
package device | ||
|
||
import ( | ||
|