diff --git a/app/cmd/server.go b/app/cmd/server.go index 386c4f2..435d2c0 100644 --- a/app/cmd/server.go +++ b/app/cmd/server.go @@ -20,6 +20,7 @@ import ( "github.com/apernet/hysteria/extras/auth" "github.com/apernet/hysteria/extras/obfs" "github.com/apernet/hysteria/extras/outbounds" + "github.com/apernet/hysteria/extras/trafficlogger" ) var serverCmd = &cobra.Command{ @@ -46,6 +47,7 @@ type serverConfig struct { Resolver serverConfigResolver `mapstructure:"resolver"` ACL serverConfigACL `mapstructure:"acl"` Outbounds []serverConfigOutboundEntry `mapstructure:"outbounds"` + TrafficStats serverConfigTrafficStats `mapstructure:"trafficStats"` Masquerade serverConfigMasquerade `mapstructure:"masquerade"` } @@ -160,6 +162,10 @@ type serverConfigOutboundEntry struct { SOCKS5 serverConfigOutboundSOCKS5 `mapstructure:"socks5"` } +type serverConfigTrafficStats struct { + Listen string `mapstructure:"listen"` +} + type serverConfigMasqueradeFile struct { Dir string `mapstructure:"dir"` } @@ -504,6 +510,15 @@ func (c *serverConfig) fillEventLogger(hyConfig *server.Config) error { return nil } +func (c *serverConfig) fillTrafficLogger(hyConfig *server.Config) error { + if c.TrafficStats.Listen != "" { + tss := trafficlogger.NewTrafficStatsServer() + hyConfig.TrafficLogger = tss + go runTrafficStatsServer(c.TrafficStats.Listen, tss) + } + return nil +} + func (c *serverConfig) fillMasqHandler(hyConfig *server.Config) error { switch strings.ToLower(c.Masquerade.Type) { case "", "404": @@ -557,6 +572,7 @@ func (c *serverConfig) Config() (*server.Config, error) { c.fillUDPIdleTimeout, c.fillAuthenticator, c.fillEventLogger, + c.fillTrafficLogger, c.fillMasqHandler, } for _, f := range fillers { @@ -594,6 +610,13 @@ func runServer(cmd *cobra.Command, args []string) { } } +func runTrafficStatsServer(listen string, handler http.Handler) { + logger.Info("traffic stats server up and running", zap.String("listen", listen)) + if err := http.ListenAndServe(listen, handler); err != nil { + logger.Fatal("failed to serve traffic stats", zap.Error(err)) + } +} + func geoipDownloadFunc(filename, url string) { logger.Info("downloading GeoIP database", zap.String("filename", filename), zap.String("url", url)) } diff --git a/app/cmd/server_test.go b/app/cmd/server_test.go index f215521..45d7e33 100644 --- a/app/cmd/server_test.go +++ b/app/cmd/server_test.go @@ -124,6 +124,9 @@ func TestServerConfig(t *testing.T) { }, }, }, + TrafficStats: serverConfigTrafficStats{ + Listen: ":9999", + }, Masquerade: serverConfigMasquerade{ Type: "proxy", File: serverConfigMasqueradeFile{ diff --git a/app/cmd/server_test.yaml b/app/cmd/server_test.yaml index be3c083..432fb9f 100644 --- a/app/cmd/server_test.yaml +++ b/app/cmd/server_test.yaml @@ -92,6 +92,9 @@ outbounds: username: hackerman password: Elliot Alderson +trafficStats: + listen: :9999 + masquerade: type: proxy file: diff --git a/extras/trafficlogger/http.go b/extras/trafficlogger/http.go new file mode 100644 index 0000000..bd22a0d --- /dev/null +++ b/extras/trafficlogger/http.go @@ -0,0 +1,114 @@ +package trafficlogger + +import ( + "encoding/json" + "net/http" + "strconv" + "sync" + + "github.com/apernet/hysteria/core/server" +) + +const ( + indexHTML = `
This is a Hysteria Traffic Stats API server.
Check the documentation for usage.