Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions daemon/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ package daemon

import (
"context"
"encoding/json"
"net"
"os"
"time"

"github.com/malamtime/cli/model"
"github.com/vmihailenco/msgpack/v5"
)

func IsSocketReady(ctx context.Context, socketPath string) bool {
Expand Down Expand Up @@ -38,7 +38,7 @@ func SendLocalDataToSocket(
},
}

encoded, err := msgpack.Marshal(data)
encoded, err := json.Marshal(data)
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions daemon/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package daemon

import (
"context"
"encoding/json"
"log/slog"

"github.com/ThreeDotsLabs/watermill/message"
"github.com/vmihailenco/msgpack/v5"
)

func SocketTopicProccessor(messages <-chan *message.Message) {
Expand All @@ -14,7 +14,7 @@ func SocketTopicProccessor(messages <-chan *message.Message) {
slog.InfoContext(ctx, "received message: ", slog.String("msg.uuid", msg.UUID))

var socketMsg SocketMessage
if err := msgpack.Unmarshal(msg.Payload, &socketMsg); err != nil {
if err := json.Unmarshal(msg.Payload, &socketMsg); err != nil {
slog.ErrorContext(ctx, "failed to parse socket message", slog.Any("err", err))
msg.Nack()
}
Expand Down
5 changes: 2 additions & 3 deletions daemon/handlers.sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,17 @@ import (
"time"

"github.com/malamtime/cli/model"
"github.com/vmihailenco/msgpack/v5"
)

func handlePubSubSync(ctx context.Context, socketMsgPayload interface{}) error {
pb, err := msgpack.Marshal(socketMsgPayload)
pb, err := json.Marshal(socketMsgPayload)
if err != nil {
slog.Error("Failed to marshal the sync payload again for unmarshal", slog.Any("payload", socketMsgPayload))
return err
}

var syncMsg model.PostTrackArgs
err = msgpack.Unmarshal(pb, &syncMsg)
err = json.Unmarshal(pb, &syncMsg)
Comment on lines +13 to +20
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This function marshals socketMsgPayload to JSON and then immediately unmarshals it back into syncMsg. This is an inefficient pattern involving an unnecessary serialization and deserialization cycle. Since socketMsgPayload is already a map[string]interface{} in memory after the initial JSON decoding, this double-conversion should be avoided.

A better approach would be to refactor the data handling. For instance, you could change SocketMessage.Payload in daemon/socket.go to be of type json.RawMessage. This would allow you to unmarshal the payload directly into the target model.PostTrackArgs struct in the handler, eliminating the intermediate marshaling step and improving performance.

if err != nil {
slog.Error("Failed to parse sync payload", slog.Any("payload", socketMsgPayload))
return err
Expand Down
12 changes: 6 additions & 6 deletions daemon/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
package daemon

import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
Expand All @@ -12,7 +13,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/suite"
"github.com/vmihailenco/msgpack/v5"
)

type handlersTestSuite struct {
Expand Down Expand Up @@ -49,7 +49,7 @@ func (s *handlersTestSuite) TestSocketTopicProcessorValidSync() {
},
},
}
payload, err := msgpack.Marshal(socketMsg)
payload, err := json.Marshal(socketMsg)
assert.NoError(s.T(), err)

msg := message.NewMessage("test-uuid", payload)
Expand Down Expand Up @@ -91,7 +91,7 @@ func (s *handlersTestSuite) TestSocketTopicProcessorNonSync() {
},
},
}
payload, err := msgpack.Marshal(socketMsg)
payload, err := json.Marshal(socketMsg)
assert.NoError(s.T(), err)

msg := message.NewMessage("test-uuid", payload)
Expand All @@ -112,7 +112,7 @@ func (s *handlersTestSuite) TestSocketTopicProcessorInvalidPayload() {
Type: "sync",
Payload: []byte(`invalid json`),
}
payload, err := msgpack.Marshal(socketMsg)
payload, err := json.Marshal(socketMsg)
assert.NoError(s.T(), err)

msg := message.NewMessage("test-uuid", payload)
Expand Down Expand Up @@ -140,7 +140,7 @@ func (s *handlersTestSuite) TestSocketTopicProcessorMultipleMessages() {
},
},
}
payload1, err := msgpack.Marshal(socketMsg1)
payload1, err := json.Marshal(socketMsg1)
assert.NoError(s.T(), err)

socketMsg2 := SocketMessage{
Expand All @@ -154,7 +154,7 @@ func (s *handlersTestSuite) TestSocketTopicProcessorMultipleMessages() {
},
},
}
payload2, err := msgpack.Marshal(socketMsg2)
payload2, err := json.Marshal(socketMsg2)
assert.NoError(s.T(), err)

msg1 := message.NewMessage("test-uuid-1", payload1)
Expand Down
10 changes: 5 additions & 5 deletions daemon/socket.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package daemon

import (
"encoding/json"
"log/slog"
"net"
"os"

"github.com/ThreeDotsLabs/watermill"
"github.com/ThreeDotsLabs/watermill/message"
"github.com/vmihailenco/msgpack/v5"
)

type SocketMessageType string
Expand All @@ -17,9 +17,9 @@ const (
)

type SocketMessage struct {
Type SocketMessageType `msgpack:"type"`
Type SocketMessageType `json:"type"`
// if parse from buffer, it will be the map[any]any
Payload interface{} `msgpack:"payload"`
Payload interface{} `json:"payload"`
}

type SocketHandler struct {
Expand Down Expand Up @@ -89,7 +89,7 @@ func (p *SocketHandler) acceptConnections() {

func (p *SocketHandler) handleConnection(conn net.Conn) {
defer conn.Close()
decoder := msgpack.NewDecoder(conn)
decoder := json.NewDecoder(conn)
var msg SocketMessage
if err := decoder.Decode(&msg); err != nil {
slog.Error("Error decoding message", slog.Any("err", err))
Expand All @@ -102,7 +102,7 @@ func (p *SocketHandler) handleConnection(conn net.Conn) {
// case "track":
// p.handleTrack(conn, msg.Payload)
case SocketMessageTypeSync:
buf, err := msgpack.Marshal(msg)
buf, err := json.Marshal(msg)
if err != nil {
slog.Error("Error encoding message", slog.Any("err", err))
}
Expand Down
3 changes: 0 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@ require (
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.10.0
github.com/ugorji/go/codec v1.3.0
github.com/uptrace/uptrace-go v1.37.0
github.com/urfave/cli/v2 v2.27.7
github.com/vmihailenco/msgpack/v5 v5.4.1
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0
go.opentelemetry.io/otel v1.37.0
go.opentelemetry.io/otel/trace v1.37.0
Expand Down Expand Up @@ -66,7 +64,6 @@ require (
github.com/sergi/go-diff v1.4.0 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect
Expand Down
16 changes: 8 additions & 8 deletions model/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ type Alias struct {
}

type importShellAliasRequest struct {
Aliases []string `json:"aliases" msgpack:"aliases"`
IsFullRefresh bool `json:"isFullRefresh" msgpack:"isFullRefresh"`
ShellType string `json:"shellType" msgpack:"shellType"`
FileLocation string `json:"fileLocation" msgpack:"fileLocation"`
Aliases []string `json:"aliases"`
IsFullRefresh bool `json:"isFullRefresh"`
ShellType string `json:"shellType"`
FileLocation string `json:"fileLocation"`

Hostname string `json:"hostname" msgpack:"hostname"`
Username string `json:"username" msgpack:"username"`
OS string `json:"os" msgpack:"os"`
OSVersion string `json:"osVersion" msgpack:"osVersion"`
Hostname string `json:"hostname"`
Username string `json:"username"`
OS string `json:"os"`
OSVersion string `json:"osVersion"`
}

type importShellAliasResponse struct {
Expand Down
96 changes: 1 addition & 95 deletions model/api.base.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@ import (
"fmt"
"io"
"net/http"
"strings"
"time"

"github.com/sirupsen/logrus"
"github.com/vmihailenco/msgpack/v5"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)

Expand All @@ -24,102 +22,10 @@ type HTTPRequestOptions[T any, R any] struct {
Path string
Payload T
Response *R
ContentType string // Optional, defaults to "application/msgpack"
ContentType string // Optional, defaults to "application/json"
Timeout time.Duration // Optional, defaults to 10 seconds
}

// SendHTTPRequest is a legacy HTTP request function that uses msgpack encoding
// Deprecated: Use SendHTTPRequestJSON for new implementations
func SendHTTPRequest[T any, R any](opts HTTPRequestOptions[T, R]) error {
ctx, span := modelTracer.Start(opts.Context, "http.send")
defer span.End()

jsonData, err := msgpack.Marshal(opts.Payload)
if err != nil {
logrus.Errorln(err)
return err
}

timeout := time.Second * 10
if opts.Timeout > 0 {
timeout = opts.Timeout
}

client := &http.Client{
Timeout: timeout,
Transport: otelhttp.NewTransport(http.DefaultTransport),
}

req, err := http.NewRequestWithContext(ctx, opts.Method, opts.Endpoint.APIEndpoint+opts.Path, bytes.NewBuffer(jsonData))
if err != nil {
logrus.Errorln(err)
return err
}

contentType := "application/msgpack"
if opts.ContentType != "" {
contentType = opts.ContentType
}

req.Header.Set("Content-Type", contentType)
req.Header.Set("User-Agent", fmt.Sprintf("shelltimeCLI@%s", commitID))
req.Header.Set("Authorization", "CLI "+opts.Endpoint.Token)

logrus.Traceln("http: ", req.URL.String())

resp, err := client.Do(req)
if err != nil {
logrus.Errorln(err)
return err
}
defer resp.Body.Close()

logrus.Traceln("http: ", resp.Status)

if resp.StatusCode == http.StatusNoContent {
return nil
}

buf, err := io.ReadAll(resp.Body)
if err != nil {
logrus.Errorln(err)
return err
}

if resp.StatusCode != http.StatusOK {
var msg errorResponse
err = json.Unmarshal(buf, &msg)
if err != nil {
logrus.Errorln("Failed to parse error response:", err)
return fmt.Errorf("HTTP error: %d", resp.StatusCode)
}
logrus.Errorln("Error response:", msg.ErrorMessage)
return errors.New(msg.ErrorMessage)
}

// Only try to unmarshal if we have a response struct
if opts.Response != nil {
contentType := resp.Header.Get("Content-Type")
if strings.Contains(contentType, "json") {
err = json.Unmarshal(buf, opts.Response)
if err != nil {
logrus.Errorln("Failed to unmarshal JSON response:", err)
return err
}
return nil
}
if strings.Contains(contentType, "msgpack") {
err = msgpack.Unmarshal(buf, opts.Response)
if err != nil {
logrus.Errorln("Failed to unmarshal response:", err)
return err
}
}
}

return nil
}

// SendHTTPRequestJSON is a generic HTTP request function that sends JSON data and unmarshals the response
func SendHTTPRequestJSON[T any, R any](opts HTTPRequestOptions[T, R]) error {
ctx, span := modelTracer.Start(opts.Context, "http.send.json")
Expand Down
38 changes: 19 additions & 19 deletions model/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,37 @@ type errorResponse struct {
}

type TrackingData struct {
SessionID int64 `json:"sessionId" msgpack:"sessionId"`
Command string `json:"command" msgpack:"command"`
StartTime int64 `json:"startTime" msgpack:"startTime"`
EndTime int64 `json:"endTime" msgpack:"endTime"`
StartTimeNano int64 `json:"startTimeNano" msgpack:"startTimeNano"`
EndTimeNano int64 `json:"endTimeNano" msgpack:"endTimeNano"`
Result int `json:"result" msgpack:"result"`
SessionID int64 `json:"sessionId"`
Command string `json:"command"`
StartTime int64 `json:"startTime"`
EndTime int64 `json:"endTime"`
StartTimeNano int64 `json:"startTimeNano"`
EndTimeNano int64 `json:"endTimeNano"`
Result int `json:"result"`
}

type TrackingMetaData struct {
Hostname string `json:"hostname" msgpack:"hostname"`
Username string `json:"username" msgpack:"username"`
OS string `json:"os" msgpack:"os"`
OSVersion string `json:"osVersion" msgpack:"osVersion"`
Shell string `json:"shell" msgpack:"shell"`
Hostname string `json:"hostname"`
Username string `json:"username"`
OS string `json:"os"`
OSVersion string `json:"osVersion"`
Shell string `json:"shell"`

// 0: cli, 1: daemon
Source int `json:"source" msgpack:"source"`
Source int `json:"source"`
}

type PostTrackArgs struct {
// nano timestamp
CursorID int64 `json:"cursorId" msgpack:"cursorId"`
Data []TrackingData `json:"data" msgpack:"data"`
Meta TrackingMetaData `json:"meta" msgpack:"meta"`
CursorID int64 `json:"cursorId"`
Data []TrackingData `json:"data"`
Meta TrackingMetaData `json:"meta"`

Encrypted string `json:"encrypted" msgpack:"encrypted"`
Encrypted string `json:"encrypted"`
// a base64 encoded AES-GCM key that encrypted by PublicKey from open token
AesKey string `json:"aesKey" msgpack:"aesKey"`
AesKey string `json:"aesKey"`
// the AES-GCM nonce. not encrypted
Nonce string `json:"nonce" msgpack:"nonce"`
Nonce string `json:"nonce"`
}

func doSendData(ctx context.Context, endpoint Endpoint, data PostTrackArgs) error {
Expand Down
Loading
Loading