mirror of
https://github.com/jwetzell/showbridge-go.git
synced 2026-04-27 13:25:40 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba2fead834 | ||
|
|
cb7504922e | ||
|
|
59d9405781 | ||
|
|
fbda348b58 | ||
|
|
c1a98483a4 | ||
|
|
cd567e5b97 | ||
|
|
d8d53f01d2 | ||
|
|
f363fbf0a6 | ||
|
|
d629146592 | ||
|
|
b06ced2631 | ||
|
|
a33fe88757 | ||
|
|
ce673e31db | ||
|
|
38857f7a29 | ||
|
|
45965a4eac | ||
|
|
b372b53422 | ||
|
|
97cf721abc |
2
.github/FUNDING.yml
vendored
Normal file
2
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# These are supported funding model platforms
|
||||
github: [jwetzell]
|
||||
2
.github/workflows/release-showbridge.yaml
vendored
2
.github/workflows/release-showbridge.yaml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: setup go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
- name: release
|
||||
|
||||
29
README.md
Normal file
29
README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
<div align="center">
|
||||
|
||||
# showbridge (go edition)
|
||||
|
||||
Simple protocol router _/s_
|
||||
|
||||
</div>
|
||||
|
||||
### Supported Protocols
|
||||
- HTTP
|
||||
- client
|
||||
- server
|
||||
- UDP
|
||||
- client
|
||||
- server
|
||||
- TCP
|
||||
- client
|
||||
- server
|
||||
- [MQTT](https://mqtt.org/)
|
||||
- client
|
||||
- [NATS](https://nats.io/)
|
||||
- client
|
||||
- [PosiStageNet](https://posistage.net/)
|
||||
- client
|
||||
- MIDI
|
||||
- client (not included in pre-built binaries yet)
|
||||
|
||||
|
||||
|
||||
5
go.mod
5
go.mod
@@ -8,6 +8,7 @@ require (
|
||||
github.com/jwetzell/free-d-go v0.1.0
|
||||
github.com/jwetzell/osc-go v0.1.0
|
||||
github.com/jwetzell/psn-go v0.2.1
|
||||
github.com/nats-io/nats.go v1.47.0
|
||||
github.com/urfave/cli/v3 v3.6.1
|
||||
gitlab.com/gomidi/midi/v2 v2.3.16
|
||||
modernc.org/quickjs v0.17.0
|
||||
@@ -18,10 +19,14 @@ require (
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/nats-io/nkeys v0.4.11 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
golang.org/x/crypto v0.42.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
|
||||
golang.org/x/net v0.44.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
|
||||
10
go.sum
10
go.sum
@@ -20,8 +20,16 @@ github.com/jwetzell/osc-go v0.1.0 h1:EXxup5VWBErHot2Ri4MFToPf6KCzLDTbCt2x6GLfw8I
|
||||
github.com/jwetzell/osc-go v0.1.0/go.mod h1:xLz0jTwebSxtx1TkKN1YVdeRqvpFNweDhTut5TE393A=
|
||||
github.com/jwetzell/psn-go v0.2.1 h1:pNG6XNfVRTb4qctH6pJjRJ1ReYGnGgNRA4H7tNbmzRU=
|
||||
github.com/jwetzell/psn-go v0.2.1/go.mod h1:bcEAeti4sQM375buujb3mIfmUstD4Aby18gq3ENb6+o=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/nats-io/nats.go v1.47.0 h1:YQdADw6J/UfGUd2Oy6tn4Hq6YHxCaJrVKayxxFqYrgM=
|
||||
github.com/nats-io/nats.go v1.47.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
||||
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
|
||||
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
@@ -38,6 +46,8 @@ go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
|
||||
go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
|
||||
83
internal/processing/nats-message-create.go
Normal file
83
internal/processing/nats-message-create.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package processing
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type NATSMessage struct {
|
||||
Subject string
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
type NATSMessageCreate struct {
|
||||
config ProcessorConfig
|
||||
Subject string
|
||||
Payload *template.Template
|
||||
}
|
||||
|
||||
func (nmc *NATSMessageCreate) Process(ctx context.Context, payload any) (any, error) {
|
||||
|
||||
var payloadBuffer bytes.Buffer
|
||||
err := nmc.Payload.Execute(&payloadBuffer, payload)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
payloadString := payloadBuffer.String()
|
||||
|
||||
message := NATSMessage{
|
||||
Subject: nmc.Subject,
|
||||
Payload: []byte(payloadString),
|
||||
}
|
||||
|
||||
return message, nil
|
||||
}
|
||||
|
||||
func (nmc *NATSMessageCreate) Type() string {
|
||||
return nmc.config.Type
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterProcessor(ProcessorRegistration{
|
||||
Type: "nats.message.create",
|
||||
New: func(config ProcessorConfig) (Processor, error) {
|
||||
params := config.Params
|
||||
// TODO(jwetzell): support template for subject
|
||||
subject, ok := params["subject"]
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("nats.message.create requires a subject parameter")
|
||||
}
|
||||
|
||||
subjectString, ok := subject.(string)
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("nats.message.create subject must be a string")
|
||||
}
|
||||
|
||||
payload, ok := params["payload"]
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("osc.message.create requires a payload parameter")
|
||||
}
|
||||
|
||||
payloadString, ok := payload.(string)
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("osc.message.create payload must be a string")
|
||||
}
|
||||
|
||||
payloadTemplate, err := template.New("payload").Parse(payloadString)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &NATSMessageCreate{config: config, Subject: subjectString, Payload: payloadTemplate}, nil
|
||||
},
|
||||
})
|
||||
}
|
||||
35
internal/processing/nats-message-encode.go
Normal file
35
internal/processing/nats-message-encode.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package processing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
type NATSMessageEncode struct {
|
||||
config ProcessorConfig
|
||||
}
|
||||
|
||||
func (nme *NATSMessageEncode) Process(ctx context.Context, payload any) (any, error) {
|
||||
payloadMessage, ok := payload.(*nats.Msg)
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("nats.message.encode processor only accepts an nats.Msg")
|
||||
}
|
||||
|
||||
return payloadMessage.Data, nil
|
||||
}
|
||||
|
||||
func (nme *NATSMessageEncode) Type() string {
|
||||
return nme.config.Type
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterProcessor(ProcessorRegistration{
|
||||
Type: "nats.message.encode",
|
||||
New: func(config ProcessorConfig) (Processor, error) {
|
||||
return &NATSMessageEncode{config: config}, nil
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -31,6 +31,10 @@ func (sj *ScriptJS) Process(ctx context.Context, payload any) (any, error) {
|
||||
|
||||
_, err = vm.Eval(sj.Program, quickjs.EvalGlobal)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
output, err := vm.GetProperty(vm.GlobalObject(), payloadAtom)
|
||||
|
||||
if err != nil {
|
||||
|
||||
57
internal/processing/string-create.go
Normal file
57
internal/processing/string-create.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package processing
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type StringCreate struct {
|
||||
config ProcessorConfig
|
||||
Template *template.Template
|
||||
}
|
||||
|
||||
func (sc *StringCreate) Process(ctx context.Context, payload any) (any, error) {
|
||||
var templateBuffer bytes.Buffer
|
||||
err := sc.Template.Execute(&templateBuffer, payload)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
payloadString := templateBuffer.String()
|
||||
|
||||
return payloadString, nil
|
||||
}
|
||||
|
||||
func (sc *StringCreate) Type() string {
|
||||
return sc.config.Type
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterProcessor(ProcessorRegistration{
|
||||
Type: "string.create",
|
||||
New: func(config ProcessorConfig) (Processor, error) {
|
||||
params := config.Params
|
||||
tmpl, ok := params["template"]
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("string.create requires a template parameter")
|
||||
}
|
||||
|
||||
templateString, ok := tmpl.(string)
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("string.create template must be a string")
|
||||
}
|
||||
|
||||
templateTemplate, err := template.New("template").Parse(templateString)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &StringCreate{config: config, Template: templateTemplate}, nil
|
||||
},
|
||||
})
|
||||
}
|
||||
42
internal/processing/string-encode_test.go
Normal file
42
internal/processing/string-encode_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package processing_test
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/jwetzell/showbridge-go/internal/processing"
|
||||
)
|
||||
|
||||
func TestGoodStringEncode(t *testing.T) {
|
||||
stringEncoder := processing.StringEncode{}
|
||||
tests := []struct {
|
||||
processor processing.Processor
|
||||
name string
|
||||
payload any
|
||||
expected []byte
|
||||
}{
|
||||
{
|
||||
processor: &stringEncoder,
|
||||
name: "hello",
|
||||
payload: "hello",
|
||||
expected: []byte{0x68, 0x65, 0x6c, 0x6c, 0x6f},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got, err := test.processor.Process(t.Context(), test.payload)
|
||||
|
||||
gotBytes, ok := got.([]byte)
|
||||
if !ok {
|
||||
t.Errorf("string.encode returned a %T payload: %s", got, got)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("string.encode failed: %s", err)
|
||||
}
|
||||
if !slices.Equal(gotBytes, test.expected) {
|
||||
t.Errorf("string.encode got %s, expected %s", got, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
113
nats-client.go
Normal file
113
nats-client.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package showbridge
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/jwetzell/showbridge-go/internal/processing"
|
||||
"github.com/nats-io/nats.go"
|
||||
)
|
||||
|
||||
type NATSClient struct {
|
||||
config ModuleConfig
|
||||
router *Router
|
||||
URL string
|
||||
Subject string
|
||||
client *nats.Conn
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterModule(ModuleRegistration{
|
||||
Type: "net.nats.client",
|
||||
New: func(config ModuleConfig) (Module, error) {
|
||||
params := config.Params
|
||||
url, ok := params["url"]
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("net.nats.client requires a url parameter")
|
||||
}
|
||||
|
||||
urlString, ok := url.(string)
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("net.nats.client url must be string")
|
||||
}
|
||||
|
||||
subject, ok := params["subject"]
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("net.nats.client requires a subject parameter")
|
||||
}
|
||||
|
||||
subjectString, ok := subject.(string)
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("net.nats.client subject must be string")
|
||||
}
|
||||
|
||||
return &NATSClient{config: config, URL: urlString, Subject: subjectString}, nil
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (nc *NATSClient) Id() string {
|
||||
return nc.config.Id
|
||||
}
|
||||
|
||||
func (nc *NATSClient) Type() string {
|
||||
return nc.config.Type
|
||||
}
|
||||
|
||||
func (nc *NATSClient) RegisterRouter(router *Router) {
|
||||
nc.router = router
|
||||
}
|
||||
|
||||
func (nc *NATSClient) Run() error {
|
||||
client, err := nats.Connect(nc.URL, nats.RetryOnFailedConnect(true))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nc.client = client
|
||||
|
||||
defer client.Drain()
|
||||
defer client.Close()
|
||||
|
||||
sub, err := nc.client.Subscribe(nc.Subject, func(msg *nats.Msg) {
|
||||
if nc.router != nil {
|
||||
nc.router.HandleInput(nc.config.Id, msg)
|
||||
}
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
<-nc.router.Context.Done()
|
||||
slog.Debug("router context done in module", "id", nc.config.Id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (nc *NATSClient) Output(payload any) error {
|
||||
|
||||
payloadMessage, ok := payload.(processing.NATSMessage)
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("net.nats.client is only able to output NATSMessage")
|
||||
}
|
||||
|
||||
if nc.client == nil {
|
||||
return fmt.Errorf("net.nats.client client is not setup")
|
||||
}
|
||||
|
||||
if !nc.client.IsConnected() {
|
||||
return fmt.Errorf("net.nats.client is not connected")
|
||||
}
|
||||
|
||||
err := nc.client.Publish(payloadMessage.Subject, payloadMessage.Payload)
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -11,11 +11,10 @@ import (
|
||||
|
||||
type TCPClient struct {
|
||||
config ModuleConfig
|
||||
Host string
|
||||
Port uint16
|
||||
framer framing.Framer
|
||||
conn net.Conn
|
||||
router *Router
|
||||
Addr *net.TCPAddr
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -46,6 +45,11 @@ func init() {
|
||||
return nil, fmt.Errorf("net.tcp.client port must be uint16")
|
||||
}
|
||||
|
||||
addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", hostString, uint16(portNum)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
framingMethod, ok := params["framing"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("net.tcp.client requires a framing method")
|
||||
@@ -72,7 +76,7 @@ func init() {
|
||||
return nil, fmt.Errorf("unknown framing method: %s", framingMethodString)
|
||||
}
|
||||
|
||||
return &TCPClient{framer: framer, Host: hostString, Port: uint16(portNum), config: config}, nil
|
||||
return &TCPClient{framer: framer, Addr: addr, config: config}, nil
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -90,10 +94,6 @@ func (tc *TCPClient) RegisterRouter(router *Router) {
|
||||
}
|
||||
|
||||
func (tc *TCPClient) Run() error {
|
||||
addr, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", tc.Host, tc.Port))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO(jwetzell): shutdown with router.Context properly
|
||||
go func() {
|
||||
@@ -105,7 +105,7 @@ func (tc *TCPClient) Run() error {
|
||||
}()
|
||||
|
||||
for {
|
||||
client, err := net.DialTCP("tcp", nil, addr)
|
||||
err := tc.SetupConn()
|
||||
if err != nil {
|
||||
if tc.router.Context.Err() != nil {
|
||||
slog.Debug("router context done in module", "id", tc.config.Id)
|
||||
@@ -116,8 +116,6 @@ func (tc *TCPClient) Run() error {
|
||||
continue
|
||||
}
|
||||
|
||||
tc.conn = client
|
||||
|
||||
buffer := make([]byte, 1024)
|
||||
select {
|
||||
case <-tc.router.Context.Done():
|
||||
@@ -131,7 +129,7 @@ func (tc *TCPClient) Run() error {
|
||||
slog.Debug("router context done in module", "id", tc.config.Id)
|
||||
return nil
|
||||
default:
|
||||
byteCount, err := client.Read(buffer)
|
||||
byteCount, err := tc.conn.Read(buffer)
|
||||
|
||||
if err != nil {
|
||||
tc.framer.Clear()
|
||||
@@ -151,22 +149,29 @@ func (tc *TCPClient) Run() error {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (tc *TCPClient) SetupConn() error {
|
||||
client, err := net.DialTCP("tcp", nil, tc.Addr)
|
||||
tc.conn = client
|
||||
return err
|
||||
}
|
||||
|
||||
func (tc *TCPClient) Output(payload any) error {
|
||||
if tc.conn != nil {
|
||||
payloadBytes, ok := payload.([]byte)
|
||||
if !ok {
|
||||
return fmt.Errorf("net.tcp.client is only able to output bytes")
|
||||
// NOTE(jwetzell): not sure how this would occur but
|
||||
if tc.conn == nil {
|
||||
err := tc.SetupConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := tc.conn.Write(tc.framer.Encode(payloadBytes))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
payloadBytes, ok := payload.([]byte)
|
||||
if !ok {
|
||||
return fmt.Errorf("net.tcp.client is only able to output bytes")
|
||||
}
|
||||
_, err := tc.conn.Write(tc.framer.Encode(payloadBytes))
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
type TCPServer struct {
|
||||
config ModuleConfig
|
||||
Ip string
|
||||
Port uint16
|
||||
framingMethod string
|
||||
router *Router
|
||||
@@ -46,7 +47,20 @@ func init() {
|
||||
return nil, fmt.Errorf("tcp framing method must be a string")
|
||||
}
|
||||
|
||||
return &TCPServer{framingMethod: framingMethodString, Port: uint16(portNum), config: config, quit: make(chan interface{})}, nil
|
||||
ipString := "0.0.0.0"
|
||||
|
||||
ip, ok := params["ip"]
|
||||
if ok {
|
||||
|
||||
specificIpString, ok := ip.(string)
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("tcp ip method must be a string")
|
||||
}
|
||||
ipString = specificIpString
|
||||
}
|
||||
|
||||
return &TCPServer{framingMethod: framingMethodString, Port: uint16(portNum), Ip: ipString, config: config, quit: make(chan interface{})}, nil
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -116,7 +130,7 @@ ClientRead:
|
||||
}
|
||||
|
||||
func (ts *TCPServer) Run() error {
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", ts.Port))
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", ts.Ip, ts.Port))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
)
|
||||
|
||||
type UDPServer struct {
|
||||
Ip string
|
||||
Port uint16
|
||||
config ModuleConfig
|
||||
router *Router
|
||||
@@ -30,7 +31,20 @@ func init() {
|
||||
return nil, fmt.Errorf("net.udp.server port must be uint16")
|
||||
}
|
||||
|
||||
return &UDPServer{Port: uint16(portNum), config: config}, nil
|
||||
ipString := "0.0.0.0"
|
||||
|
||||
ip, ok := params["ip"]
|
||||
if ok {
|
||||
|
||||
specificIpString, ok := ip.(string)
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("tcp ip method must be a string")
|
||||
}
|
||||
ipString = specificIpString
|
||||
}
|
||||
|
||||
return &UDPServer{Ip: ipString, Port: uint16(portNum), config: config}, nil
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -49,7 +63,7 @@ func (us *UDPServer) RegisterRouter(router *Router) {
|
||||
|
||||
func (us *UDPServer) Run() error {
|
||||
|
||||
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf(":%d", us.Port))
|
||||
addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", us.Ip, us.Port))
|
||||
if err != nil {
|
||||
log.Fatalf("error resolving UDP address: %v", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user