Skip to content

Commit 14a718e

Browse files
committed
propertly implement the internal function
1 parent 32c06ae commit 14a718e

File tree

3 files changed

+73
-53
lines changed

3 files changed

+73
-53
lines changed

cmd/arduino-app-cli/monitor/monitor.go

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121

2222
"github.com/spf13/cobra"
2323

24+
"github.com/arduino/arduino-app-cli/cmd/feedback"
2425
"github.com/arduino/arduino-app-cli/internal/monitor"
2526
)
2627

@@ -29,7 +30,11 @@ func NewMonitorCmd() *cobra.Command {
2930
Use: "monitor",
3031
Short: "Attach to the microcontroller serial monitor",
3132
RunE: func(cmd *cobra.Command, args []string) error {
32-
start, err := monitor.NewMonitorHandler(&stdInOutProxy{stdin: os.Stdin, stdout: os.Stdout}) // nolint:forbidigo
33+
stdout, _, err := feedback.DirectStreams()
34+
if err != nil {
35+
return err
36+
}
37+
start, err := monitor.NewMonitorHandler(&combinedReadWrite{r: os.Stdin, w: stdout}) // nolint:forbidigo
3338
if err != nil {
3439
return err
3540
}
@@ -40,25 +45,19 @@ func NewMonitorCmd() *cobra.Command {
4045
}
4146
}
4247

43-
type stdInOutProxy struct {
44-
stdin io.Reader
45-
stdout io.Writer
48+
type combinedReadWrite struct {
49+
r io.Reader
50+
w io.Writer
4651
}
4752

48-
func (s stdInOutProxy) ReadMessage() (int, []byte, error) {
49-
var p [1024]byte
50-
n, err := s.stdin.Read(p[:])
51-
if err != nil {
52-
return 0, nil, err
53-
}
54-
return 1, p[:n], nil
53+
func (crw *combinedReadWrite) Read(p []byte) (n int, err error) {
54+
return crw.r.Read(p)
5555
}
5656

57-
func (s stdInOutProxy) WriteMessage(messageType int, data []byte) error {
58-
_, err := s.stdout.Write(data)
59-
return err
57+
func (crw *combinedReadWrite) Write(p []byte) (n int, err error) {
58+
return crw.w.Write(p)
6059
}
6160

62-
func (s stdInOutProxy) Close() error {
61+
func (crw *combinedReadWrite) Close() error {
6362
return nil
6463
}

internal/api/handlers/monitor.go

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"net"
2222
"net/http"
2323
"strings"
24-
"time"
2524

2625
"github.com/gorilla/websocket"
2726

@@ -40,27 +39,18 @@ func HandleMonitorWS(allowedOrigins []string) http.HandlerFunc {
4039
}
4140

4241
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
49-
}
50-
5142
// Upgrade the connection to websocket
5243
conn, err := upgrader.Upgrade(w, r, nil)
5344
if err != nil {
5445
// Remember to close monitor connection if websocket upgrade fails.
55-
mon.Close()
5646

5747
slog.Error("Failed to upgrade connection", slog.String("error", err.Error()))
5848
render.EncodeResponse(w, http.StatusInternalServerError, map[string]string{"error": "Failed to upgrade connection: " + err.Error()})
5949
return
6050
}
6151

6252
// Now the connection is managed by the websocket library, let's move the handlers in the goroutine
63-
start, err := monitor.NewMonitorHandler(conn)
53+
start, err := monitor.NewMonitorHandler(&wsReadWriteCloser{conn: conn})
6454
if err != nil {
6555
slog.Error("Unable to start monitor handler", slog.String("error", err.Error()))
6656
render.EncodeResponse(w, http.StatusInternalServerError, models.ErrorResponse{Details: "Unable to start monitor handler: " + err.Error()})
@@ -114,3 +104,50 @@ func checkOrigin(origin string, allowedOrigins []string) bool {
114104
slog.Error("WebSocket origin check failed", slog.String("origin", origin))
115105
return false
116106
}
107+
108+
type wsReadWriteCloser struct {
109+
conn *websocket.Conn
110+
111+
buff []byte
112+
}
113+
114+
func (w *wsReadWriteCloser) Read(p []byte) (n int, err error) {
115+
if len(w.buff) > 0 {
116+
n = copy(p, w.buff)
117+
w.buff = w.buff[n:]
118+
return n, nil
119+
}
120+
121+
ty, message, err := w.conn.ReadMessage()
122+
if err != nil {
123+
return 0, mapWebSocketErrors(err)
124+
}
125+
if ty != websocket.BinaryMessage {
126+
return 0, fmt.Errorf("unexpected websocket message type: %d", ty)
127+
}
128+
w.buff = message
129+
130+
n = copy(p, w.buff)
131+
w.buff = w.buff[n:]
132+
return n, nil
133+
}
134+
135+
func (w *wsReadWriteCloser) Write(p []byte) (n int, err error) {
136+
err = w.conn.WriteMessage(websocket.BinaryMessage, p)
137+
if err != nil {
138+
return 0, mapWebSocketErrors(err)
139+
}
140+
return len(p), nil
141+
}
142+
143+
func (w *wsReadWriteCloser) Close() error {
144+
w.buff = nil
145+
return w.conn.Close()
146+
}
147+
148+
func mapWebSocketErrors(err error) error {
149+
if websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway, websocket.CloseNoStatusReceived, websocket.CloseAbnormalClosure) {
150+
return net.ErrClosed
151+
}
152+
return err
153+
}

internal/monitor/monitor.go

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17,44 +17,27 @@ package monitor
1717

1818
import (
1919
"errors"
20-
"fmt"
2120
"io"
2221
"log/slog"
2322
"net"
2423
"time"
25-
26-
"github.com/gorilla/websocket"
2724
)
2825

29-
type MessageReaderWriter interface {
30-
ReadMessage() (messageType int, p []byte, err error)
31-
WriteMessage(messageType int, data []byte) error
32-
Close() error
33-
}
26+
const monitorAddr = "127.0.0.1:7500"
3427

35-
func NewMonitorHandler(rw MessageReaderWriter) (func(), error) {
28+
func NewMonitorHandler(rw io.ReadWriteCloser) (func(), error) {
3629
// Connect to monitor
37-
mon, err := net.DialTimeout("tcp", "127.0.0.1:7500", time.Second)
30+
monitor, err := net.DialTimeout("tcp", monitorAddr, time.Second)
3831
if err != nil {
3932
return nil, err
4033
}
4134

4235
return func() {
43-
monitorStream(mon, rw)
36+
monitorStream(monitor, rw)
4437
}, nil
4538
}
4639

47-
func monitorStream(mon net.Conn, rw MessageReaderWriter) {
48-
logWebsocketError := func(msg string, err error) {
49-
// Do not log simple close or interruption errors
50-
if websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway, websocket.CloseNoStatusReceived, websocket.CloseAbnormalClosure) {
51-
if e, ok := err.(*websocket.CloseError); ok {
52-
slog.Error(msg, slog.String("closecause", fmt.Sprintf("%d: %s", e.Code, err)))
53-
} else {
54-
slog.Error(msg, slog.String("error", err.Error()))
55-
}
56-
}
57-
}
40+
func monitorStream(mon net.Conn, rw io.ReadWriteCloser) {
5841
logSocketError := func(msg string, err error) {
5942
if !errors.Is(err, net.ErrClosed) && !errors.Is(err, io.EOF) {
6043
slog.Error(msg, slog.String("error", err.Error()))
@@ -63,14 +46,15 @@ func monitorStream(mon net.Conn, rw MessageReaderWriter) {
6346
go func() {
6447
defer mon.Close()
6548
defer rw.Close()
49+
buff := [1024]byte{}
6650
for {
6751
// Read from websocket and write to monitor
68-
_, msg, err := rw.ReadMessage()
52+
n, err := rw.Read(buff[:])
6953
if err != nil {
70-
logWebsocketError("Error reading from websocket", err)
54+
logSocketError("Error reading from websocket", err)
7155
return
7256
}
73-
if _, err := mon.Write(msg); err != nil {
57+
if _, err := mon.Write(buff[:n]); err != nil {
7458
logSocketError("Error writing to monitor", err)
7559
return
7660
}
@@ -88,8 +72,8 @@ func monitorStream(mon net.Conn, rw MessageReaderWriter) {
8872
return
8973
}
9074

91-
if err := rw.WriteMessage(websocket.BinaryMessage, buff[:n]); err != nil {
92-
logWebsocketError("Error writing to websocket", err)
75+
if _, err := rw.Write(buff[:n]); err != nil {
76+
logSocketError("Error writing to websocket", err)
9377
return
9478
}
9579
}

0 commit comments

Comments
 (0)