go-proxy/proxy_http_runtime.go

228 lines
5.6 KiB
Go
Raw Normal View History

package proxy
import (
"context"
"encoding/json"
"net"
"net/http"
"strconv"
"strings"
"time"
)
const proxyAPIVersion = "1.0.0"
type summaryHTTPResponse struct {
Version string `json:"version"`
Mode string `json:"mode"`
Hashrate summaryHashrate `json:"hashrate"`
Miners summaryMinerCounts `json:"miners"`
Workers uint64 `json:"workers"`
Upstreams summaryUpstreams `json:"upstreams"`
Results summaryResults `json:"results"`
}
type summaryHashrate struct {
Total [6]float64 `json:"total"`
}
type summaryMinerCounts struct {
Now uint64 `json:"now"`
Max uint64 `json:"max"`
}
type summaryUpstreams struct {
Active uint64 `json:"active"`
Sleep uint64 `json:"sleep"`
Error uint64 `json:"error"`
Total uint64 `json:"total"`
Ratio float64 `json:"ratio"`
}
type summaryResults struct {
Accepted uint64 `json:"accepted"`
Rejected uint64 `json:"rejected"`
Invalid uint64 `json:"invalid"`
Expired uint64 `json:"expired"`
AvgTime uint32 `json:"avg_time"`
Latency uint32 `json:"latency"`
HashesTotal uint64 `json:"hashes_total"`
Best [10]uint64 `json:"best"`
}
func startHTTPServer(p *Proxy) {
if p == nil || p.config == nil || !p.config.HTTP.Enabled || p.httpServer != nil {
return
}
mux := http.NewServeMux()
registerMonitoringRoutes(mux, p)
address := net.JoinHostPort(p.config.HTTP.Host, strconv.Itoa(int(p.config.HTTP.Port)))
listener, errorValue := net.Listen("tcp", address)
if errorValue != nil {
return
}
server := &http.Server{
Handler: mux,
}
p.httpServer = server
go func() {
_ = server.Serve(listener)
}()
}
func stopHTTPServer(p *Proxy) {
if p == nil || p.httpServer == nil {
return
}
server := p.httpServer
p.httpServer = nil
shutdownContext, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_ = server.Shutdown(shutdownContext)
}
func registerMonitoringRoutes(router *http.ServeMux, proxyValue *Proxy) {
if router == nil || proxyValue == nil {
return
}
router.HandleFunc("/1/summary", func(writer http.ResponseWriter, request *http.Request) {
if !allowHTTPRequest(writer, request, proxyValue.HTTPConfig()) {
return
}
summary := proxyValue.Summary()
upstreams := proxyValue.Upstreams()
ratio := 0.0
if upstreams.Total > 0 {
ratio = float64(proxyValue.CurrentMiners()) / float64(upstreams.Total)
}
response := summaryHTTPResponse{
Version: proxyAPIVersion,
Mode: proxyValue.Mode(),
Hashrate: summaryHashrate{
Total: summary.Hashrate,
},
Miners: summaryMinerCounts{
Now: proxyValue.CurrentMiners(),
Max: proxyValue.MaxMiners(),
},
Workers: uint64(len(proxyValue.Workers())),
Upstreams: summaryUpstreams{
Active: upstreams.Active,
Sleep: upstreams.Sleep,
Error: upstreams.Error,
Total: upstreams.Total,
Ratio: ratio,
},
Results: summaryResults{
Accepted: summary.Accepted,
Rejected: summary.Rejected,
Invalid: summary.Invalid,
Expired: summary.Expired,
AvgTime: summary.AvgTime,
Latency: summary.AvgLatency,
HashesTotal: summary.Hashes,
Best: summary.TopDiff,
},
}
writeHTTPJSON(writer, response)
})
router.HandleFunc("/1/workers", func(writer http.ResponseWriter, request *http.Request) {
if !allowHTTPRequest(writer, request, proxyValue.HTTPConfig()) {
return
}
records := proxyValue.Workers()
rows := make([][]interface{}, 0, len(records))
for _, record := range records {
rows = append(rows, []interface{}{
record.Name,
record.LastIP,
record.Connections,
record.Accepted,
record.Rejected,
record.Invalid,
record.Hashes,
record.LastHashAt.Unix(),
record.Hashrate(60),
record.Hashrate(600),
record.Hashrate(3600),
record.Hashrate(43200),
record.Hashrate(86400),
})
}
writeHTTPJSON(writer, map[string]interface{}{
"mode": proxyValue.WorkersMode(),
"workers": rows,
})
})
router.HandleFunc("/1/miners", func(writer http.ResponseWriter, request *http.Request) {
if !allowHTTPRequest(writer, request, proxyValue.HTTPConfig()) {
return
}
miners := proxyValue.Miners()
rows := make([][]interface{}, 0, len(miners))
for _, miner := range miners {
ip := ""
if remote := miner.RemoteAddr(); remote != nil {
ip = remote.String()
}
rows = append(rows, []interface{}{
miner.ID(),
ip,
miner.TX(),
miner.RX(),
miner.State(),
miner.Diff(),
miner.User(),
"********",
miner.RigID(),
miner.Agent(),
})
}
writeHTTPJSON(writer, map[string]interface{}{
"format": []string{"id", "ip", "tx", "rx", "state", "diff", "user", "password", "rig_id", "agent"},
"miners": rows,
})
})
}
func allowHTTPRequest(writer http.ResponseWriter, request *http.Request, config HTTPConfig) bool {
if request == nil {
return false
}
if config.AccessToken != "" {
header := request.Header.Get("Authorization")
prefix := "Bearer "
if !strings.HasPrefix(header, prefix) || strings.TrimSpace(strings.TrimPrefix(header, prefix)) != config.AccessToken {
writer.Header().Set("WWW-Authenticate", "Bearer")
http.Error(writer, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
return false
}
}
if config.Restricted && request.Method != http.MethodGet {
http.Error(writer, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
return false
}
return true
}
func writeHTTPJSON(writer http.ResponseWriter, value interface{}) {
writer.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(writer).Encode(value)
}