Invalid AWN Request Fixer
This commit is contained in:
parent
3d3492a283
commit
cd04beeec6
2
go.mod
2
go.mod
@ -3,7 +3,7 @@ module gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter
|
|||||||
go 1.23.4
|
go 1.23.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
gitea.libretechconsulting.com/rmcguire/go-app v0.4.2
|
gitea.libretechconsulting.com/rmcguire/go-app v0.5.0
|
||||||
github.com/go-resty/resty/v2 v2.16.5
|
github.com/go-resty/resty/v2 v2.16.5
|
||||||
github.com/gorilla/schema v1.4.1
|
github.com/gorilla/schema v1.4.1
|
||||||
github.com/rs/zerolog v1.33.0
|
github.com/rs/zerolog v1.33.0
|
||||||
|
2
go.sum
2
go.sum
@ -4,6 +4,8 @@ gitea.libretechconsulting.com/rmcguire/go-app v0.4.1 h1:gjDg2M/j1AdMCtkXqQnLCo6j
|
|||||||
gitea.libretechconsulting.com/rmcguire/go-app v0.4.1/go.mod h1:9c71S+sJb2NqvOwt3CFsW5WjE895goiRlMTdLimgwHs=
|
gitea.libretechconsulting.com/rmcguire/go-app v0.4.1/go.mod h1:9c71S+sJb2NqvOwt3CFsW5WjE895goiRlMTdLimgwHs=
|
||||||
gitea.libretechconsulting.com/rmcguire/go-app v0.4.2 h1:LQxVLXEHruY32GaMsS5K/tMdjS5kvw6reUh25gshn40=
|
gitea.libretechconsulting.com/rmcguire/go-app v0.4.2 h1:LQxVLXEHruY32GaMsS5K/tMdjS5kvw6reUh25gshn40=
|
||||||
gitea.libretechconsulting.com/rmcguire/go-app v0.4.2/go.mod h1:9c71S+sJb2NqvOwt3CFsW5WjE895goiRlMTdLimgwHs=
|
gitea.libretechconsulting.com/rmcguire/go-app v0.4.2/go.mod h1:9c71S+sJb2NqvOwt3CFsW5WjE895goiRlMTdLimgwHs=
|
||||||
|
gitea.libretechconsulting.com/rmcguire/go-app v0.5.0 h1:5yYyaXXN5KcxMIPBYLZKztvKGMlYol3+oqzUnkvHBaQ=
|
||||||
|
gitea.libretechconsulting.com/rmcguire/go-app v0.5.0/go.mod h1:9c71S+sJb2NqvOwt3CFsW5WjE895goiRlMTdLimgwHs=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA=
|
github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA=
|
||||||
|
3
main.go
3
main.go
@ -10,6 +10,7 @@ import (
|
|||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/ambient"
|
"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/ambient"
|
||||||
|
"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/ambient/ambienthttp"
|
||||||
"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/ambient/config"
|
"gitea.libretechconsulting.com/rmcguire/ambient-weather-local-exporter/pkg/ambient/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -46,6 +47,8 @@ func main() {
|
|||||||
HandlerFunc: aw.GetAWNHandlerFunc(ctx),
|
HandlerFunc: aw.GetAWNHandlerFunc(ctx),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
CustomListener: ambienthttp.NewLFStrippingListener(ctx,
|
||||||
|
awConfig.HTTP.Listen), // Necessary to fix certain bad AWN firmware
|
||||||
HealthChecks: []srv.HealthCheckFunc{
|
HealthChecks: []srv.HealthCheckFunc{
|
||||||
// TODO: Implement
|
// TODO: Implement
|
||||||
func(ctx context.Context) error {
|
func(ctx context.Context) error {
|
||||||
|
101
pkg/ambient/ambienthttp/ambienthttp.go
Normal file
101
pkg/ambient/ambienthttp/ambienthttp.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// This package exists purely to override the net.Listener used
|
||||||
|
// by the application's http server. This is necessary for certain versions
|
||||||
|
// of firmware which errantly put an 0x0a (LF) following PASSKEY for
|
||||||
|
// AmbientWeather type http reporting.
|
||||||
|
//
|
||||||
|
// This needs to be fixed upstream by Ambient Weather and is a complete
|
||||||
|
// hack that should never be necessary. Without this, the http server
|
||||||
|
// will silently crank back an HTTP:400
|
||||||
|
package ambienthttp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Invalid Request Pattern
|
||||||
|
var badReqURI = regexp.MustCompile(`PASSKEY=[^\n&]{16,}$`)
|
||||||
|
|
||||||
|
// Listener encapsulates LFStrippingConn to perform
|
||||||
|
// infuriating strip of newline character present after PASSKEY
|
||||||
|
// sent errantly by specific versions of firmware sending updates
|
||||||
|
// in AmbientWeather protocol
|
||||||
|
type LFStrippingListener struct {
|
||||||
|
ctx context.Context
|
||||||
|
net.Listener
|
||||||
|
}
|
||||||
|
|
||||||
|
type LFStrippingConn struct {
|
||||||
|
net.Conn
|
||||||
|
reader io.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LFStrippingListener) WrapConn(conn net.Conn) net.Conn {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
reader := io.TeeReader(conn, buf)
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(reader)
|
||||||
|
var newData []byte
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Bytes()
|
||||||
|
newData = append(newData, line...)
|
||||||
|
|
||||||
|
// Only restore newline if not a bad request
|
||||||
|
if !badReqURI.Match(line) {
|
||||||
|
newData = append(newData, '\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if scanner.Err() != nil {
|
||||||
|
zerolog.Ctx(l.ctx).Err(scanner.Err()).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use a multi-reader to prepend the modified request
|
||||||
|
finalReader := io.MultiReader(bytes.NewReader(newData), conn)
|
||||||
|
|
||||||
|
return &LFStrippingConn{
|
||||||
|
Conn: conn,
|
||||||
|
reader: finalReader,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLFStrippingListener(ctx context.Context, listen string) net.Listener {
|
||||||
|
// Create the underlying TCP listener
|
||||||
|
rawListener, err := net.Listen("tcp", listen)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return &LFStrippingListener{
|
||||||
|
Listener: rawListener,
|
||||||
|
ctx: ctx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept waits for and returns the next connection to the listener.
|
||||||
|
func (l *LFStrippingListener) Accept() (net.Conn, error) {
|
||||||
|
conn, err := l.Listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return l.WrapConn(conn), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the listener.
|
||||||
|
// Any blocked Accept operations will be unblocked and return errors.
|
||||||
|
func (l *LFStrippingListener) Close() error {
|
||||||
|
return l.Listener.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addr returns the listener's network address.
|
||||||
|
func (l *LFStrippingListener) Addr() net.Addr {
|
||||||
|
return l.Listener.Addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *LFStrippingConn) Read(b []byte) (int, error) {
|
||||||
|
return c.reader.Read(b)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user