Skip to content
Open
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,4 @@ return totalCount
- [streamdeck-sdk-go](https://github.com/tystuyfzand/streamdeck-sdk-go) - StreamDeck SDK for Go
- [streamdeck-easypi](https://github.com/BarRaider/streamdeck-easypi) - EasyPI for StreamDeck
- [gopher-lua](https://github.com/yuin/gopher-lua) - Lua VM in Go
- [gjson](https://github.com/tidwall/gjson) - JSON parser for Go
- [gjson](https://github.com/tidwall/gjson) - JSON parser for Go
9 changes: 8 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,13 @@ func main() {
return
}

if err = manager.StartAsync(event.Context); err != nil {
if err = manager.StartAsync(event.Context, true); err != nil {
lg.Err(err).Send()
return
}
})
sdk.AddHandler(func(event *sdk.WillDisappearEvent) {
defer recoverPanic()
if event.Payload == nil {
return
}
Expand All @@ -84,13 +85,19 @@ func main() {
})

sdk.AddHandler(func(event *sdk.ReceiveSettingsEvent) {
defer recoverPanic()
if err := manager.SetInstanceConfig(event.Context, event.Settings); err != nil {
lg.Err(err).Send()
return
}
if err := manager.StartAsync(event.Context, false); err != nil {
lg.Err(err).Send()
return
}
})

sdk.AddHandler(func(event *sdk.KeyDownEvent) {
defer recoverPanic()
if err := manager.KeyPressed(event.Context); err != nil {
lg.Err(err).Send()
return
Expand Down
1 change: 1 addition & 0 deletions pkg/common/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type Config struct {
TitlePrefix string `json:"titlePrefix"`
BodyScript string `json:"bodyScript"`
ShowSuccessNotification bool `json:"showSuccessNotification"`
InsecureSkipVerify bool `json:"insecureSkipVerify"`
MethodType string `json:"methodType"`
Body string `json:"body"`
}
7 changes: 6 additions & 1 deletion pkg/executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package executor

import (
"context"
"crypto/tls"
"strings"

"github.com/cockroachdb/errors"
Expand All @@ -28,7 +29,11 @@ func (e *Executor) Execute(
ctx context.Context,
executeReq ExecuteRequest,
) (*ExecuteResponse, error) {
httpReq := req.C().NewRequest()
client := req.C()
if executeReq.Config.InsecureSkipVerify {
client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
}
httpReq := client.NewRequest()
Comment on lines +32 to +36
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Creating a new HTTP client with req.C() for every Execute call is inefficient. http.Client (which req.Client wraps) is designed for reuse. Frequent creation can lead to resource issues like socket exhaustion (TIME_WAIT state).

To improve this, you could create and manage req.Client instances within the Executor struct. For example, you could have one client for secure connections and another for insecure connections, and choose the appropriate one here based on executeReq.Config.InsecureSkipVerify.

httpReq = httpReq.SetContext(ctx)
httpReq.Method = executeReq.Config.MethodType

Expand Down
74 changes: 63 additions & 11 deletions pkg/instance/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func (i *DefaultInstance) ShowOk() {
i.sdk.ShowOk(i.ctxID)
}

func (i *DefaultInstance) StartAsync() {
func (i *DefaultInstance) StartAsync(immediate bool) {
i.mut.Lock()
defer i.mut.Unlock()

Expand All @@ -87,18 +87,18 @@ func (i *DefaultInstance) StartAsync() {
i.ctx = ctx
i.ctxCancel = cancel

go i.run()
go i.run(immediate)
}

func (i *DefaultInstance) run() {
ctx := i.ctx

for ctx.Err() == nil {
interval := 30
if i.cfg.IntervalSeconds > 0 {
interval = i.cfg.IntervalSeconds
func (i *DefaultInstance) run(immediate bool) {
defer func() {
if rec := recover(); rec != nil {
log.Error().Msgf("panic in run: %v", rec)
}
}()
ctx := i.ctx

if immediate {
newLogger := log.With().
Str("id", uuid.NewString()).
Str("ctxID", i.ctxID).
Expand All @@ -109,14 +109,43 @@ func (i *DefaultInstance) run() {

i.ExecuteSingleRequest(innerCtx)
innerCancel()
}

time.Sleep(time.Duration(interval) * time.Second)
if i.cfg == nil || i.cfg.IntervalSeconds <= 0 {
return
}

ticker := time.NewTicker(time.Duration(i.cfg.IntervalSeconds) * time.Second)
defer ticker.Stop()

for {
select {
case <-ctx.Done():
return
case <-ticker.C:
newLogger := log.With().
Str("id", uuid.NewString()).
Str("ctxID", i.ctxID).
Logger()

innerCtx, innerCancel := context.WithCancel(ctx)
innerCtx = newLogger.WithContext(innerCtx)

i.ExecuteSingleRequest(innerCtx)
innerCancel()
}
}
}

func (i *DefaultInstance) ExecuteSingleRequest(
ctx context.Context,
) {
if i.cfg == nil {
zerolog.Ctx(ctx).Error().Msg("config is nil, cannot execute request")
i.ShowAlert()
return
}

resp, err := i.executor.Execute(ctx, executor.ExecuteRequest{
Config: *i.cfg,
})
Expand Down Expand Up @@ -221,9 +250,32 @@ func (i *DefaultInstance) stopWithoutLock() {
}

func (i *DefaultInstance) KeyPressed() error {
i.mut.Lock()
defer i.mut.Unlock()

if i.cfg == nil {
i.ShowAlert()
return errors.New("instance config is nil")
}

targetUrl := i.cfg.BrowserUrl
if targetUrl == "" {
targetUrl = i.cfg.ApiUrl
ctx := i.ctx
if ctx == nil {
ctx = context.Background()
}

newLogger := log.With().
Str("id", uuid.NewString()).
Str("ctxID", i.ctxID).
Logger()

innerCtx, innerCancel := context.WithCancel(ctx)
defer innerCancel()
innerCtx = newLogger.WithContext(innerCtx)

i.ExecuteSingleRequest(innerCtx)
return nil
}

targetUrl, err := utils.ExecuteTemplate(targetUrl, i.cfg.TemplateParameters)
Expand Down
2 changes: 1 addition & 1 deletion pkg/instance/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type Factory interface {

type Instance interface {
SetConfig(payload *fastjson.Value) error
StartAsync()
StartAsync(immediate bool)
Stop()
KeyPressed() error
}
4 changes: 2 additions & 2 deletions pkg/instance/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (m *Manager) InitInstance(ctxId string) (Instance, error) {
return instance, nil
}

func (m *Manager) StartAsync(ctxId string) error {
func (m *Manager) StartAsync(ctxId string, immediate bool) error {
m.mut.Lock()
defer m.mut.Unlock()
instance, ok := m.instances[ctxId]
Expand All @@ -45,7 +45,7 @@ func (m *Manager) StartAsync(ctxId string) error {
return errors.New("instance not found")
}

instance.StartAsync()
instance.StartAsync(immediate)

return nil
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/instance/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ func TestNewManager(t *testing.T) {
mockInstance := NewMockInstance(gomock.NewController(t))
factory.EXPECT().Create(ctxID).Return(mockInstance)

mockInstance.EXPECT().StartAsync()
mockInstance.EXPECT().StartAsync(gomock.Any())
mockInstance.EXPECT().Stop()
mockInstance.EXPECT().KeyPressed()

_, err := mgr.InitInstance(ctxID)
assert.Nil(t, err)

assert.NoError(t, mgr.StartAsync(ctxID))
assert.NoError(t, mgr.StartAsync(ctxID, true))
assert.NoError(t, mgr.KeyPressed(ctxID))

js := &fastjson.Value{}
Expand All @@ -43,7 +43,7 @@ func TestNotExist(t *testing.T) {

ctxID := "1231231"

assert.ErrorContains(t, mgr.StartAsync(ctxID), "instance not found")
assert.ErrorContains(t, mgr.StartAsync(ctxID, true), "instance not found")
assert.ErrorContains(t, mgr.KeyPressed(ctxID), "instance not found")
assert.ErrorContains(t, mgr.SetInstanceConfig(ctxID, nil), "instance not found")
assert.NoError(t, mgr.Stop(ctxID))
Expand Down
12 changes: 9 additions & 3 deletions resources/pi/pi.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
</div>
<div class="sdpi-item">
<div class="sdpi-item-label">Interval (Sec)</div>
<input class="sdpi-item-value sdProperty" type="number" v-model="config.intervalSeconds">
<input class="sdpi-item-value sdProperty" type="number" v-model="config.intervalSeconds" placeholder="Call this endpoint every x seconds. 0 to disable">
</div>
<div class="sdpi-item">
<div class="sdpi-item-label">Indicator</div>
Expand All @@ -66,8 +66,14 @@
<label for="showSuccessNotification" class="sdpi-item-label"><span></span>Show Success</label>
</div>
</div>
<div class="sdpi-item-value">
<input class="sdProperty sdCheckbox" type="checkbox" v-model="config.showSuccessNotification">
</div>
<div class="sdpi-item">
<div class="sdpi-item-label">Invalid certs</div>
<div type="checkbox" class="sdpi-item">
<div class="sdpi-item-value">
<input id="insecureSkipVerify" class="sdProperty sdCheckbox" type="checkbox" v-model="config.insecureSkipVerify">
<label for="insecureSkipVerify" class="sdpi-item-label"><span></span>Ignore</label>
</div>
</div>
</div>
<hr>
Expand Down