1616package handlers
1717
1818import (
19- "errors"
2019 "fmt"
21- "io"
2220 "log/slog"
2321 "net"
2422 "net/http"
@@ -28,59 +26,50 @@ import (
2826 "github.com/gorilla/websocket"
2927
3028 "github.com/arduino/arduino-app-cli/internal/api/models"
29+ "github.com/arduino/arduino-app-cli/internal/monitor"
3130 "github.com/arduino/arduino-app-cli/internal/render"
3231)
3332
34- func monitorStream (mon net.Conn , ws * websocket.Conn ) {
35- logWebsocketError := func (msg string , err error ) {
36- // Do not log simple close or interruption errors
37- if websocket .IsUnexpectedCloseError (err , websocket .CloseNormalClosure , websocket .CloseGoingAway , websocket .CloseNoStatusReceived , websocket .CloseAbnormalClosure ) {
38- if e , ok := err .(* websocket.CloseError ); ok {
39- slog .Error (msg , slog .String ("closecause" , fmt .Sprintf ("%d: %s" , e .Code , err )))
40- } else {
41- slog .Error (msg , slog .String ("error" , err .Error ()))
42- }
43- }
33+ func HandleMonitorWS (allowedOrigins []string ) http.HandlerFunc {
34+ upgrader := websocket.Upgrader {
35+ ReadBufferSize : 1024 ,
36+ WriteBufferSize : 1024 ,
37+ CheckOrigin : func (r * http.Request ) bool {
38+ return checkOrigin (r .Header .Get ("Origin" ), allowedOrigins )
39+ },
4440 }
45- logSocketError := func (msg string , err error ) {
46- if ! errors .Is (err , net .ErrClosed ) && ! errors .Is (err , io .EOF ) {
47- slog .Error (msg , slog .String ("error" , err .Error ()))
41+
42+ return func (w http.ResponseWriter , r * http.Request ) {
43+ // Connect to monitor
44+ mon , err := net .DialTimeout ("tcp" , "127.0.0.1:7500" , time .Second )
45+ if err != nil {
46+ slog .Error ("Unable to connect to monitor" , slog .String ("error" , err .Error ()))
47+ render .EncodeResponse (w , http .StatusServiceUnavailable , models.ErrorResponse {Details : "Unable to connect to monitor: " + err .Error ()})
48+ return
4849 }
49- }
50- go func () {
51- defer mon .Close ()
52- defer ws .Close ()
53- for {
54- // Read from websocket and write to monitor
55- _ , msg , err := ws .ReadMessage ()
56- if err != nil {
57- logWebsocketError ("Error reading from websocket" , err )
58- return
59- }
60- if _ , err := mon .Write (msg ); err != nil {
61- logSocketError ("Error writing to monitor" , err )
62- return
63- }
50+
51+ // Upgrade the connection to websocket
52+ conn , err := upgrader .Upgrade (w , r , nil )
53+ if err != nil {
54+ // Remember to close monitor connection if websocket upgrade fails.
55+ mon .Close ()
56+
57+ slog .Error ("Failed to upgrade connection" , slog .String ("error" , err .Error ()))
58+ render .EncodeResponse (w , http .StatusInternalServerError , map [string ]string {"error" : "Failed to upgrade connection: " + err .Error ()})
59+ return
6460 }
65- }()
66- go func () {
67- defer mon .Close ()
68- defer ws .Close ()
69- buff := [1024 ]byte {}
70- for {
71- // Read from monitor and write to websocket
72- n , err := mon .Read (buff [:])
73- if err != nil {
74- logSocketError ("Error reading from monitor" , err )
75- return
76- }
7761
78- if err := ws .WriteMessage (websocket .BinaryMessage , buff [:n ]); err != nil {
79- logWebsocketError ("Error writing to websocket" , err )
80- return
81- }
62+ // Now the connection is managed by the websocket library, let's move the handlers in the goroutine
63+ start , err := monitor .NewMonitorHandler (conn )
64+ if err != nil {
65+ slog .Error ("Unable to start monitor handler" , slog .String ("error" , err .Error ()))
66+ render .EncodeResponse (w , http .StatusInternalServerError , models.ErrorResponse {Details : "Unable to start monitor handler: " + err .Error ()})
67+ return
8268 }
83- }()
69+ go start ()
70+
71+ // and return nothing to the http library
72+ }
8473}
8574
8675func splitOrigin (origin string ) (scheme , host , port string , err error ) {
@@ -125,42 +114,3 @@ func checkOrigin(origin string, allowedOrigins []string) bool {
125114 slog .Error ("WebSocket origin check failed" , slog .String ("origin" , origin ))
126115 return false
127116}
128-
129- func HandleMonitorWS (allowedOrigins []string ) http.HandlerFunc {
130- // Do a dry-run of checkorigin, so it can panic if misconfigured now, not on first request
131- _ = checkOrigin ("http://localhost" , allowedOrigins )
132-
133- upgrader := websocket.Upgrader {
134- ReadBufferSize : 1024 ,
135- WriteBufferSize : 1024 ,
136- CheckOrigin : func (r * http.Request ) bool {
137- return checkOrigin (r .Header .Get ("Origin" ), allowedOrigins )
138- },
139- }
140-
141- return func (w http.ResponseWriter , r * http.Request ) {
142- // Connect to monitor
143- mon , err := net .DialTimeout ("tcp" , "127.0.0.1:7500" , time .Second )
144- if err != nil {
145- slog .Error ("Unable to connect to monitor" , slog .String ("error" , err .Error ()))
146- render .EncodeResponse (w , http .StatusServiceUnavailable , models.ErrorResponse {Details : "Unable to connect to monitor: " + err .Error ()})
147- return
148- }
149-
150- // Upgrade the connection to websocket
151- conn , err := upgrader .Upgrade (w , r , nil )
152- if err != nil {
153- // Remember to close monitor connection if websocket upgrade fails.
154- mon .Close ()
155-
156- slog .Error ("Failed to upgrade connection" , slog .String ("error" , err .Error ()))
157- render .EncodeResponse (w , http .StatusInternalServerError , map [string ]string {"error" : "Failed to upgrade connection: " + err .Error ()})
158- return
159- }
160-
161- // Now the connection is managed by the websocket library, let's move the handlers in the goroutine
162- go monitorStream (mon , conn )
163-
164- // and return nothing to the http library
165- }
166- }
0 commit comments