mirror of
https://github.com/sigmasternchen/iot-relay
synced 2025-03-15 06:28:55 +00:00
initial commit
This commit is contained in:
commit
6257896d1a
12 changed files with 307 additions and 0 deletions
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
9
.idea/iot-relay.iml
Normal file
9
.idea/iot-relay.iml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="Go" enabled="true" />
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
24
cmd/main.go
Normal file
24
cmd/main.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
. "iot-relay/internal/client"
|
||||||
|
. "iot-relay/internal/config"
|
||||||
|
. "iot-relay/internal/server"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const configFile = "config.json"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.Println("reading config file")
|
||||||
|
config, err := ReadConfig(configFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("starting server")
|
||||||
|
err = Listen(config, GetHandler(config))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
}
|
12
config.json
Normal file
12
config.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"server": {
|
||||||
|
"bind": "0.0.0.0:20159",
|
||||||
|
"timeout": 30
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"address": "http://localhost:8086",
|
||||||
|
"db": "grafana",
|
||||||
|
"measurement": "iot",
|
||||||
|
"host": "iot-relay"
|
||||||
|
}
|
||||||
|
}
|
3
go.mod
Normal file
3
go.mod
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module iot-relay
|
||||||
|
|
||||||
|
go 1.17
|
58
internal/client/client.go
Normal file
58
internal/client/client.go
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"iot-relay/internal/config"
|
||||||
|
"iot-relay/internal/types"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func lineProtocol(request types.Request, config config.Config) string {
|
||||||
|
var builder strings.Builder
|
||||||
|
builder.WriteString(config.Client.Measurement)
|
||||||
|
builder.WriteString(",host=")
|
||||||
|
builder.WriteString(config.Client.Host)
|
||||||
|
builder.WriteString(",ip=")
|
||||||
|
builder.WriteString(request.IP)
|
||||||
|
builder.WriteString(",id=")
|
||||||
|
builder.WriteString(request.ID)
|
||||||
|
builder.WriteString(",loc=")
|
||||||
|
builder.WriteString(request.Location)
|
||||||
|
builder.WriteString(" ")
|
||||||
|
|
||||||
|
for key, value := range request.Data {
|
||||||
|
builder.WriteString(key)
|
||||||
|
builder.WriteString("=")
|
||||||
|
builder.WriteString(value)
|
||||||
|
builder.WriteString(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString(strconv.FormatInt(time.Now().UnixNano(), 10))
|
||||||
|
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetHandler(config config.Config) types.Callback {
|
||||||
|
return func(request types.Request) error {
|
||||||
|
line := lineProtocol(request, config)
|
||||||
|
|
||||||
|
url := config.Client.Address + "/write?db=" + config.Client.DB
|
||||||
|
|
||||||
|
resp, err := http.Post(url, "application/octet-stream", strings.NewReader(line))
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != 204 {
|
||||||
|
err = fmt.Errorf("influxdb responded with %d", resp.StatusCode)
|
||||||
|
log.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
41
internal/config/config.go
Normal file
41
internal/config/config.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Server struct {
|
||||||
|
Bind string `json:"bind"`
|
||||||
|
Timeout int `json:"timeout"` // in seconds
|
||||||
|
} `json:"server"`
|
||||||
|
Client struct {
|
||||||
|
Address string `json:"address"`
|
||||||
|
DB string `json:"db"`
|
||||||
|
Measurement string `json:"measurement"`
|
||||||
|
Host string `json:"host"`
|
||||||
|
} `json:"client"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReadConfig(filename string) (Config, error) {
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return Config{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := ioutil.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
return Config{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var config Config
|
||||||
|
|
||||||
|
err = json.Unmarshal(content, &config)
|
||||||
|
if err != nil {
|
||||||
|
return Config{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
57
internal/server/connection.go
Normal file
57
internal/server/connection.go
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"iot-relay/internal/types"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func connectionHandler(conn net.Conn) {
|
||||||
|
reader := bufio.NewReader(conn)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
log.Println("closing connection")
|
||||||
|
_ = conn.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
var request types.Request
|
||||||
|
request.Data = make(map[string]string)
|
||||||
|
request.IP = strings.Split(conn.RemoteAddr().String(), ":")[0]
|
||||||
|
|
||||||
|
for {
|
||||||
|
line, err := reader.ReadString('\n')
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("connection error: %v", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(line) == 1 {
|
||||||
|
// end of request
|
||||||
|
|
||||||
|
if !request.IsValid() {
|
||||||
|
log.Println("protocol error")
|
||||||
|
_, _ = conn.Write([]byte("bad\n"))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
err = callback(request)
|
||||||
|
if err != nil {
|
||||||
|
// should be logged by callback
|
||||||
|
_, _ = conn.Write([]byte("fail\n"))
|
||||||
|
} else {
|
||||||
|
_, _ = conn.Write([]byte("ok\n"))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
err = parseLine(line, &request)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("parsing error: %v", err)
|
||||||
|
_, _ = conn.Write([]byte("bad\n"))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
internal/server/parser.go
Normal file
38
internal/server/parser.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"iot-relay/internal/types"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var forbiddenKeys = []string{
|
||||||
|
"ip",
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLine(line string, request *types.Request) error {
|
||||||
|
token := strings.Split(line, "=")
|
||||||
|
if len(token) != 2 {
|
||||||
|
return errors.New("malformed; missing =")
|
||||||
|
}
|
||||||
|
|
||||||
|
key := strings.ToLower(token[0])
|
||||||
|
value := strings.TrimRight(token[1], "\n")
|
||||||
|
|
||||||
|
for _, forbidden := range forbiddenKeys {
|
||||||
|
if forbidden == key {
|
||||||
|
return errors.New("forbidden key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch key {
|
||||||
|
case "id":
|
||||||
|
request.ID = value
|
||||||
|
case "loc":
|
||||||
|
request.Location = value
|
||||||
|
default:
|
||||||
|
request.Data[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
32
internal/server/server.go
Normal file
32
internal/server/server.go
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"iot-relay/internal/config"
|
||||||
|
"iot-relay/internal/types"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var callback types.Callback
|
||||||
|
|
||||||
|
func Listen(config config.Config, _callback types.Callback) error {
|
||||||
|
callback = _callback
|
||||||
|
|
||||||
|
listener, err := net.Listen("tcp", config.Server.Bind)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
connection, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
} else {
|
||||||
|
log.Printf("connection from %v", connection.RemoteAddr())
|
||||||
|
|
||||||
|
_ = connection.SetReadDeadline(time.Now().Add(time.Second * time.Duration(config.Server.Timeout)))
|
||||||
|
go connectionHandler(connection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
3
internal/types/callback.go
Normal file
3
internal/types/callback.go
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
type Callback func(request Request) error
|
22
internal/types/request.go
Normal file
22
internal/types/request.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
ID string
|
||||||
|
Location string
|
||||||
|
IP string
|
||||||
|
Data map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Request) IsValid() bool {
|
||||||
|
if len(r.IP) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(r.ID) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(r.Location) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
Loading…
Reference in a new issue