124 lines
2.9 KiB
Go
124 lines
2.9 KiB
Go
// SPDX-License-Identifier: MIT
|
|
// SPDX-FileCopyrightText: 2023 mysubarumq
|
|
// SPDX-FileContributor: alex-savin
|
|
|
|
package listeners
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"log/slog"
|
|
"net/http"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"git.savin.nyc/alex/mysubaru-mq/system"
|
|
)
|
|
|
|
// HTTPStats is a listener for presenting the server $SYS stats on a JSON http endpoint.
|
|
type HTTPStats struct {
|
|
sync.RWMutex
|
|
id string // the internal id of the listener
|
|
address string // the network address to bind to
|
|
config *Config // configuration values for the listener
|
|
listen *http.Server // the http server
|
|
sysInfo *system.Info // pointers to the server data
|
|
log *slog.Logger // server logger
|
|
end uint32 // ensure the close methods are only called once
|
|
}
|
|
|
|
// NewHTTPStats initialises and returns a new HTTP listener, listening on an address.
|
|
func NewHTTPStats(id, address string, config *Config, sysInfo *system.Info) *HTTPStats {
|
|
if config == nil {
|
|
config = new(Config)
|
|
}
|
|
return &HTTPStats{
|
|
id: id,
|
|
address: address,
|
|
sysInfo: sysInfo,
|
|
config: config,
|
|
}
|
|
}
|
|
|
|
// ID returns the id of the listener.
|
|
func (l *HTTPStats) ID() string {
|
|
return l.id
|
|
}
|
|
|
|
// Address returns the address of the listener.
|
|
func (l *HTTPStats) Address() string {
|
|
return l.address
|
|
}
|
|
|
|
// Protocol returns the address of the listener.
|
|
func (l *HTTPStats) Protocol() string {
|
|
if l.listen != nil && l.listen.TLSConfig != nil {
|
|
return "https"
|
|
}
|
|
|
|
return "http"
|
|
}
|
|
|
|
// Init initializes the listener.
|
|
func (l *HTTPStats) Init(log *slog.Logger) error {
|
|
l.log = log
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/", l.jsonHandler)
|
|
l.listen = &http.Server{
|
|
ReadTimeout: 5 * time.Second,
|
|
WriteTimeout: 5 * time.Second,
|
|
Addr: l.address,
|
|
Handler: mux,
|
|
}
|
|
|
|
if l.config.TLSConfig != nil {
|
|
l.listen.TLSConfig = l.config.TLSConfig
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Serve starts listening for new connections and serving responses.
|
|
func (l *HTTPStats) Serve() {
|
|
|
|
var err error
|
|
if l.listen.TLSConfig != nil {
|
|
err = l.listen.ListenAndServeTLS("", "")
|
|
} else {
|
|
err = l.listen.ListenAndServe()
|
|
}
|
|
|
|
// After the listener has been shutdown, no need to print the http.ErrServerClosed error.
|
|
if err != nil && atomic.LoadUint32(&l.end) == 0 {
|
|
l.log.Error("failed to serve.", "error", err, "listener", l.id)
|
|
}
|
|
}
|
|
|
|
// Close closes the listener and any client connections.
|
|
func (l *HTTPStats) Close() {
|
|
l.Lock()
|
|
defer l.Unlock()
|
|
|
|
if atomic.CompareAndSwapUint32(&l.end, 0, 1) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
_ = l.listen.Shutdown(ctx)
|
|
}
|
|
|
|
// closeClients(l.id)
|
|
}
|
|
|
|
// jsonHandler is an HTTP handler which outputs the $SYS stats as JSON.
|
|
func (l *HTTPStats) jsonHandler(w http.ResponseWriter, req *http.Request) {
|
|
info := *l.sysInfo.Clone()
|
|
|
|
out, err := json.MarshalIndent(info, "", "\t")
|
|
if err != nil {
|
|
_, _ = io.WriteString(w, err.Error())
|
|
}
|
|
|
|
_, _ = w.Write(out)
|
|
}
|